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
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
492 QQC2.ButtonGroup.group: monthGroup
500 checked: date.getDate() === selectedDate.getDate() &&
501 date.getMonth() === selectedDate.getMonth() &&
502 date.getFullYear() === selectedDate.getFullYear()
503 opacity: sameMonth && inScope ? 1 : 0.6
514 onCurrentIndexChanged: {
515 if (pickerView.currentIndex === 0) {
516 root.selectedDate =
new Date(currentItem.firstDayOfMonth.getFullYear(), currentItem.firstDayOfMonth.getMonth(), root.selectedDate.getDate());
519 if (currentIndex >= count - 2) {
520 model.addDates(
true);
521 }
else if (currentIndex <= 1) {
522 model.addDates(
false);
523 startIndex += model.datesToAdd;
533 model: InfiniteCalendarViewModel {
534 scale: InfiniteCalendarViewModel.YearScale
535 currentDate: root.selectedDate
541 required
property int index
542 required
property date startDate
544 property bool isNextOrCurrentItem: index >= yearPathView.currentIndex -1 && index <= yearPathView.currentIndex + 1
547 height: parent.height
549 active: isNextOrCurrentItem
551 sourceComponent: GridLayout {
563 model: yearGrid.columns * yearGrid.rows
565 delegate: DatePickerDelegate {
568 date:
new Date(yearViewLoader.startDate.getFullYear(), index)
570 minimumDate: root.minimumDate.valueOf() ?
new Date(root.minimumDate).setDate(0) :
new Date(
"invalid")
571 maximumDate: root.maximumDate.valueOf() ? new Date(root.maximumDate.getFullYear(), root.maximumDate.getMonth() + 1, 0) : new Date("invalid")
572 repeater: monthRepeater
573 previousAction: goPreviousAction
574 nextAction: goNextAction
576 Accessible.ignored: !yearPathView.QQC2.
SwipeView.isCurrentItem || !yearViewLoader.PathView.isCurrentItem
577 Accessible.name: date.toLocaleDateString(
Qt.locale(), "MMMM yyyy")
579 QQC2.ButtonGroup.group: yearGroup
581 horizontalPadding: padding * 2
582 rightPadding: undefined
583 leftPadding: undefined
584 highlighted: date.getMonth() === new Date().getMonth() &&
585 date.getFullYear() === new Date().getFullYear()
587 checked: date.getMonth() === selectedDate.getMonth() &&
588 date.getFullYear() === selectedDate.getFullYear()
589 text:
Qt.locale().standaloneMonthName(date.getMonth())
591 selectedDate =
new Date(date);
592 root.datePicked(date);
593 if(root.showDays) pickerView.currentIndex = 0;
600 onCurrentIndexChanged: {
601 if (pickerView.currentIndex === 1) {
602 root.selectedDate =
new Date(currentItem.startDate.getFullYear(), root.selectedDate.getMonth(), root.selectedDate.getDate());
605 if (currentIndex >= count - 2) {
606 model.addDates(
true);
607 }
else if (currentIndex <= 1) {
608 model.addDates(
false);
609 startIndex += model.datesToAdd;
620 model: InfiniteCalendarViewModel {
621 scale: InfiniteCalendarViewModel.DecadeScale
622 currentDate: root.selectedDate
628 required
property int index
629 required
property date startDate
631 property bool isNextOrCurrentItem: index >= decadePathView.currentIndex -1 && index <= decadePathView.currentIndex + 1
634 height: parent.height
636 active: isNextOrCurrentItem
638 sourceComponent: GridLayout {
651 model: decadeGrid.columns * decadeGrid.rows
653 delegate: DatePickerDelegate {
656 readonly
property bool sameDecade: Math.floor(date.getFullYear() / 10) == Math.floor(year / 10)
658 Accessible.ignored: !decadePathView.QQC2.SwipeView.isCurrentItem || !decadeViewLoader.PathView.isCurrentItem
659 QQC2.ButtonGroup.group: decadeGroup
661 date:
new Date(startDate.getFullYear() + index, 0)
662 minimumDate: root.minimumDate.valueOf() ? new Date(root.minimumDate.getFullYear(), 0, 0) : new Date("invalid")
663 maximumDate: root.maximumDate.valueOf() ? new Date(root.maximumDate.getFullYear(), 12, 0) : new Date("invalid")
664 repeater: decadeRepeater
665 previousAction: goPreviousAction
666 nextAction: goNextAction
668 highlighted: date.getFullYear() === new Date().getFullYear()
670 horizontalPadding: padding * 2
671 rightPadding: undefined
672 leftPadding: undefined
674 checked: date.getFullYear() === selectedDate.getFullYear()
675 opacity: sameDecade ? 1 : 0.7
676 text: date.getFullYear()
678 selectedDate =
new Date(date);
679 root.datePicked(date);
680 pickerView.currentIndex = 1;
687 onCurrentIndexChanged: {
688 if (pickerView.currentIndex === 2) {
690 root.selectedDate =
new Date(currentItem.startDate.getFullYear() + 1, root.selectedDate.getMonth(), root.selectedDate.getDate());
693 if (currentIndex >= count - 2) {
694 model.addDates(
true);
695 }
else if (currentIndex <= 1) {
696 model.addDates(
false);
697 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)