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 | |