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