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 QLISTVIEW_P_H |
5 | #define QLISTVIEW_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 for the convenience |
12 | // of other Qt classes. 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 <QtWidgets/private/qtwidgetsglobal_p.h> |
19 | #include "private/qabstractitemview_p.h" |
20 | #include "qbitarray.h" |
21 | #include "qbsptree_p.h" |
22 | #include <limits.h> |
23 | #include <qscrollbar.h> |
24 | |
25 | QT_REQUIRE_CONFIG(listview); |
26 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | class QListViewItem |
30 | { |
31 | friend class QListViewPrivate; |
32 | friend class QListModeViewBase; |
33 | friend class QIconModeViewBase; |
34 | public: |
35 | constexpr QListViewItem() |
36 | : x(-1), y(-1), w(0), h(0), indexHint(-1), visited(0xffff) {} |
37 | constexpr QListViewItem(QRect r, int i) |
38 | : x(r.x()), y(r.y()), w(qMin(a: r.width(), SHRT_MAX)), h(qMin(a: r.height(), SHRT_MAX)), |
39 | indexHint(i), visited(0xffff) {} |
40 | constexpr bool operator==(const QListViewItem &other) const { |
41 | return (x == other.x && y == other.y && w == other.w && h == other.h && |
42 | indexHint == other.indexHint); } |
43 | constexpr bool operator!=(const QListViewItem &other) const |
44 | { return !(*this == other); } |
45 | constexpr bool isValid() const |
46 | { return rect().isValid() && (indexHint > -1); } |
47 | constexpr void invalidate() |
48 | { x = -1; y = -1; w = 0; h = 0; } |
49 | constexpr void resize(QSize size) |
50 | { w = qMin(a: size.width(), SHRT_MAX); h = qMin(a: size.height(), SHRT_MAX); } |
51 | constexpr void move(QPoint position) |
52 | { x = position.x(); y = position.y(); } |
53 | constexpr int width() const { return w; } |
54 | constexpr int height() const { return h; } |
55 | private: |
56 | constexpr QRect rect() const |
57 | { return QRect(x, y, w, h); } |
58 | int x, y; |
59 | short w, h; |
60 | mutable int indexHint; |
61 | uint visited; |
62 | }; |
63 | Q_DECLARE_TYPEINFO(QListViewItem, Q_PRIMITIVE_TYPE); |
64 | |
65 | struct QListViewLayoutInfo |
66 | { |
67 | QRect bounds; |
68 | QSize grid; |
69 | int spacing; |
70 | int first; |
71 | int last; |
72 | bool wrap; |
73 | QListView::Flow flow; |
74 | int max; |
75 | }; |
76 | Q_DECLARE_TYPEINFO(QListViewLayoutInfo, Q_PRIMITIVE_TYPE); |
77 | |
78 | class QListView; |
79 | class QListViewPrivate; |
80 | |
81 | class QCommonListViewBase |
82 | { |
83 | public: |
84 | inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q), batchStartRow(0), batchSavedDeltaSeg(0) {} |
85 | virtual ~QCommonListViewBase() {} |
86 | |
87 | //common interface |
88 | virtual int itemIndex(const QListViewItem &item) const = 0; |
89 | virtual QListViewItem indexToListViewItem(const QModelIndex &index) const = 0; |
90 | virtual bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max) = 0; |
91 | virtual void clear() = 0; |
92 | virtual void setRowCount(int) = 0; |
93 | virtual QList<QModelIndex> intersectingSet(const QRect &area) const = 0; |
94 | virtual void dataChanged(const QModelIndex &, const QModelIndex &) = 0; |
95 | |
96 | virtual int horizontalScrollToValue(int index, QListView::ScrollHint hint, |
97 | bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const; |
98 | virtual int verticalScrollToValue(int index, QListView::ScrollHint hint, |
99 | bool above, bool below, const QRect &area, const QRect &rect) const; |
100 | virtual void scrollContentsBy(int dx, int dy, bool scrollElasticBand); |
101 | virtual QRect mapToViewport(const QRect &rect) const {return rect;} |
102 | virtual int horizontalOffset() const; |
103 | virtual int verticalOffset() const { return verticalScrollBar()->value(); } |
104 | virtual void updateHorizontalScrollBar(const QSize &step); |
105 | virtual void updateVerticalScrollBar(const QSize &step); |
106 | virtual void appendHiddenRow(int row); |
107 | virtual void removeHiddenRow(int row); |
108 | virtual void setPositionForIndex(const QPoint &, const QModelIndex &) { } |
109 | |
110 | #if QT_CONFIG(draganddrop) |
111 | virtual void paintDragDrop(QPainter *painter); |
112 | virtual bool filterDragMoveEvent(QDragMoveEvent *) { return false; } |
113 | virtual bool filterDragLeaveEvent(QDragLeaveEvent *) { return false; } |
114 | virtual bool filterDropEvent(QDropEvent *) { return false; } |
115 | virtual bool filterStartDrag(Qt::DropActions) { return false; } |
116 | #endif |
117 | |
118 | |
119 | //other inline members |
120 | inline int spacing() const; |
121 | inline bool isWrapping() const; |
122 | inline QSize gridSize() const; |
123 | inline QListView::Flow flow() const; |
124 | inline QListView::Movement movement() const; |
125 | |
126 | inline QPoint offset() const; |
127 | inline QPoint pressedPosition() const; |
128 | inline bool uniformItemSizes() const; |
129 | inline int column() const; |
130 | |
131 | inline QScrollBar *verticalScrollBar() const; |
132 | inline QScrollBar *horizontalScrollBar() const; |
133 | inline QListView::ScrollMode verticalScrollMode() const; |
134 | inline QListView::ScrollMode horizontalScrollMode() const; |
135 | |
136 | inline QModelIndex modelIndex(int row) const; |
137 | inline int rowCount() const; |
138 | |
139 | inline void initViewItemOption(QStyleOptionViewItem *option) const; |
140 | inline QWidget *viewport() const; |
141 | inline QRect clipRect() const; |
142 | |
143 | inline QSize cachedItemSize() const; |
144 | inline QRect viewItemRect(const QListViewItem &item) const; |
145 | inline QSize itemSize(const QStyleOptionViewItem &opt, const QModelIndex &idx) const; |
146 | inline QAbstractItemDelegate *delegate(const QModelIndex &idx) const; |
147 | |
148 | inline bool isHidden(int row) const; |
149 | inline int hiddenCount() const; |
150 | |
151 | inline bool isRightToLeft() const; |
152 | |
153 | QListViewPrivate *dd; |
154 | QListView *qq; |
155 | QSize contentsSize; |
156 | int batchStartRow; |
157 | int batchSavedDeltaSeg; |
158 | }; |
159 | |
160 | class QListModeViewBase : public QCommonListViewBase |
161 | { |
162 | public: |
163 | QListModeViewBase(QListView *q, QListViewPrivate *d); |
164 | |
165 | QList<int> flowPositions; |
166 | QList<int> segmentPositions; |
167 | QList<int> segmentStartRows; |
168 | QList<int> segmentExtents; |
169 | QList<int> scrollValueMap; |
170 | |
171 | // used when laying out in batches |
172 | int batchSavedPosition; |
173 | |
174 | //reimplementations |
175 | int itemIndex(const QListViewItem &item) const override { return item.indexHint; } |
176 | QListViewItem indexToListViewItem(const QModelIndex &index) const override; |
177 | bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max) override; |
178 | void clear() override; |
179 | void setRowCount(int rowCount) override { flowPositions.resize(size: rowCount); } |
180 | QList<QModelIndex> intersectingSet(const QRect &area) const override; |
181 | void dataChanged(const QModelIndex &, const QModelIndex &) override; |
182 | |
183 | int horizontalScrollToValue(int index, QListView::ScrollHint hint, |
184 | bool leftOf, bool rightOf,const QRect &area, const QRect &rect) const override; |
185 | int verticalScrollToValue(int index, QListView::ScrollHint hint, |
186 | bool above, bool below, const QRect &area, const QRect &rect) const override; |
187 | void scrollContentsBy(int dx, int dy, bool scrollElasticBand) override; |
188 | QRect mapToViewport(const QRect &rect) const override; |
189 | int horizontalOffset() const override; |
190 | int verticalOffset() const override; |
191 | inline static QSize viewportSize(const QAbstractItemView *v); |
192 | void updateHorizontalScrollBar(const QSize &step) override; |
193 | void updateVerticalScrollBar(const QSize &step) override; |
194 | |
195 | #if QT_CONFIG(draganddrop) |
196 | // The next two methods are to be used on LefToRight flow only. |
197 | // WARNING: Plenty of duplicated code from QAbstractItemView{,Private}. |
198 | QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const; |
199 | void dragMoveEvent(QDragMoveEvent *e); |
200 | bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); |
201 | #endif |
202 | |
203 | private: |
204 | QPoint initStaticLayout(const QListViewLayoutInfo &info); |
205 | void doStaticLayout(const QListViewLayoutInfo &info); |
206 | int perItemScrollToValue(int index, int value, int height, |
207 | QAbstractItemView::ScrollHint hint, |
208 | Qt::Orientation orientation, bool wrap, int extent) const; |
209 | int perItemScrollingPageSteps(int length, int bounds, bool wrap) const; |
210 | }; |
211 | |
212 | class QIconModeViewBase : public QCommonListViewBase |
213 | { |
214 | public: |
215 | QIconModeViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d), interSectingVector(nullptr) {} |
216 | |
217 | QBspTree tree; |
218 | QList<QListViewItem> items; |
219 | QBitArray moved; |
220 | |
221 | QList<QModelIndex> draggedItems; // indices to the tree.itemVector |
222 | mutable QPoint draggedItemsPos; |
223 | |
224 | // used when laying out in batches |
225 | QList<QModelIndex> *interSectingVector; // used from within intersectingSet |
226 | |
227 | //reimplementations |
228 | int itemIndex(const QListViewItem &item) const override; |
229 | QListViewItem indexToListViewItem(const QModelIndex &index) const override; |
230 | bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max) override; |
231 | void clear() override; |
232 | void setRowCount(int rowCount) override; |
233 | QList<QModelIndex> intersectingSet(const QRect &area) const override; |
234 | |
235 | void scrollContentsBy(int dx, int dy, bool scrollElasticBand) override; |
236 | void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) override; |
237 | void appendHiddenRow(int row) override; |
238 | void removeHiddenRow(int row) override; |
239 | void setPositionForIndex(const QPoint &position, const QModelIndex &index) override; |
240 | |
241 | #if QT_CONFIG(draganddrop) |
242 | bool filterDragMoveEvent(QDragMoveEvent *) override; |
243 | bool filterDragLeaveEvent(QDragLeaveEvent *) override; |
244 | bool filterDropEvent(QDropEvent *e) override; |
245 | bool filterStartDrag(Qt::DropActions) override; |
246 | #endif |
247 | |
248 | private: |
249 | void initBspTree(const QSize &contents); |
250 | QPoint initDynamicLayout(const QListViewLayoutInfo &info); |
251 | void doDynamicLayout(const QListViewLayoutInfo &info); |
252 | static void addLeaf(QList<int> &leaf, const QRect &area, uint visited, QBspTree::Data data); |
253 | QRect itemsRect(const QList<QModelIndex> &indexes) const; |
254 | QRect draggedItemsRect() const; |
255 | QPoint snapToGrid(const QPoint &pos) const; |
256 | void updateContentsSize(); |
257 | QPoint draggedItemsDelta() const; |
258 | void drawItems(QPainter *painter, const QList<QModelIndex> &indexes) const; |
259 | void moveItem(int index, const QPoint &dest); |
260 | |
261 | }; |
262 | |
263 | class Q_AUTOTEST_EXPORT QListViewPrivate: public QAbstractItemViewPrivate |
264 | { |
265 | Q_DECLARE_PUBLIC(QListView) |
266 | public: |
267 | QListViewPrivate(); |
268 | ~QListViewPrivate(); |
269 | |
270 | void clear(); |
271 | void prepareItemsLayout(); |
272 | |
273 | bool doItemsLayout(int num); |
274 | |
275 | inline QList<QModelIndex> intersectingSet(const QRect &area, bool doLayout = true) const |
276 | { |
277 | if (doLayout) executePostedLayout(); |
278 | QRect a = (q_func()->isRightToLeft() ? flipX(r: area.normalized()) : area.normalized()); |
279 | return commonListView->intersectingSet(area: a); |
280 | } |
281 | |
282 | inline void resetBatchStartRow() { commonListView->batchStartRow = 0; } |
283 | inline int batchStartRow() const { return commonListView->batchStartRow; } |
284 | inline QSize contentsSize() const { return commonListView->contentsSize; } |
285 | inline void setContentsSize(int w, int h) { commonListView->contentsSize = QSize(w, h); } |
286 | |
287 | inline int flipX(int x) const |
288 | { return qMax(a: viewport->width(), b: contentsSize().width()) - x; } |
289 | inline QPoint flipX(const QPoint &p) const |
290 | { return QPoint(flipX(x: p.x()), p.y()); } |
291 | inline QRect flipX(const QRect &r) const |
292 | { return QRect(flipX(x: r.x()) - r.width(), r.y(), r.width(), r.height()); } |
293 | inline QRect viewItemRect(const QListViewItem &item) const |
294 | { if (q_func()->isRightToLeft()) return flipX(r: item.rect()); return item.rect(); } |
295 | |
296 | QListViewItem indexToListViewItem(const QModelIndex &index) const; |
297 | inline QModelIndex listViewItemToIndex(const QListViewItem &item) const |
298 | { return model->index(row: commonListView->itemIndex(item), column, parent: root); } |
299 | |
300 | inline bool hasRectForIndex(const QModelIndex &index) const |
301 | { |
302 | return isIndexValid(index) && index.parent() == root && index.column() == column && !isHidden(row: index.row()); |
303 | } |
304 | |
305 | QRect rectForIndex(const QModelIndex &index) const |
306 | { |
307 | if (!hasRectForIndex(index)) |
308 | return QRect(); |
309 | executePostedLayout(); |
310 | return viewItemRect(item: indexToListViewItem(index)); |
311 | } |
312 | |
313 | QRect cellRectForIndex(const QModelIndex &index) |
314 | { |
315 | if (!hasRectForIndex(index)) |
316 | return QRect(); |
317 | executePostedLayout(); |
318 | auto oldItemAlignment = itemAlignment; |
319 | itemAlignment = Qt::Alignment(); |
320 | const QRect rect = rectForIndex(index); |
321 | itemAlignment = oldItemAlignment; |
322 | return rect; |
323 | } |
324 | |
325 | void viewUpdateGeometries() { q_func()->updateGeometries(); } |
326 | |
327 | |
328 | QRect mapToViewport(const QRect &rect, bool extend = true) const; |
329 | |
330 | QModelIndex closestIndex(const QRect &target, const QList<QModelIndex> &candidates) const; |
331 | QSize itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const; |
332 | |
333 | bool selectionAllowed(const QModelIndex &index) const override |
334 | { if (viewMode == QListView::ListMode && !showElasticBand) return index.isValid(); return true; } |
335 | |
336 | int horizontalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const; |
337 | int verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const; |
338 | |
339 | QItemSelection selection(const QRect &rect) const; |
340 | void selectAll(QItemSelectionModel::SelectionFlags command) override; |
341 | |
342 | #if QT_CONFIG(draganddrop) |
343 | QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const override; |
344 | bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index) override; |
345 | #endif |
346 | |
347 | inline void setGridSize(const QSize &size) { grid = size; } |
348 | inline QSize gridSize() const { return grid; } |
349 | inline void setWrapping(bool b) { wrap = b; } |
350 | inline bool isWrapping() const { return wrap; } |
351 | inline void setSpacing(int s) { space = s; } |
352 | inline int spacing() const { return space; } |
353 | inline void setSelectionRectVisible(bool visible) { showElasticBand = visible; } |
354 | inline bool isSelectionRectVisible() const { return showElasticBand; } |
355 | |
356 | inline QModelIndex modelIndex(int row) const { return model->index(row, column, parent: root); } |
357 | inline bool isHidden(int row) const { |
358 | QModelIndex idx = model->index(row, column: 0, parent: root); |
359 | return isPersistent(index: idx) && hiddenRows.contains(value: idx); |
360 | } |
361 | // helper to avoid checking for isPersistent and creating persistent indexes as above in isHidden |
362 | QList<int> hiddenRowIds() const |
363 | { |
364 | QList<int> rowIds; |
365 | rowIds.reserve(asize: hiddenRows.size()); |
366 | for (const auto &idx : hiddenRows) |
367 | rowIds += idx.row(); |
368 | return rowIds; |
369 | } |
370 | inline bool isHiddenOrDisabled(int row) const { return isHidden(row) || !isIndexEnabled(index: modelIndex(row)); } |
371 | |
372 | void removeCurrentAndDisabled(QList<QModelIndex> *indexes, const QModelIndex ¤t) const; |
373 | |
374 | void scrollElasticBandBy(int dx, int dy); |
375 | |
376 | QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const override; |
377 | |
378 | void emitIndexesMoved(const QModelIndexList &indexes) { emit q_func()->indexesMoved(indexes); } |
379 | |
380 | |
381 | QCommonListViewBase *commonListView; |
382 | |
383 | // ### FIXME: see if we can move the members into the dynamic/static classes |
384 | |
385 | bool wrap; |
386 | int space; |
387 | QSize grid; |
388 | |
389 | QListView::Flow flow; |
390 | QListView::Movement movement; |
391 | QListView::ResizeMode resizeMode; |
392 | QListView::LayoutMode layoutMode; |
393 | QListView::ViewMode viewMode; |
394 | |
395 | // the properties controlling the |
396 | // icon- or list-view modes |
397 | enum ModeProperties { |
398 | Wrap = 1, |
399 | Spacing = 2, |
400 | GridSize = 4, |
401 | Flow = 8, |
402 | Movement = 16, |
403 | ResizeMode = 32, |
404 | SelectionRectVisible = 64 |
405 | }; |
406 | |
407 | uint modeProperties : 8; |
408 | |
409 | QRect layoutBounds; |
410 | |
411 | // timers |
412 | QBasicTimer batchLayoutTimer; |
413 | |
414 | // used for hidden items |
415 | QSet<QPersistentModelIndex> hiddenRows; |
416 | |
417 | int column; |
418 | bool uniformItemSizes; |
419 | mutable QSize cachedItemSize; |
420 | int batchSize; |
421 | |
422 | QRect elasticBand; |
423 | bool showElasticBand; |
424 | |
425 | Qt::Alignment itemAlignment; |
426 | }; |
427 | |
428 | // inline implementations |
429 | |
430 | inline int QCommonListViewBase::spacing() const { return dd->spacing(); } |
431 | inline bool QCommonListViewBase::isWrapping() const { return dd->isWrapping(); } |
432 | inline QSize QCommonListViewBase::gridSize() const { return dd->gridSize(); } |
433 | inline QListView::Flow QCommonListViewBase::flow() const { return dd->flow; } |
434 | inline QListView::Movement QCommonListViewBase::movement() const { return dd->movement; } |
435 | |
436 | inline QPoint QCommonListViewBase::offset() const { return dd->offset(); } |
437 | inline QPoint QCommonListViewBase::pressedPosition() const { return dd->pressedPosition; } |
438 | inline bool QCommonListViewBase::uniformItemSizes() const { return dd->uniformItemSizes; } |
439 | inline int QCommonListViewBase::column() const { return dd->column; } |
440 | |
441 | inline QScrollBar *QCommonListViewBase::verticalScrollBar() const { return qq->verticalScrollBar(); } |
442 | inline QScrollBar *QCommonListViewBase::horizontalScrollBar() const { return qq->horizontalScrollBar(); } |
443 | inline QListView::ScrollMode QCommonListViewBase::verticalScrollMode() const { return qq->verticalScrollMode(); } |
444 | inline QListView::ScrollMode QCommonListViewBase::horizontalScrollMode() const { return qq->horizontalScrollMode(); } |
445 | |
446 | inline QModelIndex QCommonListViewBase::modelIndex(int row) const |
447 | { return dd->model->index(row, column: dd->column, parent: dd->root); } |
448 | inline int QCommonListViewBase::rowCount() const { return dd->model->rowCount(parent: dd->root); } |
449 | |
450 | inline void QCommonListViewBase::initViewItemOption(QStyleOptionViewItem *option) const { qq->initViewItemOption(option); } |
451 | inline QWidget *QCommonListViewBase::viewport() const { return dd->viewport; } |
452 | inline QRect QCommonListViewBase::clipRect() const { return dd->clipRect(); } |
453 | |
454 | inline QSize QCommonListViewBase::cachedItemSize() const { return dd->cachedItemSize; } |
455 | inline QRect QCommonListViewBase::viewItemRect(const QListViewItem &item) const { return dd->viewItemRect(item); } |
456 | inline QSize QCommonListViewBase::itemSize(const QStyleOptionViewItem &opt, const QModelIndex &idx) const |
457 | { return dd->itemSize(option: opt, index: idx); } |
458 | |
459 | inline QAbstractItemDelegate *QCommonListViewBase::delegate(const QModelIndex &idx) const |
460 | { return qq->itemDelegateForIndex(index: idx); } |
461 | |
462 | inline bool QCommonListViewBase::isHidden(int row) const { return dd->isHidden(row); } |
463 | inline int QCommonListViewBase::hiddenCount() const { return dd->hiddenRows.size(); } |
464 | |
465 | inline bool QCommonListViewBase::isRightToLeft() const { return qq->isRightToLeft(); } |
466 | |
467 | QT_END_NAMESPACE |
468 | |
469 | #endif // QLISTVIEW_P_H |
470 | |