Merge lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/rewriteDatePickerInternal into lp:ubuntu-ui-toolkit/staging

Proposed by Cris Dywan
Status: Work in progress
Proposed branch: lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/rewriteDatePickerInternal
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 1468 lines (+1057/-254)
12 files modified
examples/ubuntu-ui-toolkit-gallery/Pickers.qml (+2/-0)
src/imports/Components/Pickers/1.3/DatePicker.qml (+335/-254)
src/imports/Components/Pickers/1.3/DatePickerHeader.qml (+98/-0)
src/imports/Components/Pickers/1.3/DatePickerMonthDays.qml (+115/-0)
src/imports/Components/Pickers/1.3/DatePickerMonthDaysWorker.js (+61/-0)
src/imports/Components/Pickers/1.3/DayNightPicker.qml (+50/-0)
src/imports/Components/Pickers/1.3/DigitSeparator.qml (+10/-0)
src/imports/Components/Pickers/1.3/HourPicker.qml (+114/-0)
src/imports/Components/Pickers/1.3/YearListDropdown.qml (+131/-0)
src/imports/Components/Pickers/1.3/calendar.js (+79/-0)
src/imports/Components/Pickers/1.3/dateutils.js (+53/-0)
src/imports/Components/Pickers/Pickers.pro (+9/-0)
To merge this branch: bzr merge lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/rewriteDatePickerInternal
Reviewer Review Type Date Requested Status
Ubuntu SDK team Pending
Review via email: mp+308624@code.launchpad.net

Commit message

Rewrite DatePicker internals

Description of the change

✔️ Allow overriding individual day display for calendar events integr.
✅ show/hide month/year switcher in calendar view depending on mode
✅ week numbers yes/no
✅ minimum month doesn't work (no arrow, but year selector)
⚪ current day needs highlight
✅ time picker shows calendar
⚪ selected 'last month'-day disappears while pressed

To post a comment you must log in.
2139. By Cris Dywan

WIP

Unmerged revisions

2139. By Cris Dywan

WIP

2138. By Cris Dywan

WIP

2137. By Cris Dywan

Rewrite DatePicker internals

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/ubuntu-ui-toolkit-gallery/Pickers.qml'
2--- examples/ubuntu-ui-toolkit-gallery/Pickers.qml 2015-09-24 20:05:52 +0000
3+++ examples/ubuntu-ui-toolkit-gallery/Pickers.qml 2016-10-25 11:37:58 +0000
4@@ -23,6 +23,7 @@
5 id: root
6
7 property var stringListModel: ["starred", "media-record", "like", "language-chooser", "go-home", "email", "contact-group", "notification", "active-call"]
8+ /*
9 TemplateSection {
10 className: "Picker"
11 documentation: "qml-ubuntu-components-pickers-picker.html"
12@@ -205,6 +206,7 @@
13 }
14 }
15 }
16+ */
17 TemplateSection {
18 className: "DatePicker"
19 documentation: "qml-ubuntu-components-pickers-datepicker.html"
20
21=== modified file 'src/imports/Components/Pickers/1.3/DatePicker.qml'
22--- src/imports/Components/Pickers/1.3/DatePicker.qml 2016-03-15 18:16:01 +0000
23+++ src/imports/Components/Pickers/1.3/DatePicker.qml 2016-10-25 11:37:58 +0000
24@@ -16,6 +16,8 @@
25
26 import QtQuick 2.4
27 import Ubuntu.Components 1.3
28+import Ubuntu.Components.Private 1.3
29+import 'dateutils.js' as DU
30
31 /*!
32 \qmltype DatePicker
33@@ -167,7 +169,8 @@
34 insensitive. If minimum and maximum are within the same month, the month picker
35 will also be insensitive.
36 */
37-StyledItem {
38+Item {
39+// StyledItem {
40 id: datePicker
41
42 /*!
43@@ -237,7 +240,8 @@
44 The default values are the current date for the minimum, and 50 year distance
45 value for maximum.
46 */
47- property date minimum: Date.prototype.midnight.call(new Date())
48+ //property date minimum: Date.prototype.midnight.call(new Date())
49+ property date minimum: DU.clone(null, 'setDate', 1)
50 /*!
51 \qmlproperty int maximum
52
53@@ -246,11 +250,12 @@
54
55 See \l minimum for more details.
56 */
57- property date maximum: {
58+ /*property date maximum: {
59 var d = Date.prototype.midnight.call(new Date());
60 d.setFullYear(d.getFullYear() + 50);
61 return d;
62- }
63+ }*/
64+ property date maximum: DU.clone(minimum, 'setFullYear', minimum.getFullYear() + 50)
65
66 /*!
67 For convenience, the \b year value of the \l date property.
68@@ -298,11 +303,49 @@
69 The property holds whether the component's pickers are moving.
70 \sa Picker::moving
71 */
72- readonly property alias moving: positioner.moving
73-
74- implicitWidth: units.gu(36)
75- implicitHeight: units.gu(20)
76- activeFocusOnPress: true
77+ readonly property alias moving: listView.moving
78+
79+ implicitWidth: parent.width
80+ implicitHeight: content.visible ? content.height : timeContent.height
81+ // activeFocusOnPress: true
82+
83+ /*!
84+ \qmlproperty Component delegate
85+ Individual days are rendered by the delegate, in the case of \l mode
86+ containing "Months". This is useful to customize the colors, frame, or
87+ add visuals for calendar events.
88+ Roles:
89+ date: the date of the rendered day
90+ text: the day as a string
91+ currentMonth: the date lies in the currently selected month
92+ pressed: whether this day is being pressed
93+ */
94+ property Component delegate: Label {
95+ id: label
96+ anchors.centerIn: parent
97+ horizontalAlignment: Text.AlignHCenter
98+ verticalAlignment: Text.AlignVCenter
99+ property color dayColor: {
100+ if (DU.sameDay(date, datePicker.date))
101+ return theme.palette.normal.activity;
102+ if (currentMonth)
103+ return theme.palette.normal.backgroundText;
104+ // FIXME: Original color is too light for Qt.lighter
105+ return Qt.darker(theme.palette.normal.base);
106+ }
107+ color: pressed ? Qt.lighter(dayColor) : dayColor
108+ textSize: Label.Medium
109+ text: modelData.text
110+
111+ Frame {
112+ anchors.fill: parent
113+ thickness: units.dp(2)
114+ radius: units.gu(1.7)
115+ color: theme.palette.normal.backgroundText
116+ property date today: new Date()
117+ visible: date.getFullYear() == today.getFullYear() && date.getMonth() == today.getMonth() && DU.sameDay(date, today)
118+ }
119+ }
120
121 /*! \internal */
122 onMinimumChanged: {
123@@ -323,21 +366,30 @@
124 date = maximum;
125 }
126 }
127- /*! \internal */
128- onWidthChanged: {
129- // use dayPicker narrowFormatLimit even if the dayPicker is hidden
130- // and clamp the width so it cannot have less width that the sum of
131- // the three tumblers' narrowFormatLimit
132- var minWidth = 0.0;
133- for (var i = 0; i < tumblerModel.count; i++) {
134- minWidth += tumblerModel.get(i).pickerModel.narrowFormatLimit;
135- }
136- width = Math.max(width, minWidth);
137- }
138+
139+ onDateChanged: {
140+ if (!moving)
141+ listView.positionViewAtIndex(DU.getMonthsRange(minimum, date), ListView.Visible)
142+ }
143+
144+ property int monthsRange: DU.getMonthsRange(minimum, maximum)
145+ // FIXME: Move to Style
146+ property int scrollDirection: Qt.Horizontal
147+ property int snapMode: ListView.SnapToItem
148+ state: ''
149+
150+ function cancelFlick() {
151+ listView.cancelFlick()
152+ }
153+
154+ function toggleYearList() {
155+ datePicker.state = datePicker.state === 'years-months'? '' : 'years-months'
156+ }
157+
158 /*! \internal */
159 onModeChanged: internals.updatePickers()
160 /*! \internal */
161- onLocaleChanged: internals.updatePickers()
162+ // onLocaleChanged: internals.updatePickers()
163
164 Component.onCompleted: {
165 if (minimum === undefined) {
166@@ -347,79 +399,10 @@
167 internals.updatePickers();
168 }
169
170- // models
171- YearModel {
172- id: yearModel
173- mainComponent: datePicker
174- pickerCompleted: internals.completed && internals.showYearPicker
175- pickerWidth: (!pickerItem) ? 0 : narrowFormatLimit
176- function syncModels() {
177- dayModel.syncModels();
178- }
179- }
180- MonthModel {
181- id: monthModel
182- mainComponent: datePicker
183- pickerCompleted: internals.completed && internals.showMonthPicker
184- pickerWidth: {
185- if (!pickerItem) {
186- return 0;
187- }
188- return MathUtils.clamp(datePicker.width - yearModel.pickerWidth - dayModel.pickerWidth, narrowFormatLimit, longFormatLimit);
189- }
190- function syncModels() {
191- dayModel.syncModels();
192- }
193- }
194- DayModel {
195- id: dayModel
196- mainComponent: datePicker
197- pickerCompleted: internals.completed && internals.showDayPicker
198- pickerWidth: {
199- if (!pickerItem) {
200- return 0;
201- }
202- var w = Math.max(datePicker.width * internals.dayPickerRatio, narrowFormatLimit);
203- if (w < longFormatLimit && w >= shortFormatLimit) {
204- return shortFormatLimit;
205- }
206- return w;
207- }
208- }
209- HoursModel {
210- id: hoursModel
211- mainComponent: datePicker
212- pickerCompleted: internals.completed && internals.showHoursPicker
213- pickerWidth: {
214- if (!pickerItem) {
215- return 0;
216- }
217- return narrowFormatLimit;
218- }
219- }
220- MinutesModel {
221- id: minutesModel
222- mainComponent: datePicker
223- pickerCompleted: internals.completed && internals.showMinutesPicker
224- pickerWidth: {
225- if (!pickerItem) {
226- return 0;
227- }
228- return narrowFormatLimit;
229- }
230- }
231- SecondsModel {
232- id: secondsModel
233- mainComponent: datePicker
234- pickerCompleted: internals.completed && internals.showSecondsPicker
235- pickerWidth: {
236- if (!pickerItem) {
237- return 0;
238- }
239- return narrowFormatLimit;
240- }
241- }
242+ width: parent.width
243+ height: content.height
244
245+ /*
246 styleName: "DatePickerStyle"
247 Binding {
248 target: __styleInstance
249@@ -437,77 +420,12 @@
250 value: (internals.showHoursPicker || internals.showMinutesPicker || internals.showSecondsPicker) ?
251 ":" : ""
252 }
253-
254- // tumbler positioner
255- PickerRow {
256- id: positioner
257- parent: (datePicker.__styleInstance && datePicker.__styleInstance.hasOwnProperty("tumblerHolder")) ?
258- datePicker.__styleInstance.tumblerHolder : datePicker
259- mainComponent: datePicker
260- model: tumblerModel
261- margins: internals.margin
262- anchors {
263- top: parent.top
264- bottom: parent.bottom
265- horizontalCenter: parent.horizontalCenter
266- }
267- }
268- // tumbler model
269- ListModel {
270- /*
271- Model to hold tumbler order for repeaters.
272- Roles:
273- - pickerModel
274- - pickerName
275- */
276- id: tumblerModel
277-
278- /*
279- Signal triggered when the model is about to remove a picker. We cannot rely on
280- rowAboutToBeRemoved, as by the time the signal is called the list element is
281- already removed from the model.
282- */
283- signal pickerRemoved(int index)
284-
285- // the function checks whether a pickerModel was added or not
286- // returns the index of the model object the pickerModel was found
287- // or -1 on error.
288- function pickerModelIndex(name) {
289- for (var i = 0; i < count; i++) {
290- if (get(i).pickerName === name) {
291- return i;
292- }
293- }
294- return -1;
295- }
296-
297- // the function checks whether a pickerModel is present in the list;
298- // moves the existing one to the given index or inserts it if not present
299- function setPickerModel(model, name, index) {
300- var idx = pickerModelIndex(name);
301- if (idx >= 0) {
302- move(idx, index, 1);
303- } else {
304- append({"pickerModel": model, "pickerName": name});
305- }
306- }
307-
308- // removes the given picker
309- function removePicker(name) {
310- var idx = pickerModelIndex(name);
311- if (idx >= 0) {
312- pickerRemoved(idx);
313- remove(idx);
314- }
315- }
316- }
317-
318- // component to calculate text fitting
319- Label { id: textSizer; visible: false }
320+ */
321+
322 QtObject {
323 id: internals
324 property bool completed: false
325- property real margin: units.gu(1.5)
326+ property real margin: units.gu(2)
327 property real dayPickerRatio: 0.1
328
329 property bool showYearPicker: true
330@@ -517,6 +435,7 @@
331 property bool showHoursPicker: false
332 property bool showMinutesPicker: false
333 property bool showSecondsPicker: false
334+ property bool showDayNightPicker: showHoursPicker || showMinutesPicker || showSecondsPicker
335
336 /*
337 Update pickers.
338@@ -574,99 +493,261 @@
339 console.warn("Date and Time picking not allowed at the same time.");
340 return;
341 }
342-
343- arrangeTumblers();
344- resetPickers();
345- }
346- }
347-
348- /*
349- Resets the pickers. Pickers will update their models with the given date,
350- minimum and maximum values.
351- */
352- function resetPickers() {
353- if (!completed) return;
354- for (var i = 0; i < tumblerModel.count; i++) {
355- var pickerItem = tumblerModel.get(i).pickerModel.pickerItem;
356- pickerItem.resetPicker();
357- }
358-
359- // calculate the ratio for the dayPicker
360- var maxWidth = 0.0;
361- maxWidth += showYearPicker ? yearModel.longFormatLimit : 0.0;
362- maxWidth += showMonthPicker ? monthModel.longFormatLimit : 0.0;
363- maxWidth += showDayPicker ? dayModel.longFormatLimit : 0.0;
364- if (showDayPicker && maxWidth > 0.0) {
365- dayPickerRatio = (dayModel.longFormatLimit / maxWidth).toPrecision(3);
366- }
367- }
368-
369- /*
370- Detects the tumbler order from the date format of the locale
371- */
372- function arrangeTumblers() {
373- // disable completion so avoid accidental date changes
374- completed = false;
375-
376- // use short format to exclude any extra characters
377- // FIXME: The js split(/\W/g) terminates the process on armhf with Qt 5.3 (v4 js) (https://bugreports.qt-project.org/browse/QTBUG-39255)
378- var format = datePicker.locale.dateFormat(Locale.ShortFormat).match(/\w+/g);
379- // loop through the format to decide the position of the tumbler
380- var formatIndex = 0;
381- for (var i in format) {
382- if (!format[i].length) continue;
383- // check the first two characters
384- switch (format[i].substr(0, 1).toLowerCase()) {
385- case 'y':
386- if (showYearPicker) {
387- tumblerModel.setPickerModel(yearModel, "YearPicker", formatIndex);
388- formatIndex++;
389- } else {
390- tumblerModel.removePicker("YearPicker");
391- }
392-
393- break;
394- case 'm':
395- if (showMonthPicker) {
396- tumblerModel.setPickerModel(monthModel, "MonthPicker", formatIndex);
397- formatIndex++;
398- } else {
399- tumblerModel.removePicker("MonthPicker");
400- }
401-
402- break;
403- case 'd':
404- if (showDayPicker) {
405- tumblerModel.setPickerModel(dayModel, "DayPicker", formatIndex);
406- formatIndex++;
407- } else {
408- tumblerModel.removePicker("DayPicker");
409- }
410- break;
411- }
412- }
413- // check hms
414- if (showHoursPicker) {
415- tumblerModel.setPickerModel(hoursModel, "HoursPicker", formatIndex);
416- formatIndex++;
417- } else {
418- tumblerModel.removePicker("HoursPicker");
419- }
420- if (showMinutesPicker) {
421- tumblerModel.setPickerModel(minutesModel, "MinutesPicker", formatIndex);
422- formatIndex++;
423- } else {
424- tumblerModel.removePicker("MinutesPicker");
425- }
426- if (showSecondsPicker) {
427- tumblerModel.setPickerModel(secondsModel, "SecondsPicker", formatIndex);
428- formatIndex++;
429- } else {
430- tumblerModel.removePicker("SecondsPicker");
431- }
432-
433- // re-enable completion
434- completed = true;
435+ }
436+ }
437+ }
438+
439+ states: [
440+ State { name: '' },
441+ State { name: 'years-months' }
442+ ]
443+
444+ property real dropdownAnimDuration: UbuntuAnimation.FastDuration
445+
446+ transitions: [
447+ Transition {
448+ from: ''
449+ to: 'years-months'
450+ ParallelAnimation {
451+ UbuntuNumberAnimation {
452+ target: headerShadow
453+ property: 'width'
454+ to: content.width
455+ duration: datePicker.dropdownAnimDuration / 2
456+ }
457+ UbuntuNumberAnimation {
458+ target: headerShadow
459+ property: 'opacity'
460+ to: 1
461+ duration: datePicker.dropdownAnimDuration / 2
462+ }
463+ UbuntuNumberAnimation {
464+ target: yearsListDropdown
465+ property: 'y'
466+ to: 0
467+ duration: datePicker.dropdownAnimDuration
468+ }
469+ SequentialAnimation {
470+ PauseAnimation {
471+ duration: datePicker.dropdownAnimDuration
472+ }
473+ UbuntuNumberAnimation {
474+ target: headerShadow
475+ property: 'width'
476+ to: 0
477+ duration: datePicker.dropdownAnimDuration / 1.5
478+ }
479+ }
480+ }
481+ },
482+ Transition {
483+ from: 'years-months'
484+ to: ''
485+ ParallelAnimation {
486+ property real duration: 300
487+ UbuntuNumberAnimation {
488+ target: headerShadow
489+ property: 'width'
490+ to: content.width
491+ duration: datePicker.dropdownAnimDuration / 2
492+ }
493+ UbuntuNumberAnimation {
494+ target: headerShadow
495+ property: 'opacity'
496+ to: 1
497+ duration: datePicker.dropdownAnimDuration / 2
498+ }
499+ UbuntuNumberAnimation {
500+ target: yearsListDropdown
501+ property: 'y'
502+ to: -yearsListDropdown.height
503+ duration: datePicker.dropdownAnimDuration
504+ }
505+ SequentialAnimation {
506+ PauseAnimation {
507+ duration: datePicker.dropdownAnimDuration
508+ }
509+ UbuntuNumberAnimation {
510+ target: headerShadow
511+ property: 'width'
512+ to: 0
513+ duration: datePicker.dropdownAnimDuration / 1.5
514+ }
515+ UbuntuNumberAnimation {
516+ target: headerShadow
517+ property: 'opacity'
518+ to: 0
519+ duration: datePicker.dropdownAnimDuration / 3
520+ }
521+ }
522+ }
523+ }
524+ ]
525+
526+ Rectangle {
527+ color: theme.palette.normal.background
528+ anchors.fill: parent
529+ }
530+
531+ // TimePicker
532+
533+ Item {
534+ id: timeContent
535+ height: container.itemHeight * 5
536+ anchors.leftMargin: root.margin
537+ anchors.rightMargin: root.margin
538+ visible: internals.showHoursPicker || internals.showMinutesPicker || internals.showSecondsPicker
539+
540+ Row {
541+ id: container
542+ anchors.fill: parent
543+ property real itemHeight: units.gu(2)
544+ property int pickerCount: 3
545+ property real pickerWidth: (container.width - internals.margin * container.pickerCount - 1) / container.pickerCount
546+
547+ HourPicker {
548+ type: 'hours'
549+ width: container.pickerWidth
550+ height: container.height
551+ ampm: internals.showDayPicker
552+ }
553+
554+ HourPicker {
555+ type: 'minutes'
556+ width: container.pickerWidth
557+ height: container.height
558+ }
559+
560+ HourPicker {
561+ type: 'seconds'
562+ width: container.pickerWidth
563+ height: container.height
564+ }
565+
566+ DayNightPicker {
567+ width: container.pickerWidth
568+ height: container.height
569+ itemHeight: container.itemHeight
570+ }
571+
572+ DigitSeparator {
573+ width: container.pickerWidth
574+ height: container.height
575+ }
576+ }
577+ }
578+
579+ Item {
580+ id: selectedShape
581+ width: parent.width
582+ height: parent.height / 5
583+ y: parent.height / 2 - height / 2
584+ visible: internals.showHoursPicker || internals.showMinutesPicker || internals.showSecondsPicker
585+
586+ Rectangle {
587+ width: parent.width
588+ height: units.dp(1)
589+ y: 0
590+ color: UbuntuColors.lightGrey
591+ }
592+
593+ Rectangle {
594+ width: parent.width
595+ height: units.dp(1)
596+ y: parent.height - height
597+ color: UbuntuColors.lightGrey
598+ }
599+ }
600+
601+ // DatePicker
602+
603+ Column {
604+ id: content
605+ width: parent.width
606+ visible: internals.showMonthPicker || internals.showYearPicker || internals.showDayPicker
607+
608+ DatePickerHeader {
609+ id: header
610+ anchors.left: parent.left
611+ anchors.right: parent.right
612+ fontSize: 'large'
613+ color: theme.palette.normal.backgroundText
614+ activeColor: UbuntuColors.darkGrey
615+ date: DU.clone(datePicker.date, 'setDate', 1)
616+ minimum: datePicker.minimum
617+ maximum: datePicker.maximum
618+ onRequestNextMonth: datePicker.date = DU.clone(datePicker.date, 'setMonth', datePicker.date.getMonth() + 1)
619+ onRequestPreviousMonth: datePicker.date = DU.clone(datePicker.date, 'setMonth', datePicker.date.getMonth() - 1)
620+ onRequestToggleYearList: datePicker.toggleYearList()
621+ yearsListOpened: datePicker.state === 'years-months'
622+ visible: internals.showYearPicker
623+ }
624+
625+ Item {
626+ width: parent.width
627+ height: header.height * 0.5
628+ Rectangle {
629+ id: headerShadow
630+ y: parent.height
631+ visible: false
632+ opacity: 0
633+ color: theme.palette.normal.baseText
634+ width: 0
635+ height: units.gu(1/16)
636+ anchors.horizontalCenter: parent.horizontalCenter
637+ }
638+ visible: internals.showYearPicker || internals.showMonthPicker
639+ }
640+
641+ Item {
642+ width: parent.width
643+ height: listView.height
644+ visible: internals.showDayPicker
645+ ListView {
646+ id: listView
647+ clip: true
648+ width: parent.width
649+ height: currentItem.height
650+ model: datePicker.monthsRange
651+ snapMode: datePicker.snapMode
652+ orientation: datePicker.scrollDirection === Qt.Horizontal ? ListView.Horizontal : ListView.Vertical
653+ highlightFollowsCurrentItem: true
654+ highlightMoveDuration: 0
655+ highlightRangeMode: ListView.StrictlyEnforceRange
656+ property bool indexInit: false
657+ onCurrentIndexChanged: {
658+ if (!indexInit && currentIndex === 0) {
659+ indexInit = true;
660+ return;
661+ }
662+ if (!listView.moving) {
663+ return;
664+ }
665+ datePicker.date = DU.clone(minimum, 'setMonth', minimum.getMonth() + currentIndex);
666+ }
667+
668+ delegate: DatePickerMonthDays {
669+ id: panel
670+ width: datePicker.width
671+ displayedMonth: DU.clone(minimum, 'setMonth', minimum.getMonth() + index)
672+ onRequestNextMonth: datePicker.date = DU.clone(displayedMonth, 'setMonth', displayedMonth.getMonth() + 1)
673+ onRequestPreviousMonth: datePicker.date = DU.clone(displayedMonth, 'setMonth', displayedMonth.getMonth() - 1)
674+ onRequestDateChange: datePicker.date = date
675+ delegate: datePicker.delegate
676+ }
677+ }
678+
679+ Item {
680+ clip: true
681+ width: parent.width
682+ height: parent.height
683+ YearListDropdown {
684+ id: yearsListDropdown
685+ minimum: datePicker.minimum
686+ maximum: datePicker.maximum
687+ onRequestDateChange: datePicker.date = newDate
688+ }
689+ }
690 }
691 }
692 }
693
694=== added file 'src/imports/Components/Pickers/1.3/DatePickerHeader.qml'
695--- src/imports/Components/Pickers/1.3/DatePickerHeader.qml 1970-01-01 00:00:00 +0000
696+++ src/imports/Components/Pickers/1.3/DatePickerHeader.qml 2016-10-25 11:37:58 +0000
697@@ -0,0 +1,98 @@
698+import QtQuick 2.4
699+import Ubuntu.Components 1.3
700+import './calendar.js' as Calendar
701+import './dateutils.js' as DU
702+
703+Item {
704+ id: root
705+
706+ property string fontSize: 'large'
707+ property color color: 'black'
708+ property color activeColor: 'grey'
709+
710+ property date date: new Date()
711+ property date minimum: DU.clone(null, 'setDate', 1)
712+ property date maximum: DU.clone(minimum, 'setFullYear', minimum.getFullYear() + 50)
713+
714+ property bool yearsListOpened: false
715+
716+ signal requestPreviousMonth()
717+ signal requestNextMonth()
718+ signal requestToggleYearList()
719+
720+ width: parent.width
721+ height: month.height
722+
723+ MouseArea {
724+ id: yearDropDown
725+ onReleased: if (containsMouse) requestToggleYearList()
726+ anchors.fill: parent
727+ Row {
728+ spacing: 0
729+ anchors.horizontalCenter: parent.horizontalCenter
730+ height: parent.height
731+ Label {
732+ id: month
733+ textSize: root.fontSize === 'large' ? Label.Medium : Label.Large
734+ text: DU.format(root.date, 'MMMM')
735+ horizontalAlignment: Text.AlignHCenter
736+ color: yearDropDown.containsPress? root.activeColor : root.color
737+ }
738+ Item {
739+ width: units.gu(0.5)
740+ height: 1
741+ }
742+ Label {
743+ id: year
744+ textSize: root.fontSize === 'large' ? Label.Medium : Label.Large
745+ text: DU.format(root.date, 'yyyy')
746+ horizontalAlignment: Text.AlignHCenter
747+ color: yearDropDown.containsPress? root.activeColor : root.color
748+ }
749+ Item {
750+ width: units.gu(0.5)
751+ height: 1
752+ }
753+ Icon {
754+ name: yearsListOpened? 'up' : 'down'
755+ height: year.height / 1.5
756+ y: parent.height / 2 - height / 2 + height * 0.1
757+ color: yearDropDown.containsPress? root.activeColor : root.color
758+ }
759+ }
760+ }
761+
762+ MouseArea {
763+ visible: !DU.sameDay(root.date, minimum)
764+ anchors.left: parent.left
765+ anchors.leftMargin: units.gu(1.7)
766+ width: parent.height
767+ height: width
768+ onReleased: if (containsMouse) requestPreviousMonth()
769+ Icon {
770+ anchors.left: parent.left
771+ anchors.verticalCenter: parent.verticalCenter
772+ width: parent.width
773+ height: width
774+ color: parent.containsPress? root.activeColor : root.color
775+ name: 'previous'
776+ }
777+ }
778+
779+ MouseArea {
780+ visible: !DU.sameDay(root.date, maximum)
781+ anchors.right: parent.right
782+ anchors.rightMargin: units.gu(1.7)
783+ width: parent.height
784+ height: width
785+ onReleased: if (containsMouse) requestNextMonth()
786+ Icon {
787+ anchors.right: parent.right
788+ anchors.verticalCenter: parent.verticalCenter
789+ width: parent.width
790+ height: width
791+ color: parent.containsPress? root.activeColor : root.color
792+ name: 'next'
793+ }
794+ }
795+}
796
797=== added file 'src/imports/Components/Pickers/1.3/DatePickerMonthDays.qml'
798--- src/imports/Components/Pickers/1.3/DatePickerMonthDays.qml 1970-01-01 00:00:00 +0000
799+++ src/imports/Components/Pickers/1.3/DatePickerMonthDays.qml 2016-10-25 11:37:58 +0000
800@@ -0,0 +1,115 @@
801+import QtQuick 2.4
802+import Ubuntu.Components 1.3
803+import './calendar.js' as Calendar
804+import './dateutils.js' as DU
805+
806+MouseArea {
807+ id: root
808+
809+ height: cellHeight * 7
810+ onPressed: lastPressedPosition = getPosition(mouse)
811+
812+ onReleased: {
813+ lastPressedPosition = null
814+ var pressedPosition = getPosition(mouse)
815+ var index = pressedPosition[1] * 7 + pressedPosition[0]
816+ var item = monthDaysRepeater.itemAt(index)
817+
818+ var currDay = root.monthDays[index]
819+ var currDate = currDay.date
820+ var dispDate = root.displayedMonth
821+ if (currDay.surrounding) {
822+ if (DU.compareDates(currDate, dispDate) < 0) {
823+ requestPreviousMonth()
824+ } else {
825+ requestNextMonth()
826+ }
827+ } else if (root.date.getTime() !== currDate.getTime()) {
828+ requestDateChange(currDate)
829+ }
830+ }
831+
832+ property date date: new Date()
833+ property date displayedMonth: new Date(date.getTime())
834+ property int firstDayOfWeek: 1
835+ property var monthDays: []
836+ property Component delegate
837+
838+ readonly property real cellWidth: width / 7
839+ readonly property real cellHeight: cellWidth * 0.62
840+
841+ property var lastPressedPosition: null
842+
843+ property Item pressedItem: {
844+ if (!(root.containsPress && root.lastPressedPosition)) {
845+ return null
846+ }
847+ var index = lastPressedPosition[1] * 7 + lastPressedPosition[0]
848+ return monthDaysRepeater.itemAt(index)
849+ }
850+
851+ function getPosition(mouse) {
852+ return [
853+ Math.floor(mouse.x / cellWidth),
854+ Math.floor((mouse.y - cellHeight) / cellHeight),
855+ ]
856+ }
857+
858+ WorkerScript {
859+ source: 'DatePickerMonthDaysWorker.js'
860+ onMessage: {
861+ if (messageObject.name === 'monthDays') {
862+ root.monthDays = messageObject.monthDays
863+ monthDaysRepeater.model = messageObject.monthDays.length
864+ }
865+ }
866+ Component.onCompleted: {
867+ this.sendMessage({
868+ name: 'requestMonthDays',
869+ firstDayOfWeek: firstDayOfWeek,
870+ displayedMonth: displayedMonth,
871+ })
872+ }
873+ }
874+
875+ signal requestPreviousMonth()
876+ signal requestNextMonth()
877+ signal requestDateChange(date date)
878+
879+ Repeater {
880+ model: 7
881+ Label {
882+ id: label
883+ width: cellWidth
884+ height: cellHeight
885+ x: index * cellWidth
886+ y: 0
887+ text: DU.shortDayName(index).toUpperCase()
888+ horizontalAlignment: Text.AlignHCenter
889+ verticalAlignment: Text.AlignVCenter
890+ textSize: Label.XSmall
891+ color: theme.palette.normal.backgroundTertiaryText
892+ }
893+ }
894+
895+ Repeater {
896+ id: monthDaysRepeater
897+ model: 0
898+ Item {
899+ id: monthDayItem
900+ width: cellWidth
901+ height: cellHeight
902+ x: (index % 7) * cellWidth
903+ y: ((index - index % 7) / 7) * cellHeight + cellHeight
904+ Loader {
905+ property var modelData: root.monthDays[index]
906+ property date date: modelData.date
907+ property string text: modelData.text
908+ property bool currentMonth: !modelData.surrounding
909+ property bool pressed: monthDayItem === pressedItem
910+ anchors.fill: parent
911+ sourceComponent: delegate
912+ }
913+ }
914+ }
915+}
916
917=== added file 'src/imports/Components/Pickers/1.3/DatePickerMonthDaysWorker.js'
918--- src/imports/Components/Pickers/1.3/DatePickerMonthDaysWorker.js 1970-01-01 00:00:00 +0000
919+++ src/imports/Components/Pickers/1.3/DatePickerMonthDaysWorker.js 2016-10-25 11:37:58 +0000
920@@ -0,0 +1,61 @@
921+function formatDay(d, month) {
922+ var dateNum = d.getDate()
923+ return {
924+ dateNum: dateNum,
925+ text: dateNum + '',
926+ surrounding: d.getMonth() !== month,
927+ date: d
928+ }
929+}
930+
931+function getMonthDays(Calendar, displayedMonth, firstDayOfWeek) {
932+
933+ var cal = new Calendar(firstDayOfWeek)
934+ var month = displayedMonth.getMonth()
935+ var year = displayedMonth.getFullYear()
936+
937+ function setTime(date) {
938+ var d = new Date(displayedMonth.getTime())
939+ d.setFullYear(date.getFullYear())
940+ d.setMonth(date.getMonth())
941+ d.setDate(date.getDate())
942+ return d
943+ }
944+
945+ var monthDates = []
946+ cal.monthDates(year, month, function(d) {
947+ monthDates.push(formatDay(setTime(d), month))
948+ })
949+
950+ if (monthDates.length / 7 > 5) {
951+ return monthDates
952+ }
953+
954+ // var lastDate = new Date(year, month, 1)
955+ var lastDate = new Date(displayedMonth.getTime())
956+ lastDate.setFullYear(year)
957+ lastDate.setMonth(month)
958+ lastDate.setDate(1)
959+
960+ var lastDay = monthDates[monthDates.length - 1]
961+ if (lastDay.surrounding) lastDate.setMonth(month + 1)
962+ lastDate.setDate(lastDay.dateNum)
963+
964+ for (var i = monthDates.length; i < 7 * 6; i++) {
965+ lastDate.setDate(lastDate.getDate() + 1)
966+ monthDates.push(formatDay(lastDate, month))
967+ }
968+
969+ return monthDates
970+}
971+
972+WorkerScript.onMessage = function(msg) {
973+ if (msg.name === 'requestMonthDays') {
974+ Qt.include('calendar.js', function(status) {
975+ WorkerScript.sendMessage({
976+ name: 'monthDays',
977+ monthDays: getMonthDays(Calendar, msg.displayedMonth, msg.firstDayOfWeek),
978+ })
979+ })
980+ }
981+}
982
983=== added file 'src/imports/Components/Pickers/1.3/DayNightPicker.qml'
984--- src/imports/Components/Pickers/1.3/DayNightPicker.qml 1970-01-01 00:00:00 +0000
985+++ src/imports/Components/Pickers/1.3/DayNightPicker.qml 2016-10-25 11:37:58 +0000
986@@ -0,0 +1,50 @@
987+import Ubuntu.Components 1.3
988+import QtQuick 2.4
989+import 'dateutils.js' as DU
990+
991+ListView {
992+ id: ampmListView
993+ clip: true
994+ model: ['AM', 'PM']
995+ cacheBuffer: 0
996+ snapMode: ListView.SnapToItem
997+ orientation: ListView.Vertical
998+ property real itemHeight
999+
1000+ highlightFollowsCurrentItem: true
1001+ highlightMoveDuration: UbuntuAnimation.FastDuration
1002+ highlightRangeMode: ListView.StrictlyEnforceRange
1003+
1004+ preferredHighlightBegin: height / 2 - itemHeight / 2
1005+ preferredHighlightEnd: height / 2 + itemHeight / 2
1006+
1007+ onCurrentIndexChanged: {
1008+ var hour = date.getHours()
1009+
1010+ if (hour < 12 && currentIndex === 1) {
1011+ hour += 12
1012+ } else if (hour > 12 && currentIndex === 0) {
1013+ hour -= 12
1014+ }
1015+
1016+ datePicker.date = DU.clone(date, 'setHours', hour);
1017+ }
1018+
1019+ delegate: MouseArea {
1020+ property bool active: ListView.isCurrentItem
1021+ width: parent.width
1022+ height: itemHeight
1023+ onReleased: ampmListView.currentIndex = index
1024+ Label {
1025+ id: item
1026+ color: parent.active? UbuntuColors.darkGrey : UbuntuColors.lightGrey // FIXME:
1027+ width: parent.width - units.gu(1)
1028+ height: parent.height
1029+ x: units.gu(1)
1030+ text: modelData
1031+ textSize: Label.Medium
1032+ horizontalAlignment: Text.AlignLeft
1033+ verticalAlignment: Text.AlignVCenter
1034+ }
1035+ }
1036+}
1037
1038=== added file 'src/imports/Components/Pickers/1.3/DigitSeparator.qml'
1039--- src/imports/Components/Pickers/1.3/DigitSeparator.qml 1970-01-01 00:00:00 +0000
1040+++ src/imports/Components/Pickers/1.3/DigitSeparator.qml 2016-10-25 11:37:58 +0000
1041@@ -0,0 +1,10 @@
1042+import QtQuick 2.4
1043+import Ubuntu.Components 1.3
1044+
1045+Label {
1046+ anchors.verticalCenter: parent.verticalCenter
1047+ color: theme.palette.normal.backgroundText
1048+ text: ':'
1049+ textSize: Label.Medium
1050+ Component.onCompleted: x -= width / 2
1051+}
1052
1053=== added file 'src/imports/Components/Pickers/1.3/HourPicker.qml'
1054--- src/imports/Components/Pickers/1.3/HourPicker.qml 1970-01-01 00:00:00 +0000
1055+++ src/imports/Components/Pickers/1.3/HourPicker.qml 2016-10-25 11:37:58 +0000
1056@@ -0,0 +1,114 @@
1057+import Ubuntu.Components 1.3
1058+import QtQuick 2.4
1059+import 'dateutils.js' as DU
1060+
1061+PathView {
1062+ id: pathview
1063+ property date date: datePicker.date // FIXME:
1064+ property real itemAlign: Text.AlignHCenter
1065+ property string type
1066+ property bool ampm: true
1067+ clip: true
1068+ pathItemCount: 6
1069+ model: {
1070+ if (type === 'hours') {
1071+ return ampm? 12 : 24
1072+ }
1073+ if (type === 'minutes' || type === 'seconds') {
1074+ return 60
1075+ }
1076+ return 0
1077+ }
1078+ snapMode: PathView.SnapToItem
1079+ preferredHighlightBegin: 0.5
1080+ preferredHighlightEnd: 0.5
1081+
1082+ property bool indexInit: false
1083+ Timer {
1084+ id: afterCompleted
1085+ interval: 0
1086+ onTriggered: indexInit = true
1087+ }
1088+ onCurrentIndexChanged: {
1089+ if (!indexInit) return
1090+
1091+ var method = null
1092+ var value = null
1093+ var am = date.getHours() > 0 && date.getHours() < 13
1094+
1095+ if (type === 'hours') {
1096+ method = 'setHours'
1097+ if (ampm) {
1098+ value = am? currentIndex + 1 : ((currentIndex + 13) % 24)
1099+ } else {
1100+ value = (currentIndex + 1) % 24
1101+ }
1102+ } else if (type === 'minutes') {
1103+ method = 'setMinutes'
1104+ value = currentIndex
1105+
1106+ } else if (type === 'seconds') {
1107+ method = 'setSeconds'
1108+ value = currentIndex
1109+ }
1110+
1111+ if (method === null || value === null) {
1112+ return
1113+ }
1114+
1115+ datePicker.date = DU.clone(date, method, value) // FIXME:
1116+ }
1117+ delegate: Item {
1118+ property bool active: PathView.isCurrentItem
1119+ width: pathview.width
1120+ height: datePicker.itemHeight // FIXME:
1121+ Label {
1122+ width: parent.width
1123+ height: parent.height
1124+ // FIXME: What color?
1125+ color: parent.active? UbuntuColors.darkGrey : UbuntuColors.lightGrey
1126+ text: {
1127+ if (pathview.type === 'hours') {
1128+ return DU.format(DU.clone(date, 'setHours', index + 1), 'HH')
1129+ }
1130+ if (pathview.type === 'minutes') {
1131+ return DU.format(DU.clone(date, 'setMinutes', index), 'mm')
1132+ }
1133+ if (pathview.type === 'seconds') {
1134+ return DU.format(DU.clone(date, 'setSeconds', index), 'ss')
1135+ }
1136+ return ''
1137+ }
1138+ // fontSize: 'x-large'
1139+ textSize: Label.Medium
1140+ horizontalAlignment: pathview.itemAlign
1141+ verticalAlignment: Text.AlignVCenter
1142+ }
1143+ }
1144+ path: Path {
1145+ startX: pathview.width / 2
1146+ startY: - datePicker.itemHeight / 2 // FIXME:
1147+ PathLine {
1148+ x: pathview.width / 2
1149+ y: pathview.height + datePicker.itemHeight / 2 // FIXME:
1150+ }
1151+ }
1152+ Component.onCompleted: {
1153+ afterCompleted.start()
1154+ switch (type) {
1155+ case 'hours':
1156+ if (ampm) {
1157+ currentIndex = date.getHours() - 1
1158+ } else {
1159+ currentIndex = date.getHours() % 24 - 1
1160+ }
1161+ break
1162+ case 'minutes':
1163+ currentIndex = date.getMinutes()
1164+ break
1165+ case 'seconds':
1166+ currentIndex = date.getSeconds()
1167+ break
1168+ }
1169+ }
1170+}
1171
1172=== added file 'src/imports/Components/Pickers/1.3/YearListDropdown.qml'
1173--- src/imports/Components/Pickers/1.3/YearListDropdown.qml 1970-01-01 00:00:00 +0000
1174+++ src/imports/Components/Pickers/1.3/YearListDropdown.qml 2016-10-25 11:37:58 +0000
1175@@ -0,0 +1,131 @@
1176+import QtQuick 2.4
1177+import Ubuntu.Components 1.3
1178+import 'dateutils.js' as DU
1179+
1180+Rectangle {
1181+ id: root
1182+
1183+ property date minimum: DU.clone(null, 'setDate', 1)
1184+ property date maximum: DU.clone(minimum, 'setFullYear', minimum.getFullYear() + 50)
1185+ property real itemHeight: height / 5
1186+ property real itemWidth: width / 2
1187+
1188+ property alias monthsPathView: monthsPathView
1189+ property alias yearsListView: yearsListView
1190+
1191+ signal requestDateChange(date newDate)
1192+
1193+ width: parent.width
1194+ height: parent.height
1195+ y: -height
1196+ color: 'white'
1197+
1198+ PathView {
1199+ id: monthsPathView
1200+ property var date: new Date(1970, 0, 1)
1201+ width: root.itemWidth
1202+ height: parent.height
1203+ pathItemCount: 6
1204+ model: 12
1205+ snapMode: PathView.SnapToItem
1206+ clip: true
1207+
1208+ preferredHighlightBegin: 0.5
1209+ preferredHighlightEnd: 0.5
1210+
1211+ onCurrentIndexChanged: {
1212+ var newDate = DU.clone(datePicker.date, 'setMonth', currentIndex)
1213+ var newDisplayedDate = DU.clone(newDate)
1214+ root.requestDateChange(newDate)
1215+ }
1216+
1217+ delegate: Item {
1218+ property bool active: PathView.isCurrentItem
1219+ width: root.itemWidth
1220+ height: root.itemHeight
1221+ Rectangle {
1222+ anchors.fill: parent
1223+ color: 'transparent'
1224+ Label {
1225+ width: parent.width - units.gu(1)
1226+ height: parent.height
1227+ color: parent.parent.active? UbuntuColors.orange : UbuntuColors.darkGrey
1228+ text: DU.format(DU.clone(monthsPathView.date, 'setMonth', index), 'MMMM')
1229+ fontSize: 'x-large'
1230+ horizontalAlignment: Text.AlignRight
1231+ verticalAlignment: Text.AlignVCenter
1232+ }
1233+ }
1234+ }
1235+
1236+ path: Path {
1237+ startX: root.itemWidth / 2
1238+ startY: - root.itemHeight / 2
1239+ PathLine {
1240+ x: root.itemWidth / 2
1241+ y: root.height + root.itemHeight / 2
1242+ }
1243+ }
1244+
1245+ MouseArea {
1246+ anchors.fill: parent
1247+ }
1248+ }
1249+
1250+ ListView {
1251+ id: yearsListView
1252+
1253+ clip: true
1254+ width: root.itemWidth
1255+ height: parent.height
1256+ x: width
1257+
1258+ model: root.maximum.getFullYear() - root.minimum.getFullYear() + 1
1259+ cacheBuffer: 10
1260+ snapMode: ListView.SnapToItem
1261+ orientation: ListView.Vertical
1262+
1263+ highlightFollowsCurrentItem: true
1264+ highlightMoveDuration: UbuntuAnimation.FastDuration
1265+ highlightRangeMode: ListView.StrictlyEnforceRange
1266+
1267+ preferredHighlightBegin: height / 2 - root.itemHeight / 2
1268+ preferredHighlightEnd: height / 2 + root.itemHeight / 2
1269+
1270+ property bool isCurrentIndexInit: false
1271+ onCurrentIndexChanged: {
1272+ if (!isCurrentIndexInit) {
1273+ isCurrentIndexInit = true
1274+ return
1275+ }
1276+ var newDate = DU.clone(
1277+ minimum, 'setFullYear',
1278+ minimum.getFullYear() + currentIndex
1279+ )
1280+ root.requestDateChange(newDate)
1281+ }
1282+
1283+ delegate: MouseArea {
1284+ width: parent.width
1285+ height: root.itemHeight
1286+ property bool active: ListView.isCurrentItem
1287+ onReleased: yearsListView.currentIndex = index
1288+ Label {
1289+ id: item
1290+ color: parent.active? UbuntuColors.orange : UbuntuColors.darkGrey
1291+ width: parent.width - units.gu(1)
1292+ height: parent.height
1293+ x: units.gu(1)
1294+ text: root.minimum.getFullYear() + index
1295+ fontSize: 'x-large'
1296+ horizontalAlignment: Text.AlignLeft
1297+ verticalAlignment: Text.AlignVCenter
1298+ }
1299+ }
1300+
1301+ Component.onCompleted: {
1302+ // yearsListView.currentIndex = yearsListView.currentIndex
1303+ yearsListView.positionViewAtIndex(yearsListView.currentIndex, ListView.SnapPosition)
1304+ }
1305+ }
1306+}
1307
1308=== added file 'src/imports/Components/Pickers/1.3/calendar.js'
1309--- src/imports/Components/Pickers/1.3/calendar.js 1970-01-01 00:00:00 +0000
1310+++ src/imports/Components/Pickers/1.3/calendar.js 2016-10-25 11:37:58 +0000
1311@@ -0,0 +1,79 @@
1312+.pragma library
1313+
1314+/*!
1315+ * calendar.js: inspired by the calendar module from Python
1316+ * Copyright(c) 2011 Luciano Ramalho <luciano@ramalho.org>
1317+ * MIT Licensed
1318+ *
1319+ * From https://github.com/ramalho/calendar.js
1320+ */
1321+
1322+var CalendarException = function CalendarException(message) {
1323+ this.message = message;
1324+ this.toString = function() {
1325+ return this.constructor.name + ": " + this.message
1326+ };
1327+}
1328+
1329+var Calendar = function Calendar(firstWeekDay) {
1330+ //properties
1331+ this.firstWeekDay = firstWeekDay || 0; // 0 = Sunday
1332+};
1333+
1334+Calendar.prototype = {
1335+ constructor : Calendar,
1336+ weekStartDate : function weekStartDate(date) {
1337+ var startDate = new Date(date.getTime());
1338+ while (startDate.getDay() !== this.firstWeekDay) {
1339+ startDate.setDate(startDate.getDate() - 1);
1340+ }
1341+ return startDate;
1342+ },
1343+ monthDates : function monthDates(year, month, dayFormatter, weekFormatter) {
1344+ if ((typeof year !== "number") || (year < 1970)) {
1345+ throw new CalendarException('year must be a number >= 1970');
1346+ };
1347+ if ((typeof month !== "number") || (month < 0) || (month > 11)) {
1348+ throw new CalendarException('month must be a number (Jan is 0)');
1349+ };
1350+ var weeks = [],
1351+ week = [],
1352+ i = 0,
1353+ date = this.weekStartDate(new Date(year, month, 1));
1354+ do {
1355+ for (i=0; i<7; i++) {
1356+ week.push(dayFormatter ? dayFormatter(date) : date);
1357+ date = new Date(date.getTime());
1358+ date.setDate(date.getDate() + 1);
1359+ }
1360+ weeks.push(weekFormatter ? weekFormatter(week) : week);
1361+ week = [];
1362+ } while ((date.getMonth()<=month) && (date.getFullYear()===year));
1363+ return weeks;
1364+ },
1365+ monthDays : function monthDays(year, month) {
1366+ var getDayOrZero = function getDayOrZero(date) {
1367+ return date.getMonth() === month ? date.getDate() : 0;
1368+ };
1369+ return this.monthDates(year, month, getDayOrZero);
1370+ },
1371+ monthText : function monthText(year, month) {
1372+ if (typeof year === "undefined") {
1373+ var now = new Date();
1374+ year = now.getFullYear();
1375+ month = now.getMonth();
1376+ };
1377+ var getDayOrBlank = function getDayOrBlank(date) {
1378+ var s = date.getMonth() === month ? date.getDate().toString() : " ";
1379+ while (s.length < 2) s = " "+s;
1380+ return s;
1381+ };
1382+ var weeks = this.monthDates(year, month, getDayOrBlank,
1383+ function (week) { return week.join(" ") });
1384+ return weeks.join("\n");
1385+ }
1386+};
1387+for (var i = 0; i < 11; i++) {
1388+ var month = Qt.locale().monthName(i); // FIXME:, Locale.LongFormat);
1389+ Calendar[month] = i;
1390+}
1391
1392=== added file 'src/imports/Components/Pickers/1.3/dateutils.js'
1393--- src/imports/Components/Pickers/1.3/dateutils.js 1970-01-01 00:00:00 +0000
1394+++ src/imports/Components/Pickers/1.3/dateutils.js 2016-10-25 11:37:58 +0000
1395@@ -0,0 +1,53 @@
1396+.pragma library
1397+
1398+// Returns a formatted string from a date, using Qt Date.prototype.toLocaleString()
1399+function format(date, format, localeStr) {
1400+ var locale = Qt.locale(localeStr || 'en_US')
1401+ return date.toLocaleString(locale, format)
1402+}
1403+
1404+// Clone a Date object, and optionally invokes methods on it
1405+function clone(date, calls) {
1406+ var d = date? new Date(date.getTime()) : new Date()
1407+ if (!calls) return d
1408+ if (!Array.isArray(calls)) {
1409+ calls = [[calls].concat([].slice.call(arguments, 2))]
1410+ }
1411+ for (var i = 0; i < calls.length; i++) {
1412+ if (!calls[i]) continue
1413+ d[calls[i][0]].apply(d, calls[i].slice(1))
1414+ }
1415+ return d
1416+}
1417+
1418+// Return true if date1 and date2 are the same day
1419+function sameDay(date1, date2) {
1420+ return date1.getDate() === date2.getDate() &&
1421+ date1.getMonth() === date2.getMonth() &&
1422+ date1.getFullYear() === date2.getFullYear()
1423+}
1424+
1425+// Compare two dates
1426+function compareDates(date1, date2) {
1427+ var time1 = date1.getTime()
1428+ var time2 = date2.getTime()
1429+ if (time1 > time2) return 1
1430+ if (time1 < time2) return -1
1431+ return 0
1432+}
1433+
1434+var shortDayNames = 'Mon Tue Wed Thu Fri Sat Sun'.split(' ')
1435+
1436+// Return a short day name based on a date object or a day number
1437+function shortDayName(date) {
1438+ if (typeof date === 'number') {
1439+ return shortDayNames[date]
1440+ }
1441+ return shortDayNames[date.getDay()]
1442+}
1443+
1444+// Return the number of months between date1 and date2 (including both)
1445+function getMonthsRange(date1, date2) {
1446+ return (date2.getFullYear() - date1.getFullYear()) * 12 +
1447+ (date2.getMonth() - date1.getMonth())
1448+}
1449
1450=== modified file 'src/imports/Components/Pickers/Pickers.pro'
1451--- src/imports/Components/Pickers/Pickers.pro 2015-05-19 09:23:01 +0000
1452+++ src/imports/Components/Pickers/Pickers.pro 2016-10-25 11:37:58 +0000
1453@@ -29,6 +29,15 @@
1454 1.3/Picker.qml \
1455 1.3/PickerRow.qml \
1456 1.3/SecondsModel.qml \
1457+ 1.3/dateutils.js \
1458+ 1.3/calendar.js \
1459+ 1.3/DatePickerMonthDays.qml \
1460+ 1.3/DatePickerMonthDaysWorker.js \
1461+ 1.3/YearListDropdown.qml \
1462+ 1.3/DatePickerHeader.qml \
1463+ 1.3/HourPicker.qml \
1464+ 1.3/DayNightPicker.qml \
1465+ 1.3/DigitSeparator.qml \
1466 1.3/YearModel.qml
1467
1468 load(ubuntu_qml_module)

Subscribers

People subscribed via source and target branches