1/****************************************************************************
2**
3** Copyright (C) 2018 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 QQUICKTABLEVIEW_P_P_H
41#define QQUICKTABLEVIEW_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 "qquicktableview_p.h"
55
56#include <QtCore/qtimer.h>
57#include <QtQmlModels/private/qqmltableinstancemodel_p.h>
58#include <QtQml/private/qqmlincubator_p.h>
59#include <QtQmlModels/private/qqmlchangeset_p.h>
60#include <QtQml/qqmlinfo.h>
61
62#include <QtQuick/private/qquickflickable_p_p.h>
63#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
64
65QT_BEGIN_NAMESPACE
66
67Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
68
69static const qreal kDefaultRowHeight = 50;
70static const qreal kDefaultColumnWidth = 50;
71
72class FxTableItem;
73class QQuickTableSectionSizeProviderPrivate;
74
75class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject {
76 Q_OBJECT
77
78public:
79 QQuickTableSectionSizeProvider(QObject *parent=nullptr);
80 void setSize(int section, qreal size);
81 qreal size(int section);
82 bool resetSize(int section);
83 void resetAll();
84
85Q_SIGNALS:
86 void sizeChanged();
87
88private:
89 Q_DISABLE_COPY(QQuickTableSectionSizeProvider)
90 Q_DECLARE_PRIVATE(QQuickTableSectionSizeProvider)
91};
92
93class Q_QUICK_PRIVATE_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
94{
95 Q_DECLARE_PUBLIC(QQuickTableView)
96
97public:
98 class TableEdgeLoadRequest
99 {
100 // Whenever we need to load new rows or columns in the
101 // table, we fill out a TableEdgeLoadRequest.
102 // TableEdgeLoadRequest is just a struct that keeps track
103 // of which cells that needs to be loaded, and which cell
104 // the table is currently loading. The loading itself is
105 // done by QQuickTableView.
106
107 public:
108 void begin(const QPoint &cell, const QPointF &pos, QQmlIncubator::IncubationMode incubationMode)
109 {
110 Q_ASSERT(!m_active);
111 m_active = true;
112 m_edge = Qt::Edge(0);
113 m_mode = incubationMode;
114 m_edgeIndex = cell.x();
115 m_visibleCellsInEdge.clear();
116 m_visibleCellsInEdge.append(t: cell.y());
117 m_currentIndex = 0;
118 m_startPos = pos;
119 qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
120 }
121
122 void begin(Qt::Edge edgeToLoad, int edgeIndex, const QList<int> visibleCellsInEdge, QQmlIncubator::IncubationMode incubationMode)
123 {
124 Q_ASSERT(!m_active);
125 m_active = true;
126 m_edge = edgeToLoad;
127 m_edgeIndex = edgeIndex;
128 m_visibleCellsInEdge = visibleCellsInEdge;
129 m_mode = incubationMode;
130 m_currentIndex = 0;
131 qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
132 }
133
134 inline void markAsDone() { m_active = false; }
135 inline bool isActive() { return m_active; }
136
137 inline QPoint currentCell() { return cellAt(index: m_currentIndex); }
138 inline bool hasCurrentCell() { return m_currentIndex < m_visibleCellsInEdge.count(); }
139 inline void moveToNextCell() { ++m_currentIndex; }
140
141 inline Qt::Edge edge() { return m_edge; }
142 inline int row() { return cellAt(index: 0).y(); }
143 inline int column() { return cellAt(index: 0).x(); }
144 inline QQmlIncubator::IncubationMode incubationMode() { return m_mode; }
145
146 inline QPointF startPosition() { return m_startPos; }
147
148 QString toString()
149 {
150 QString str;
151 QDebug dbg(&str);
152 dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
153 << m_edge << ", edgeIndex:" << m_edgeIndex << ", incubation:";
154
155 switch (m_mode) {
156 case QQmlIncubator::Asynchronous:
157 dbg << "Asynchronous";
158 break;
159 case QQmlIncubator::AsynchronousIfNested:
160 dbg << "AsynchronousIfNested";
161 break;
162 case QQmlIncubator::Synchronous:
163 dbg << "Synchronous";
164 break;
165 }
166
167 return str;
168 }
169
170 private:
171 Qt::Edge m_edge = Qt::Edge(0);
172 QList<int> m_visibleCellsInEdge;
173 int m_edgeIndex = 0;
174 int m_currentIndex = 0;
175 bool m_active = false;
176 QQmlIncubator::IncubationMode m_mode = QQmlIncubator::AsynchronousIfNested;
177 QPointF m_startPos;
178
179 inline QPoint cellAt(int index) {
180 return !m_edge || (m_edge & (Qt::LeftEdge | Qt::RightEdge))
181 ? QPoint(m_edgeIndex, m_visibleCellsInEdge[index])
182 : QPoint(m_visibleCellsInEdge[index], m_edgeIndex);
183 }
184 };
185
186 class EdgeRange {
187 public:
188 EdgeRange();
189 bool containsIndex(Qt::Edge edge, int index);
190
191 int startIndex;
192 int endIndex;
193 qreal size;
194 };
195
196 enum class RebuildState {
197 Begin = 0,
198 LoadInitalTable,
199 VerifyTable,
200 LayoutTable,
201 LoadAndUnloadAfterLayout,
202 PreloadColumns,
203 PreloadRows,
204 MovePreloadedItemsToPool,
205 Done
206 };
207
208 enum class RebuildOption {
209 None = 0,
210 LayoutOnly = 0x1,
211 ViewportOnly = 0x2,
212 CalculateNewTopLeftRow = 0x4,
213 CalculateNewTopLeftColumn = 0x8,
214 CalculateNewContentWidth = 0x10,
215 CalculateNewContentHeight = 0x20,
216 All = 0x40,
217 };
218 Q_DECLARE_FLAGS(RebuildOptions, RebuildOption)
219
220public:
221 QQuickTableViewPrivate();
222 ~QQuickTableViewPrivate() override;
223
224 static inline QQuickTableViewPrivate *get(QQuickTableView *q) { return q->d_func(); }
225
226 void updatePolish() override;
227 void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
228
229public:
230 QHash<int, FxTableItem *> loadedItems;
231
232 // model, tableModel and modelVariant all point to the same model. modelVariant
233 // is the model assigned by the user. And tableModel is the wrapper model we create
234 // around it. But if the model is an instance model directly, we cannot wrap it, so
235 // we need a pointer for that case as well.
236 QQmlInstanceModel* model = nullptr;
237 QPointer<QQmlTableInstanceModel> tableModel = nullptr;
238 QVariant modelVariant;
239
240 // When the applications assignes a new model or delegate to the view, we keep them
241 // around until we're ready to take them into use (syncWithPendingChanges).
242 QVariant assignedModel = QVariant(int(0));
243 QQmlComponent *assignedDelegate = nullptr;
244
245 // loadedRows/Columns describes the rows and columns that are currently loaded (from top left
246 // row/column to bottom right row/column). loadedTableOuterRect describes the actual
247 // pixels that all the loaded delegate items cover, and is matched agains the viewport to determine when
248 // we need to fill up with more rows/columns. loadedTableInnerRect describes the pixels
249 // that the loaded table covers if you remove one row/column on each side of the table, and
250 // is used to determine rows/columns that are no longer visible and can be unloaded.
251 QMap<int, int> loadedColumns;
252 QMap<int, int> loadedRows;
253 QRectF loadedTableOuterRect;
254 QRectF loadedTableInnerRect;
255
256 QPointF origin = QPointF(0, 0);
257 QSizeF endExtent = QSizeF(0, 0);
258
259 QRectF viewportRect = QRectF(0, 0, -1, -1);
260
261 QSize tableSize;
262
263 RebuildState rebuildState = RebuildState::Done;
264 RebuildOptions rebuildOptions = RebuildOption::All;
265 RebuildOptions scheduledRebuildOptions = RebuildOption::All;
266
267 TableEdgeLoadRequest loadRequest;
268
269 QSizeF cellSpacing = QSizeF(0, 0);
270
271 QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
272
273 bool blockItemCreatedCallback = false;
274 bool layoutWarningIssued = false;
275 bool polishing = false;
276 bool syncVertically = false;
277 bool syncHorizontally = false;
278 bool inSetLocalViewportPos = false;
279 bool inSyncViewportPosRecursive = false;
280 bool inUpdateContentSize = false;
281
282 // isTransposed is currently only used by HeaderView.
283 // Consider making it public.
284 bool isTransposed = false;
285
286 QJSValue rowHeightProvider;
287 QJSValue columnWidthProvider;
288 QQuickTableSectionSizeProvider rowHeights;
289 QQuickTableSectionSizeProvider columnWidths;
290
291 EdgeRange cachedNextVisibleEdgeIndex[4];
292 EdgeRange cachedColumnWidth;
293 EdgeRange cachedRowHeight;
294
295 // TableView uses contentWidth/height to report the size of the table (this
296 // will e.g make scrollbars written for Flickable work out of the box). This
297 // value is continuously calculated, and will change/improve as more columns
298 // are loaded into view. At the same time, we want to open up for the
299 // possibility that the application can set the content width explicitly, in
300 // case it knows what the exact width should be from the start. We therefore
301 // override the contentWidth/height properties from QQuickFlickable, to be able
302 // to implement this combined behavior. This also lets us lazy build the table
303 // if the application needs to know the content size early on.
304 QQmlNullableValue<qreal> explicitContentWidth;
305 QQmlNullableValue<qreal> explicitContentHeight;
306
307 QSizeF averageEdgeSize;
308
309 QPointer<QQuickTableView> assignedSyncView;
310 QPointer<QQuickTableView> syncView;
311 QList<QPointer<QQuickTableView> > syncChildren;
312 Qt::Orientations assignedSyncDirection = Qt::Horizontal | Qt::Vertical;
313
314 const static QPoint kLeft;
315 const static QPoint kRight;
316 const static QPoint kUp;
317 const static QPoint kDown;
318
319#ifdef QT_DEBUG
320 QString forcedIncubationMode = qEnvironmentVariable(varName: "QT_TABLEVIEW_INCUBATION_MODE");
321#endif
322
323public:
324 QQuickTableViewAttached *getAttachedObject(const QObject *object) const;
325
326 int modelIndexAtCell(const QPoint &cell) const;
327 QPoint cellAtModelIndex(int modelIndex) const;
328
329 qreal sizeHintForColumn(int column);
330 qreal sizeHintForRow(int row);
331 QSize calculateTableSize();
332 void updateTableSize();
333
334 inline bool isColumnHidden(int column);
335 inline bool isRowHidden(int row);
336
337 qreal getColumnLayoutWidth(int column);
338 qreal getRowLayoutHeight(int row);
339 qreal getColumnWidth(int column);
340 qreal getRowHeight(int row);
341
342 inline int topRow() const { return loadedRows.firstKey(); }
343 inline int bottomRow() const { return loadedRows.lastKey(); }
344 inline int leftColumn() const { return loadedColumns.firstKey(); }
345 inline int rightColumn() const { return loadedColumns.lastKey(); }
346
347 QQuickTableView *rootSyncView() const;
348
349 bool updateTableRecursive();
350 bool updateTable();
351 void relayoutTableItems();
352
353 void layoutVerticalEdge(Qt::Edge tableEdge);
354 void layoutHorizontalEdge(Qt::Edge tableEdge);
355 void layoutTopLeftItem();
356 void layoutTableEdgeFromLoadRequest();
357
358 void updateContentWidth();
359 void updateContentHeight();
360 void updateAverageColumnWidth();
361 void updateAverageRowHeight();
362 RebuildOptions checkForVisibilityChanges();
363 void forceLayout();
364
365 void updateExtents();
366 void syncLoadedTableRectFromLoadedTable();
367 void syncLoadedTableFromLoadRequest();
368
369 int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
370 int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
371 bool allColumnsLoaded();
372 bool allRowsLoaded();
373 inline int edgeToArrayIndex(Qt::Edge edge);
374 void clearEdgeSizeCache();
375
376 bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
377 bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
378 Qt::Edge nextEdgeToLoad(const QRectF rect);
379 Qt::Edge nextEdgeToUnload(const QRectF rect);
380
381 qreal cellWidth(const QPoint &cell);
382 qreal cellHeight(const QPoint &cell);
383
384 FxTableItem *loadedTableItem(const QPoint &cell) const;
385 FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
386 FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
387
388 void releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag);
389 void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
390
391 void unloadItem(const QPoint &cell);
392 void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
393 void unloadEdge(Qt::Edge edge);
394 void loadAndUnloadVisibleEdges();
395 void drainReusePoolAfterLoadRequest();
396 void processLoadRequest();
397
398 void processRebuildTable();
399 bool moveToNextRebuildState();
400 void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos);
401 void beginRebuildTable();
402 void layoutAfterLoadingInitialTable();
403
404 void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
405
406 int resolveImportVersion();
407 void createWrapperModel();
408
409 void initItemCallback(int modelIndex, QObject *item);
410 void itemCreatedCallback(int modelIndex, QObject *object);
411 void itemPooledCallback(int modelIndex, QObject *object);
412 void itemReusedCallback(int modelIndex, QObject *object);
413 void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
414
415 virtual void syncWithPendingChanges();
416 virtual void syncDelegate();
417 virtual QVariant modelImpl() const;
418 virtual void setModelImpl(const QVariant &newModel);
419 virtual void syncModel();
420 inline void syncRebuildOptions();
421 virtual void syncSyncView();
422
423 void connectToModel();
424 void disconnectFromModel();
425
426 void rowsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
427 void columnsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column);
428 void rowsInsertedCallback(const QModelIndex &parent, int begin, int end);
429 void rowsRemovedCallback(const QModelIndex &parent, int begin, int end);
430 void columnsInsertedCallback(const QModelIndex &parent, int begin, int end);
431 void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
432 void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
433 void modelResetCallback();
434
435 void scheduleRebuildIfFastFlick();
436 void setLocalViewportX(qreal contentX);
437 void setLocalViewportY(qreal contentY);
438 void syncViewportRect();
439 void syncViewportPosRecursive();
440
441 void fetchMoreData();
442
443 void _q_componentFinalized();
444 void registerCallbackWhenBindingsAreEvaluated();
445
446 inline QString tableLayoutToString() const;
447 void dumpTable() const;
448};
449
450class FxTableItem : public QQuickItemViewFxItem
451{
452public:
453 FxTableItem(QQuickItem *item, QQuickTableView *table, bool own)
454 : QQuickItemViewFxItem(item, own, QQuickTableViewPrivate::get(q: table))
455 {
456 }
457
458 qreal position() const override { return 0; }
459 qreal endPosition() const override { return 0; }
460 qreal size() const override { return 0; }
461 qreal sectionSize() const override { return 0; }
462 bool contains(qreal, qreal) const override { return false; }
463
464 QPoint cell;
465};
466
467Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickTableViewPrivate::RebuildOptions)
468
469QT_END_NAMESPACE
470
471#endif
472

source code of qtdeclarative/src/quick/items/qquicktableview_p_p.h