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