| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #ifndef QQUICKITEMVIEW_P_P_H |
| 5 | #define QQUICKITEMVIEW_P_P_H |
| 6 | |
| 7 | // |
| 8 | // W A R N I N G |
| 9 | // ------------- |
| 10 | // |
| 11 | // This file is not part of the Qt API. It exists purely as an |
| 12 | // implementation detail. This header file may change from version to |
| 13 | // version without notice, or even be removed. |
| 14 | // |
| 15 | // We mean it. |
| 16 | // |
| 17 | |
| 18 | #include <QtQuick/private/qtquickglobal_p.h> |
| 19 | |
| 20 | QT_REQUIRE_CONFIG(quick_itemview); |
| 21 | |
| 22 | #include "qquickitemview_p.h" |
| 23 | #include "qquickitemviewfxitem_p_p.h" |
| 24 | #if QT_CONFIG(quick_viewtransitions) |
| 25 | #include "qquickitemviewtransition_p.h" |
| 26 | #endif |
| 27 | #include "qquickflickable_p_p.h" |
| 28 | #include <QtQmlModels/private/qqmlobjectmodel_p.h> |
| 29 | #include <QtQmlModels/private/qqmldelegatemodel_p.h> |
| 30 | #include <QtQmlModels/private/qqmlchangeset_p.h> |
| 31 | |
| 32 | #include <QtCore/qpointer.h> |
| 33 | |
| 34 | QT_BEGIN_NAMESPACE |
| 35 | |
| 36 | class Q_AUTOTEST_EXPORT FxViewItem : public QQuickItemViewFxItem |
| 37 | { |
| 38 | public: |
| 39 | FxViewItem(QQuickItem *, QQuickItemView *, bool own, QQuickItemViewAttached *attached); |
| 40 | |
| 41 | QQuickItemView *view; |
| 42 | QQuickItemViewAttached *attached; |
| 43 | }; |
| 44 | |
| 45 | |
| 46 | class Q_QUICK_EXPORT QQuickItemViewChangeSet |
| 47 | { |
| 48 | public: |
| 49 | QQuickItemViewChangeSet(); |
| 50 | |
| 51 | bool hasPendingChanges() const; |
| 52 | void prepare(int currentIndex, int count); |
| 53 | void reset(); |
| 54 | |
| 55 | void applyChanges(const QQmlChangeSet &changeSet); |
| 56 | |
| 57 | void applyBufferedChanges(const QQuickItemViewChangeSet &other); |
| 58 | |
| 59 | int itemCount; |
| 60 | int newCurrentIndex; |
| 61 | QQmlChangeSet pendingChanges; |
| 62 | QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> removedItems; |
| 63 | |
| 64 | bool active : 1; |
| 65 | bool currentChanged : 1; |
| 66 | bool currentRemoved : 1; |
| 67 | }; |
| 68 | |
| 69 | |
| 70 | class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate |
| 71 | : public QQuickFlickablePrivate |
| 72 | #if QT_CONFIG(quick_viewtransitions) |
| 73 | , public QQuickItemViewTransitionChangeListener |
| 74 | #endif |
| 75 | , public QAnimationJobChangeListener |
| 76 | { |
| 77 | public: |
| 78 | Q_DECLARE_PUBLIC(QQuickItemView) |
| 79 | QQuickItemViewPrivate(); |
| 80 | ~QQuickItemViewPrivate(); |
| 81 | |
| 82 | static inline QQuickItemViewPrivate *get(QQuickItemView *o) { return o->d_func(); } |
| 83 | |
| 84 | struct ChangeResult { |
| 85 | QQmlNullableValue<qreal> visiblePos; |
| 86 | bool changedFirstItem; |
| 87 | qreal sizeChangesBeforeVisiblePos; |
| 88 | qreal sizeChangesAfterVisiblePos; |
| 89 | int countChangeBeforeVisible; |
| 90 | int countChangeAfterVisibleItems; |
| 91 | |
| 92 | ChangeResult() |
| 93 | : visiblePos(0), changedFirstItem(false), |
| 94 | sizeChangesBeforeVisiblePos(0), sizeChangesAfterVisiblePos(0), |
| 95 | countChangeBeforeVisible(0), countChangeAfterVisibleItems(0) {} |
| 96 | |
| 97 | ChangeResult(const QQmlNullableValue<qreal> &p) |
| 98 | : visiblePos(p), changedFirstItem(false), |
| 99 | sizeChangesBeforeVisiblePos(0), sizeChangesAfterVisiblePos(0), |
| 100 | countChangeBeforeVisible(0), countChangeAfterVisibleItems(0) {} |
| 101 | |
| 102 | ChangeResult &operator+=(const ChangeResult &other) { |
| 103 | if (&other == this) |
| 104 | return *this; |
| 105 | changedFirstItem &= other.changedFirstItem; |
| 106 | sizeChangesBeforeVisiblePos += other.sizeChangesBeforeVisiblePos; |
| 107 | sizeChangesAfterVisiblePos += other.sizeChangesAfterVisiblePos; |
| 108 | countChangeBeforeVisible += other.countChangeBeforeVisible; |
| 109 | countChangeAfterVisibleItems += other.countChangeAfterVisibleItems; |
| 110 | return *this; |
| 111 | } |
| 112 | |
| 113 | void reset() { |
| 114 | changedFirstItem = false; |
| 115 | sizeChangesBeforeVisiblePos = 0.0; |
| 116 | sizeChangesAfterVisiblePos = 0.0; |
| 117 | countChangeBeforeVisible = 0; |
| 118 | countChangeAfterVisibleItems = 0; |
| 119 | } |
| 120 | }; |
| 121 | |
| 122 | enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; |
| 123 | |
| 124 | bool isValid() const; |
| 125 | qreal position() const; |
| 126 | qreal size() const; |
| 127 | qreal startPosition() const; |
| 128 | qreal endPosition() const; |
| 129 | qreal contentStartOffset() const; |
| 130 | int findLastVisibleIndex(int defaultValue = -1) const; |
| 131 | FxViewItem *visibleItem(int modelIndex) const; |
| 132 | FxViewItem *firstItemInView() const; |
| 133 | int findLastIndexInView() const; |
| 134 | int mapFromModel(int modelIndex) const; |
| 135 | |
| 136 | virtual void init(); |
| 137 | virtual void clear(bool onDestruction=false); |
| 138 | virtual void updateViewport(); |
| 139 | |
| 140 | void regenerate(bool orientationChanged=false); |
| 141 | void layout(); |
| 142 | void animationFinished(QAbstractAnimationJob *) override; |
| 143 | void refill(); |
| 144 | void refill(qreal from, qreal to); |
| 145 | void mirrorChange() override; |
| 146 | |
| 147 | FxViewItem *createItem(int modelIndex,QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested); |
| 148 | bool releaseCurrentItem(QQmlInstanceModel::ReusableFlag reusableFlag) |
| 149 | { |
| 150 | auto oldCurrentItem = std::exchange(obj&: currentItem, new_val: nullptr); |
| 151 | return releaseItem(item: oldCurrentItem, reusableFlag); |
| 152 | } |
| 153 | virtual bool releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag); |
| 154 | void itemDestroyed(QQuickItem *item) override; |
| 155 | |
| 156 | QQuickItem *createHighlightItem(); |
| 157 | QQuickItem *createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault = false) const; |
| 158 | virtual void initializeComponentItem(QQuickItem *) const; |
| 159 | |
| 160 | void updateCurrent(int modelIndex); |
| 161 | void updateTrackedItem(); |
| 162 | void updateUnrequestedIndexes(); |
| 163 | void updateUnrequestedPositions(); |
| 164 | void updateVisibleIndex(); |
| 165 | void positionViewAtIndex(int index, int mode); |
| 166 | |
| 167 | qreal minExtentForAxis(const AxisData &axisData, bool forXAxis) const; |
| 168 | qreal maxExtentForAxis(const AxisData &axisData, bool forXAxis) const; |
| 169 | qreal calculatedMinExtent() const; |
| 170 | qreal calculatedMaxExtent() const; |
| 171 | |
| 172 | void connectModel(QQuickItemView *q, QQmlDelegateModelPointer *model); |
| 173 | void disconnectModel(QQuickItemView *q, QQmlDelegateModelPointer *model); |
| 174 | |
| 175 | void applyDelegateChange(); |
| 176 | void applyDelegateModelAccessChange() |
| 177 | { |
| 178 | QQmlDelegateModel::applyDelegateModelAccessChangeOnView(q: q_func(), d: this); |
| 179 | } |
| 180 | |
| 181 | void applyPendingChanges(); |
| 182 | bool applyModelChanges(ChangeResult *insertionResult, ChangeResult *removalResult); |
| 183 | bool applyRemovalChange(const QQmlChangeSet::Change &removal, ChangeResult *changeResult, int *removedCount); |
| 184 | void removeItem(FxViewItem *item, const QQmlChangeSet::Change &removal, ChangeResult *removeResult); |
| 185 | virtual void updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult); |
| 186 | void repositionFirstItem(FxViewItem *prevVisibleItemsFirst, qreal prevVisibleItemsFirstPos, |
| 187 | FxViewItem *prevFirstVisible, ChangeResult *insertionResult, ChangeResult *removalResult); |
| 188 | |
| 189 | #if QT_CONFIG(quick_viewtransitions) |
| 190 | void createTransitioner(); |
| 191 | void prepareVisibleItemTransitions(); |
| 192 | void prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems); |
| 193 | bool prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds); |
| 194 | void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override; |
| 195 | #endif |
| 196 | |
| 197 | int findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector<QQmlChangeSet::Change> &changes) const; |
| 198 | |
| 199 | void checkVisible() const; |
| 200 | void showVisibleItems() const; |
| 201 | |
| 202 | void markExtentsDirty() { |
| 203 | if (layoutOrientation() == Qt::Vertical) |
| 204 | vData.markExtentsDirty(); |
| 205 | else |
| 206 | hData.markExtentsDirty(); |
| 207 | } |
| 208 | |
| 209 | bool hasPendingChanges() const { |
| 210 | return currentChanges.hasPendingChanges() |
| 211 | || bufferedChanges.hasPendingChanges() |
| 212 | #if QT_CONFIG(quick_viewtransitions) |
| 213 | ||runDelayedRemoveTransition |
| 214 | #endif |
| 215 | ; |
| 216 | } |
| 217 | |
| 218 | void refillOrLayout() { |
| 219 | if (hasPendingChanges()) |
| 220 | layout(); |
| 221 | else |
| 222 | refill(); |
| 223 | } |
| 224 | |
| 225 | void forceLayoutPolish() { |
| 226 | Q_Q(QQuickItemView); |
| 227 | forceLayout = true; |
| 228 | q->polish(); |
| 229 | } |
| 230 | |
| 231 | void releaseVisibleItems(QQmlInstanceModel::ReusableFlag reusableFlag) { |
| 232 | // make a copy and clear the visibleItems first to avoid destroyed |
| 233 | // items being accessed during the loop (QTBUG-61294) |
| 234 | const QList<FxViewItem *> oldVisible = visibleItems; |
| 235 | visibleItems.clear(); |
| 236 | for (FxViewItem *item : oldVisible) |
| 237 | releaseItem(item, reusableFlag); |
| 238 | } |
| 239 | |
| 240 | void emitCountChanged(); |
| 241 | |
| 242 | virtual QQuickItemViewAttached *getAttachedObject(const QObject *) const { return nullptr; } |
| 243 | |
| 244 | QPointer<QQmlInstanceModel> model; |
| 245 | QVariant modelVariant; |
| 246 | int itemCount; |
| 247 | int buffer; |
| 248 | int bufferMode; |
| 249 | int displayMarginBeginning; |
| 250 | int displayMarginEnd; |
| 251 | Qt::LayoutDirection layoutDirection; |
| 252 | QQuickItemView::VerticalLayoutDirection verticalLayoutDirection; |
| 253 | |
| 254 | QList<FxViewItem *> visibleItems; |
| 255 | qreal firstVisibleItemPosition = 0; |
| 256 | void storeFirstVisibleItemPosition() { |
| 257 | if (!visibleItems.isEmpty()) { |
| 258 | firstVisibleItemPosition = visibleItems.constFirst()->position(); |
| 259 | } |
| 260 | } |
| 261 | int visibleIndex; |
| 262 | int currentIndex; |
| 263 | FxViewItem *currentItem; |
| 264 | FxViewItem *trackedItem; |
| 265 | QHash<QQuickItem*,int> unrequestedItems; |
| 266 | int requestedIndex; |
| 267 | QQuickItemViewChangeSet currentChanges; |
| 268 | QQuickItemViewChangeSet bufferedChanges; |
| 269 | QPauseAnimationJob bufferPause; |
| 270 | |
| 271 | QQmlComponent *highlightComponent; |
| 272 | std::unique_ptr<FxViewItem> highlight; |
| 273 | int highlightRange; // enum value |
| 274 | qreal highlightRangeStart; |
| 275 | qreal highlightRangeEnd; |
| 276 | int highlightMoveDuration; |
| 277 | |
| 278 | QQmlComponent *; |
| 279 | FxViewItem *; |
| 280 | QQmlComponent *; |
| 281 | FxViewItem *; |
| 282 | |
| 283 | // Reusing delegate items cannot be on by default for backwards compatibility. |
| 284 | // Reusing an item will e.g mean that Component.onCompleted will only be called for an |
| 285 | // item when it's created and not when it's reused, which will break legacy applications. |
| 286 | QQmlInstanceModel::ReusableFlag reusableFlag = QQmlInstanceModel::NotReusable; |
| 287 | |
| 288 | struct MovedItem { |
| 289 | FxViewItem *item; |
| 290 | QQmlChangeSet::MoveKey moveKey; |
| 291 | MovedItem(FxViewItem *i, QQmlChangeSet::MoveKey k) |
| 292 | : item(i), moveKey(k) {} |
| 293 | }; |
| 294 | #if QT_CONFIG(quick_viewtransitions) |
| 295 | QQuickItemViewTransitioner *transitioner; |
| 296 | QVector<FxViewItem *> releasePendingTransition; |
| 297 | #endif |
| 298 | |
| 299 | mutable qreal minExtent; |
| 300 | mutable qreal maxExtent; |
| 301 | |
| 302 | bool ownModel : 1; |
| 303 | bool wrap : 1; |
| 304 | bool keyNavigationEnabled : 1; |
| 305 | bool explicitKeyNavigationEnabled : 1; |
| 306 | bool inLayout : 1; |
| 307 | bool inViewportMoved : 1; |
| 308 | bool forceLayout : 1; |
| 309 | bool currentIndexCleared : 1; |
| 310 | bool haveHighlightRange : 1; |
| 311 | bool autoHighlight : 1; |
| 312 | bool highlightRangeStartValid : 1; |
| 313 | bool highlightRangeEndValid : 1; |
| 314 | bool fillCacheBuffer : 1; |
| 315 | bool inRequest : 1; |
| 316 | #if QT_CONFIG(quick_viewtransitions) |
| 317 | bool runDelayedRemoveTransition : 1; |
| 318 | #endif |
| 319 | bool delegateValidated : 1; |
| 320 | bool isClearing : 1; |
| 321 | bool explicitDelegate: 1; |
| 322 | bool explicitDelegateModelAccess: 1; |
| 323 | |
| 324 | protected: |
| 325 | virtual Qt::Orientation layoutOrientation() const = 0; |
| 326 | virtual bool isContentFlowReversed() const = 0; |
| 327 | |
| 328 | virtual qreal positionAt(int index) const = 0; |
| 329 | virtual qreal endPositionAt(int index) const = 0; |
| 330 | virtual qreal originPosition() const = 0; |
| 331 | virtual qreal lastPosition() const = 0; |
| 332 | |
| 333 | virtual qreal () const = 0; |
| 334 | virtual qreal () const = 0; |
| 335 | virtual bool (int index) const = 0; |
| 336 | virtual bool (int index) const = 0; |
| 337 | virtual void () = 0; |
| 338 | virtual void () = 0; |
| 339 | virtual bool () const { return false; } |
| 340 | virtual bool () const { return false; } |
| 341 | |
| 342 | virtual void createHighlight(bool onDestruction = false) = 0; |
| 343 | virtual void updateHighlight() = 0; |
| 344 | virtual void resetHighlightPosition() = 0; |
| 345 | virtual bool movingFromHighlight() { return false; } |
| 346 | |
| 347 | virtual void setPosition(qreal pos) = 0; |
| 348 | virtual void fixupPosition() = 0; |
| 349 | |
| 350 | virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) = 0; |
| 351 | virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) = 0; |
| 352 | virtual void visibleItemsChanged() {} |
| 353 | |
| 354 | virtual FxViewItem *newViewItem(int index, QQuickItem *item) = 0; |
| 355 | virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) = 0; |
| 356 | virtual void repositionPackageItemAt(QQuickItem *item, int index) = 0; |
| 357 | virtual void resetFirstItemPosition(qreal pos = 0.0) = 0; |
| 358 | virtual void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible) = 0; |
| 359 | |
| 360 | virtual void layoutVisibleItems(int fromModelIndex = 0) = 0; |
| 361 | virtual void changedVisibleIndex(int newIndex) = 0; |
| 362 | |
| 363 | virtual bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, |
| 364 | QList<FxViewItem *> *newItems, QList<MovedItem> *movingIntoView) = 0; |
| 365 | |
| 366 | virtual bool needsRefillForAddedOrRemovedIndex(int) const { return false; } |
| 367 | #if QT_CONFIG(quick_viewtransitions) |
| 368 | virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) = 0; |
| 369 | #endif |
| 370 | |
| 371 | virtual void initializeViewItem(FxViewItem *) {} |
| 372 | virtual void initializeCurrentItem() {} |
| 373 | virtual void updateSectionCriteria() {} |
| 374 | virtual void updateSections() {} |
| 375 | |
| 376 | void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) override; |
| 377 | }; |
| 378 | |
| 379 | |
| 380 | QT_END_NAMESPACE |
| 381 | |
| 382 | #endif // QQUICKITEMVIEW_P_P_H |
| 383 | |