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 
65 QT_BEGIN_NAMESPACE
66 
67 Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
68 
69 static const qreal kDefaultRowHeight = 50;
70 static const qreal kDefaultColumnWidth = 50;
71 
72 class FxTableItem;
73 class QQuickTableSectionSizeProviderPrivate;
74 
75 class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject {
76     Q_OBJECT
77 
78 public:
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 
85 Q_SIGNALS:
86     void sizeChanged();
87 
88 private:
89     Q_DISABLE_COPY(QQuickTableSectionSizeProvider)
90     Q_DECLARE_PRIVATE(QQuickTableSectionSizeProvider)
91 };
92 
93 class Q_QUICK_PRIVATE_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
94 {
Q_DECLARE_PUBLIC(QQuickTableView)95     Q_DECLARE_PUBLIC(QQuickTableView)
96 
97 public:
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(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(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(0).y(); }
143         inline int column() { return cellAt(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 
220 public:
221     QQuickTableViewPrivate();
222     ~QQuickTableViewPrivate() override;
223 
get(QQuickTableView * q)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 
229 public:
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("QT_TABLEVIEW_INCUBATION_MODE");
321 #endif
322 
323 public:
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 
topRow()342     inline int topRow() const { return loadedRows.firstKey(); }
bottomRow()343     inline int bottomRow() const { return loadedRows.lastKey(); }
leftColumn()344     inline int leftColumn() const { return loadedColumns.firstKey(); }
rightColumn()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     inline int edgeToArrayIndex(Qt::Edge edge);
372     void clearEdgeSizeCache();
373 
374     bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
375     bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
376     Qt::Edge nextEdgeToLoad(const QRectF rect);
377     Qt::Edge nextEdgeToUnload(const QRectF rect);
378 
379     qreal cellWidth(const QPoint &cell);
380     qreal cellHeight(const QPoint &cell);
381 
382     FxTableItem *loadedTableItem(const QPoint &cell) const;
383     FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
384     FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
385 
386     void releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag);
387     void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
388 
389     void unloadItem(const QPoint &cell);
390     void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
391     void unloadEdge(Qt::Edge edge);
392     void loadAndUnloadVisibleEdges();
393     void drainReusePoolAfterLoadRequest();
394     void processLoadRequest();
395 
396     void processRebuildTable();
397     bool moveToNextRebuildState();
398     void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos);
399     void beginRebuildTable();
400     void layoutAfterLoadingInitialTable();
401 
402     void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
403 
404     int resolveImportVersion();
405     void createWrapperModel();
406 
407     void initItemCallback(int modelIndex, QObject *item);
408     void itemCreatedCallback(int modelIndex, QObject *object);
409     void itemPooledCallback(int modelIndex, QObject *object);
410     void itemReusedCallback(int modelIndex, QObject *object);
411     void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
412 
413     virtual void syncWithPendingChanges();
414     virtual void syncDelegate();
415     virtual QVariant modelImpl() const;
416     virtual void setModelImpl(const QVariant &newModel);
417     virtual void syncModel();
418     inline void syncRebuildOptions();
419     virtual void syncSyncView();
420 
421     void connectToModel();
422     void disconnectFromModel();
423 
424     void rowsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
425     void columnsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column);
426     void rowsInsertedCallback(const QModelIndex &parent, int begin, int end);
427     void rowsRemovedCallback(const QModelIndex &parent, int begin, int end);
428     void columnsInsertedCallback(const QModelIndex &parent, int begin, int end);
429     void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
430     void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
431     void modelResetCallback();
432 
433     void scheduleRebuildIfFastFlick();
434     void setLocalViewportX(qreal contentX);
435     void setLocalViewportY(qreal contentY);
436     void syncViewportRect();
437     void syncViewportPosRecursive();
438 
439     void fetchMoreData();
440 
441     void _q_componentFinalized();
442     void registerCallbackWhenBindingsAreEvaluated();
443 
444     inline QString tableLayoutToString() const;
445     void dumpTable() const;
446 };
447 
448 class FxTableItem : public QQuickItemViewFxItem
449 {
450 public:
FxTableItem(QQuickItem * item,QQuickTableView * table,bool own)451     FxTableItem(QQuickItem *item, QQuickTableView *table, bool own)
452         : QQuickItemViewFxItem(item, own, QQuickTableViewPrivate::get(table))
453     {
454     }
455 
position()456     qreal position() const override { return 0; }
endPosition()457     qreal endPosition() const override { return 0; }
size()458     qreal size() const override { return 0; }
sectionSize()459     qreal sectionSize() const override { return 0; }
contains(qreal,qreal)460     bool contains(qreal, qreal) const override { return false; }
461 
462     QPoint cell;
463 };
464 
465 Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickTableViewPrivate::RebuildOptions)
466 
467 QT_END_NAMESPACE
468 
469 #endif
470