6import QtQuick.Controls as QQC2
8import org.kde.kirigami as Kirigami
9import org.kde.kirigamiaddons.dateandtime
10import org.kde.kirigamiaddons.components as Components
11import org.kde.kirigamiaddons.delegates as Delegates
16 signal datePicked(date pickedDate)
18 property date selectedDate: new Date()
19 readonly property int year: selectedDate.getFullYear()
20 readonly property int month: selectedDate.getMonth()
21 readonly property int day: selectedDate.getDate()
22 property bool showDays: true
23 property bool showControlHeader: true
30 property date minimumDate
37 property date maximumDate
39 topPadding: Kirigami.Units.largeSpacing
40 rightPadding: Kirigami.Units.largeSpacing
41 bottomPadding: Kirigami.Units.largeSpacing
42 leftPadding: Kirigami.Units.largeSpacing
44 onActiveFocusChanged: if (activeFocus) {
45 dateSegmentedButton.forceActiveFocus();
48 property bool _completed:
false
49 property bool _runSetDate:
false
51 onSelectedDateChanged:
if (selectedDate !== null && _completed) {
52 setToDate(selectedDate)
55 Component.onCompleted: {
58 setToDate(selectedDate);
61 onShowDaysChanged:
if (!showDays) pickerView.currentIndex = 1;
63 function setToDate(date) {
69 if (root.minimumDate.valueOf() && date.valueOf() < minimumDate.valueOf()) {
73 if (root.maximumDate.valueOf() && date.valueOf() > maximumDate.valueOf()) {
77 if (yearPathView.currentItem !== null) {
78 const yearDiff = date.getFullYear() - yearPathView.currentItem.startDate.getFullYear();
79 let newYearIndex = yearPathView.currentIndex +
yearDiff;
80 let firstYearItemDate = yearPathView.model.data(yearPathView.model.index(1,0), InfiniteCalendarViewModel.StartDateRole);
81 let lastYearItemDate = yearPathView.model.data(yearPathView.model.index(yearPathView.model.rowCount() - 2,0), InfiniteCalendarViewModel.StartDateRole);
84 while (firstYearItemDate >= date) {
85 yearPathView.model.addDates(
false)
86 firstYearItemDate = yearPathView.model.data(yearPathView.model.index(1,0), InfiniteCalendarViewModel.StartDateRole);
89 if (firstYearItemDate < date && newYearIndex === 0) {
90 newYearIndex = date.getFullYear() - firstYearItemDate.getFullYear() + 1;
93 while (lastYearItemDate <= date) {
94 yearPathView.model.addDates(
true)
95 lastYearItemDate = yearPathView.model.data(yearPathView.model.index(yearPathView.model.rowCount() - 1,0), InfiniteCalendarViewModel.StartDateRole);
98 yearPathView.currentIndex = newYearIndex;
101 if (decadePathView.currentItem !== null) {
104 const decadeDiff = Math.floor((date.getFullYear() + 1 - decadePathView.currentItem.startDate.getFullYear()) / 12);
105 let newDecadeIndex = decadePathView.currentIndex + decadeDiff;
106 let firstDecadeItemDate = decadePathView.model.data(decadePathView.model.index(1,0), InfiniteCalendarViewModel.StartDateRole);
107 let lastDecadeItemDate = decadePathView.model.data(decadePathView.model.index(decadePathView.model.rowCount() - 1,0), InfiniteCalendarViewModel.StartDateRole);
110 while (firstDecadeItemDate >= date) {
111 decadePathView.model.addDates(
false)
112 firstDecadeItemDate = decadePathView.model.data(decadePathView.model.index(1,0), InfiniteCalendarViewModel.StartDateRole);
115 if (firstDecadeItemDate < date && newDecadeIndex === 0) {
116 newDecadeIndex = date.getFullYear() - firstDecadeItemDate.getFullYear() + 1;
119 while (lastDecadeItemDate.getFullYear() <= date.getFullYear()) {
120 decadePathView.model.addDates(
true)
121 lastDecadeItemDate = decadePathView.model.data(decadePathView.model.index(decadePathView.model.rowCount() - 1,0), InfiniteCalendarViewModel.StartDateRole);
124 decadePathView.currentIndex = newDecadeIndex;
127 if (showDays && monthPathView.currentItem !== null) {
128 const monthDiff = date.getMonth() - monthPathView.currentItem.firstDayOfMonth.getMonth() + (12 * (date.getFullYear() - monthPathView.currentItem.firstDayOfMonth.getFullYear()));
129 let newMonthIndex = monthPathView.currentIndex + monthDiff;
130 let firstMonthItemDate = monthPathView.model.data(monthPathView.model.index(1,0), InfiniteCalendarViewModel.FirstDayOfMonthRole);
131 let lastMonthItemDate = monthPathView.model.data(monthPathView.model.index(monthPathView.model.rowCount() - 1,0), InfiniteCalendarViewModel.FirstDayOfMonthRole);
133 while(firstMonthItemDate >= date) {
134 monthPathView.model.addDates(
false)
135 firstMonthItemDate = monthPathView.model.data(monthPathView.model.index(1,0), InfiniteCalendarViewModel.FirstDayOfMonthRole);
138 if(firstMonthItemDate < date && newMonthIndex === 0) {
139 newMonthIndex = date.getMonth() - firstMonthItemDate.getMonth() + (12 * (date.getFullYear() - firstMonthItemDate.getFullYear())) + 1;
142 while(lastMonthItemDate <= date) {
143 monthPathView.model.addDates(
true)
144 lastMonthItemDate = monthPathView.model.data(monthPathView.model.index(monthPathView.model.rowCount() - 1,0), InfiniteCalendarViewModel.FirstDayOfMonthRole);
147 monthPathView.currentIndex = newMonthIndex;
154 selectedDate =
new Date()
157 function prevMonth() {
158 const newDate =
new Date(selectedDate.getFullYear(), selectedDate.getMonth() - 1, selectedDate.getDate());
159 if (root.minimumDate.valueOf() && newDate.valueOf() < minimumDate.valueOf()) {
160 if (selectedDate == minimumDate) {
163 selectedDate = minimumDate;
165 selectedDate = newDate;
169 function nextMonth() {
170 const newDate =
new Date(selectedDate.getFullYear(), selectedDate.getMonth() + 1, selectedDate.getDate());
171 if (root.maximumDate.valueOf() && newDate.valueOf() > maximumDate.valueOf()) {
172 if (selectedDate == maximumDate) {
175 selectedDate = maximumDate;
178 selectedDate = newDate;
182 function prevYear() {
183 const newDate =
new Date(selectedDate.getFullYear() - 1, selectedDate.getMonth(), selectedDate.getDate())
184 if (root.minimumDate.valueOf() && newDate.valueOf() < minimumDate.valueOf()) {
185 if (selectedDate == minimumDate) {
188 selectedDate = minimumDate;
190 selectedDate = newDate;
194 function nextYear() {
195 const newDate =
new Date(selectedDate.getFullYear() + 1, selectedDate.getMonth(), selectedDate.getDate());
196 if (root.maximumDate && newDate.valueOf() > maximumDate.valueOf()) {
197 if (selectedDate == maximumDate) {
200 selectedDate = maximumDate;
202 selectedDate = newDate;
206 function prevDecade() {
207 const newDate =
new Date(selectedDate.getFullYear() - 10, selectedDate.getMonth(), selectedDate.getDate());
208 if (root.minimumDate.valueOf() && newDate.valueOf() < minimumDate.valueOf()) {
209 if (selectedDate == minimumDate) {
212 selectedDate = minimumDate;
214 selectedDate = newDate;
218 function nextDecade() {
219 const newDate =
new Date(selectedDate.getFullYear() + 10, selectedDate.getMonth(), selectedDate.getDate())
220 if (root.maximumDate && newDate.valueOf() > maximumDate.valueOf()) {
221 if (selectedDate == maximumDate) {
224 selectedDate = maximumDate;
226 selectedDate = newDate;
230 contentItem: ColumnLayout {
235 Layout.fillWidth:
true
236 Layout.bottomMargin:
Kirigami.Units.smallSpacing
238 Components.SegmentedButton {
239 id: dateSegmentedButton
244 text: root.selectedDate.getDate()
245 onTriggered: pickerView.currentIndex = 0
246 checked: pickerView.currentIndex === 0
250 text: root.selectedDate.toLocaleDateString(
Qt.locale(),
"MMMM")
251 onTriggered: pickerView.currentIndex = 1
252 checked: pickerView.currentIndex === 1
256 text: root.selectedDate.getFullYear()
257 onTriggered: pickerView.currentIndex = 2
258 checked: pickerView.currentIndex === 2
264 model:dateSegmentedButton.children
266 required
property Item modelData
269 Accessible.ignored: !modelData.action
270 Accessible.role: Accessible.Dial
271 Accessible.focusable:
true
274 if (modelData.action === dayAction) {
275 return i18nd(
"kirigami-addons6",
"Day")
277 if (modelData.action === monthAction) {
278 return i18nd(
"kirigami-addons6",
"Month")
280 if (modelData.action === yearsViewCheck) {
281 return i18nd(
"kirigami-addons6",
"Year")
285 property int maximumValue: {
286 if (modelData.action === dayAction) {
287 if (maximumDate.valueOf() && root.year === maximumDate.getYear() && root.month === maximumDate.getMonth()) {
288 return maximumDate.getDate()
292 if (modelData.action === monthAction) {
293 if (maximumDate.valueOf() && root.year === maximumDate.getYear() ) {
294 return maximumDate.month() + 1
298 if (modelData.action === yearsViewCheck) {
299 if (maximumDate.valueOf()) {
300 return maximumDate.getYear()
306 property int minimumValue: {
307 if (modelData.action === dayAction) {
308 if (minimumDate.valueOf() && root.year === minimumDate.getYear() && root.month === minimumDate.getMonth()) {
309 return minimumDate.getDate()
313 if (modelData.action === monthAction) {
314 if (minimumDate.valueOf() && root.year === minimumDate.getYear() ) {
315 return minimumDate.month() + 1
319 if (modelData.action === yearsViewCheck) {
320 if (minimumDate.valueOf()) {
321 return minimumDate.getYear()
327 property int stepSize: 1
328 property int value: {
329 if (modelData.action === dayAction) {
332 if (modelData.action === monthAction) {
333 return root.month + 1
335 if (modelData.action === yearsViewCheck) {
341 if (modelData.action === dayAction) {
342 selectedDate.setDate(value)
344 if (modelData.action === monthAction) {
345 selectedDate.setMonth(value - 1)
347 if (modelData.action === yearsViewCheck) {
348 selectedDate.setFullYear(value)
352 onObjectAdded: (index, object) => {
353 object.modelData.Accessible.ignored =
true
358 Layout.fillWidth:
true
361 Components.SegmentedButton {
365 icon.name:
'go-previous-view'
366 text:
i18ndc(
"kirigami-addons6",
"@action:button",
"Go Previous")
367 displayHint:
Kirigami.DisplayHint.IconOnly
369 if (pickerView.currentIndex === 1) {
371 }
else if (pickerView.currentIndex === 2) {
379 text:
i18ndc(
"kirigami-addons6",
"@action:button",
"Jump to today")
380 displayHint:
Kirigami.DisplayHint.IconOnly
381 icon.name: 'go-jump-today'
382 onTriggered: goToday()
386 text:
i18ndc(
"kirigami-addons6",
"@action:button",
"Go Next")
387 icon.name: 'go-next-view'
388 displayHint:
Kirigami.DisplayHint.IconOnly
390 if (pickerView.currentIndex === 1) {
392 }
else if (pickerView.currentIndex === 2) {
410 Layout.fillWidth:
true
411 Layout.fillHeight:
true
417 enabled: QQC2.SwipeView.isCurrentItem
419 model: InfiniteCalendarViewModel {
420 scale: InfiniteCalendarViewModel.MonthScale
421 currentDate: root.selectedDate
422 minimumDate: root.minimumDate
423 maximumDate: root.maximumDate
430 property date firstDayOfMonth: model.firstDay
431 property bool isNextOrCurrentItem: index >= monthPathView.currentIndex -1 && index <= monthPathView.currentIndex + 1
433 active: isNextOrCurrentItem && root.showDays
435 sourceComponent: GridLayout {
439 width: monthPathView.width
440 height: monthPathView.height
441 Layout.topMargin:
Kirigami.Units.smallSpacing
443 property var modelLoader: Loader {
446 year: monthViewLoader.firstDayOfMonth.getFullYear()
447 month: monthViewLoader.firstDayOfMonth.getMonth() + 1
452 buttons: dayGrid.children
456 model: dayGrid.modelLoader.item?.weekDays
457 delegate: QQC2.Label {
458 Layout.fillWidth:
true
459 Layout.fillHeight:
true
460 horizontalAlignment:
Text.AlignHCenter
461 rightPadding:
Kirigami.Units.mediumSpacing
462 leftPadding:
Kirigami.Units.mediumSpacing
465 Accessible.ignored:
true
472 model: dayGrid.modelLoader.item
474 delegate: DatePickerDelegate {
477 required
property bool isToday
478 required
property bool sameMonth
479 required
property int dayNumber
481 repeater: dayRepeater
482 minimumDate: root.minimumDate
483 maximumDate: root.maximumDate
484 previousAction: goPreviousAction
485 nextAction: goNextAction
489 Accessible.name: date.toLocaleDateString(locale, Locale.ShortFormat)
490 Accessible.ignored: !monthPathView.QQC2.SwipeView.isCurrentItem || !monthViewLoader.PathView.isCurrentItem
498 checked: date.getDate() === selectedDate.getDate() &&
499 date.getMonth() === selectedDate.getMonth() &&
500 date.getFullYear() === selectedDate.getFullYear()
501 opacity: sameMonth && inScope ? 1 : 0.6
512 onCurrentIndexChanged: {
513 if (pickerView.currentIndex === 0) {
514 root.selectedDate =
new Date(currentItem.firstDayOfMonth.getFullYear(), currentItem.firstDayOfMonth.getMonth(), root.selectedDate.getDate());
517 if (currentIndex >= count - 2) {
518 model.addDates(
true);
519 }
else if (currentIndex <= 1) {
520 model.addDates(
false);
521 startIndex += model.datesToAdd;
531 model: InfiniteCalendarViewModel {
532 scale: InfiniteCalendarViewModel.YearScale
533 currentDate: root.selectedDate
539 required
property int index
540 required
property date startDate
542 property bool isNextOrCurrentItem: index >= yearPathView.currentIndex -1 && index <= yearPathView.currentIndex + 1
545 height: parent.height
547 active: isNextOrCurrentItem
549 sourceComponent: GridLayout {
555 buttons: yearGrid.children
561 model: yearGrid.columns * yearGrid.rows
563 delegate: DatePickerDelegate {
566 date:
new Date(yearViewLoader.startDate.getFullYear(), index)
568 minimumDate: root.minimumDate.valueOf() ?
new Date(root.minimumDate).setDate(0) :
new Date(
"invalid")
569 maximumDate: root.maximumDate.valueOf() ? new Date(root.maximumDate.getFullYear(), root.maximumDate.getMonth() + 1, 0) : new Date("invalid")
570 repeater: monthRepeater
571 previousAction: goPreviousAction
572 nextAction: goNextAction
574 Accessible.ignored: !yearPathView.QQC2.
SwipeView.isCurrentItem || !yearViewLoader.PathView.isCurrentItem
575 Accessible.name: date.toLocaleDateString(
Qt.locale(), "MMMM yyyy")
577 horizontalPadding: padding * 2
578 rightPadding: undefined
579 leftPadding: undefined
580 highlighted: date.getMonth() === new Date().getMonth() &&
581 date.getFullYear() === new Date().getFullYear()
583 checked: date.getMonth() === selectedDate.getMonth() &&
584 date.getFullYear() === selectedDate.getFullYear()
585 text:
Qt.locale().standaloneMonthName(date.getMonth())
587 selectedDate =
new Date(date);
588 root.datePicked(date);
589 if(root.showDays) pickerView.currentIndex = 0;
596 onCurrentIndexChanged: {
597 if (pickerView.currentIndex === 1) {
598 root.selectedDate =
new Date(currentItem.startDate.getFullYear(), root.selectedDate.getMonth(), root.selectedDate.getDate());
601 if (currentIndex >= count - 2) {
602 model.addDates(
true);
603 }
else if (currentIndex <= 1) {
604 model.addDates(
false);
605 startIndex += model.datesToAdd;
616 model: InfiniteCalendarViewModel {
617 scale: InfiniteCalendarViewModel.DecadeScale
618 currentDate: root.selectedDate
624 required
property int index
625 required
property date startDate
627 property bool isNextOrCurrentItem: index >= decadePathView.currentIndex -1 && index <= decadePathView.currentIndex + 1
630 height: parent.height
632 active: isNextOrCurrentItem
634 sourceComponent: GridLayout {
641 buttons: decadeGrid.children
647 model: decadeGrid.columns * decadeGrid.rows
649 delegate: DatePickerDelegate {
652 readonly
property bool sameDecade: Math.floor(date.getFullYear() / 10) == Math.floor(year / 10)
654 Accessible.ignored: !decadePathView.QQC2.SwipeView.isCurrentItem || !decadeViewLoader.PathView.isCurrentItem
656 date:
new Date(startDate.getFullYear() + index, 0)
657 minimumDate: root.minimumDate.valueOf() ? new Date(root.minimumDate.getFullYear(), 0, 0) : new Date("invalid")
658 maximumDate: root.maximumDate.valueOf() ? new Date(root.maximumDate.getFullYear(), 12, 0) : new Date("invalid")
659 repeater: decadeRepeater
660 previousAction: goPreviousAction
661 nextAction: goNextAction
663 highlighted: date.getFullYear() === new Date().getFullYear()
665 horizontalPadding: padding * 2
666 rightPadding: undefined
667 leftPadding: undefined
669 checked: date.getFullYear() === selectedDate.getFullYear()
670 opacity: sameDecade ? 1 : 0.7
671 text: date.getFullYear()
673 selectedDate =
new Date(date);
674 root.datePicked(date);
675 pickerView.currentIndex = 1;
682 onCurrentIndexChanged: {
683 if (pickerView.currentIndex === 2) {
685 root.selectedDate =
new Date(currentItem.startDate.getFullYear() + 1, root.selectedDate.getMonth(), root.selectedDate.getDate());
688 if (currentIndex >= count - 2) {
689 model.addDates(
true);
690 }
else if (currentIndex <= 1) {
691 model.addDates(
false);
692 startIndex += model.datesToAdd;
Month model exposing month days and events to a QML view.
QString i18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
int yearDiff(QDate start, QDate end)