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