Warning, /plasma/plasma-workspace/components/calendar/qml/InfiniteList.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2022 Tanbir Jishan <tantalising007@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 import QtQuick 0007 import org.kde.plasma.workspace.calendar 2.0 0008 import org.kde.kirigami 2.20 as Kirigami 0009 0010 ListView { 0011 id: infiniteList 0012 0013 // Set up alternative model for delegates at edges 0014 // so that they can be set up to always show the right date (top: previous date; bottom: next date) 0015 // date here means what the respective views should show(e.g. MonthView date -> month) 0016 // this prevents them from showing the current date when they are being animated out of/into view 0017 // since different years don't have different names for months, we don't need to set up alternative models for YearView 0018 readonly property QtObject previousModel: switch(infiniteList.viewType) { 0019 case InfiniteList.ViewType.DayView: 0020 return previousAlternativeBackend.item?.daysModel ?? null; 0021 case InfiniteList.ViewType.DecadeView: 0022 return previousYearModel.item; 0023 default: 0024 return null; 0025 } 0026 readonly property QtObject nextModel: switch(infiniteList.viewType) { 0027 case InfiniteList.ViewType.DayView: 0028 return nextAlternativeBackend.item?.daysModel ?? null; 0029 case InfiniteList.ViewType.DecadeView: 0030 return nextYearModel.item; 0031 default: 0032 return null; 0033 } 0034 0035 readonly property double cellHeight: currentItem ? currentItem.cellHeight : 0 0036 readonly property double cellWidth: currentItem ? currentItem.cellWidth : 0 0037 0038 required property var backend 0039 required property int viewType 0040 0041 enum ViewType { 0042 DayView, 0043 YearView, 0044 DecadeView 0045 } 0046 0047 property bool changeDate: false // To control whether to animate or animate + date change after start up complete. Should always be false on start up. 0048 property bool dragHandled: false 0049 0050 highlightRangeMode: ListView.StrictlyEnforceRange 0051 snapMode: ListView.SnapToItem 0052 highlightMoveDuration: Kirigami.Units.longDuration 0053 highlightMoveVelocity: -1 0054 reuseItems: true 0055 model: 3 0056 keyNavigationEnabled: false // It's actually enabled. The default behaviour is not desirable 0057 0058 function focusFirstCellOfView() { 0059 currentItem.repeater.itemAt(0).forceActiveFocus(Qt.TabFocusReason); 0060 } 0061 0062 function resetViewPosition() { 0063 positionViewAtIndex(1, ListView.Beginning); 0064 currentIndex = 1; 0065 } 0066 0067 // prevents keep changing the date by dragging and holding; returns true if date should not change else returns false 0068 function handleDrag() { 0069 if (dragHandled) { // if already date changed for dragging once (drag still going on like streams) then 0070 resetViewPosition(); //reset so that further dragging is not broken when this dragging session is over 0071 return true; // return true so that still ongoing drags do not change date 0072 } else { // else if drag not handled then 0073 if (draggingVertically) { // if reached view edge because of drag (and not wheel or buttons) then 0074 dragHandled = true; // mark as drag handled so that next drags in stream are ignored 0075 } 0076 } 0077 return false; // return false because we need to change date either for first drag or for view changed by some means other than drag 0078 } 0079 0080 // These signal handlers animate the view. They are the only ones through which date (and should as well) changes. 0081 onAtYEndChanged: { 0082 if (atYEnd) { 0083 if (handleDrag()) return; 0084 changeDate ? nextView() : changeDate = true; 0085 resetViewPosition(); 0086 } 0087 } 0088 0089 onAtYBeginningChanged: { 0090 if (atYBeginning) { 0091 if (handleDrag()) return; 0092 changeDate ? previousView() : changeDate = true; 0093 resetViewPosition(); 0094 } 0095 } 0096 0097 onDraggingVerticallyChanged: if (draggingVertically === false) dragHandled = false; //reset the value when drag ends 0098 0099 /* ------------------------------- UI ENDS ----------------- MODEL MANIPULATING FUNCTIONS ----------------------------------- */ 0100 0101 0102 // used to update the alternative decadeview models when year changes 0103 function updateDecadeOverview(offset) { 0104 if (Math.abs(offset) !== 1) return; 0105 0106 const model = offset == 1 ? nextYearModel.item: previousYearModel.item; 0107 const year = backend.year + (10 * offset) // Increase or decrease year by a decade 0108 const decade = year - year % 10; 0109 0110 for (let i = 0, j = model.count; i < j; ++i) { // aware 0111 const label = decade - 1 + i; 0112 0113 model.setProperty(i, "yearNumber", label); 0114 model.setProperty(i, "label", label); 0115 } 0116 } 0117 0118 function initYearModel(offset) { 0119 if (Math.abs(offset) !== 1) return; 0120 0121 const model = offset == 1 ? nextYearModel.item: previousYearModel.item; 0122 for (let i = 0; i < 12; ++i) { 0123 model.append({ 0124 label: 2050, // this value will be overwritten, but it set the type of the property to int 0125 yearNumber: 2050, 0126 isCurrent: (i > 0 && i < 11) // first and last year are outside the decade 0127 }) 0128 } 0129 0130 infiniteList.updateDecadeOverview(offset); 0131 } 0132 0133 function modulo(a:int, n: int): int { // always keep the 'a' between [1, n] 0134 return ((((a - 1) % n) + n) % n) + 1; 0135 } 0136 0137 function previousView() { 0138 switch(infiniteList.viewType) { 0139 case InfiniteList.ViewType.DayView: 0140 backend.previousMonth(); 0141 break; 0142 0143 case InfiniteList.ViewType.YearView: 0144 backend.previousYear(); 0145 break; 0146 0147 case InfiniteList.ViewType.DecadeView: 0148 backend.previousDecade(); 0149 break; 0150 } 0151 } 0152 0153 function nextView() { 0154 switch(infiniteList.viewType) { 0155 case InfiniteList.ViewType.DayView: 0156 backend.nextMonth(); 0157 break; 0158 0159 case InfiniteList.ViewType.YearView: 0160 backend.nextYear(); 0161 break; 0162 0163 case InfiniteList.ViewType.DecadeView: 0164 backend.nextDecade(); 0165 break; 0166 } 0167 } 0168 0169 /*----------------------------------------------------- alternative models ---------------------------------------------------------------*/ 0170 0171 Loader { 0172 id: previousAlternativeBackend 0173 0174 active: infiniteList.viewType === InfiniteList.ViewType.DayView 0175 asynchronous: true 0176 0177 sourceComponent: Calendar { 0178 days: backend.days 0179 weeks: backend.weeks 0180 firstDayOfWeek: backend.firstDayOfWeek 0181 today: backend.today 0182 0183 function goToPreviousView() { 0184 const month = modulo(backend.month - 1, 12) 0185 const year = month === 12 ? backend.year - 1 : backend.year 0186 goToYear(year); 0187 goToMonth(month); 0188 } 0189 } 0190 onStatusChanged: if (status === Loader.Ready) item.goToPreviousView(); 0191 } 0192 0193 Loader { 0194 id: nextAlternativeBackend 0195 0196 active: infiniteList.viewType === InfiniteList.ViewType.DayView 0197 asynchronous: true 0198 0199 sourceComponent: Calendar { 0200 days: backend.days 0201 weeks: backend.weeks 0202 firstDayOfWeek: backend.firstDayOfWeek 0203 today: backend.today 0204 0205 function goToNextView() { 0206 const month = modulo(backend.month + 1, 12) 0207 const year = month === 1 ? backend.year + 1 : backend.year 0208 goToYear(year); 0209 goToMonth(month); 0210 } 0211 } 0212 onStatusChanged: if (status === Loader.Ready) item.goToNextView(); //clear other model names 0213 } 0214 0215 Loader { 0216 id: nextYearModel 0217 0218 active: infiniteList.viewType === InfiniteList.ViewType.DecadeView 0219 asynchronous: true 0220 0221 function update() { infiniteList.updateDecadeOverview(1) } 0222 0223 sourceComponent: ListModel {} 0224 onStatusChanged: if (nextYearModel.status === Loader.Ready) infiniteList.initYearModel(1); 0225 } 0226 0227 Loader { 0228 id: previousYearModel 0229 0230 active: infiniteList.viewType === InfiniteList.ViewType.DecadeView 0231 asynchronous: true 0232 0233 function update() { infiniteList.updateDecadeOverview(-1) } 0234 0235 sourceComponent: ListModel {} 0236 onStatusChanged: if (previousYearModel.status === Loader.Ready) infiniteList.initYearModel(-1); 0237 } 0238 0239 Connections { 0240 target: backend 0241 enabled: infiniteList.viewType === InfiniteList.ViewType.DayView 0242 0243 function onMonthChanged() { 0244 previousAlternativeBackend.item.goToPreviousView(); 0245 nextAlternativeBackend.item.goToNextView(); 0246 } 0247 } 0248 0249 Connections { 0250 target: backend 0251 enabled: infiniteList.viewType === InfiniteList.ViewType.DecadeView 0252 0253 function onYearChanged() { 0254 nextYearModel.update(); 0255 previousYearModel.update(); 0256 } 0257 } 0258 }