Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickheaderview.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant reason:default
4
5#include <QtQuickTemplates2/private/qquickheaderview_p_p.h>
6#include <algorithm>
7
8/*!
9 \qmltype HorizontalHeaderView
10 \inqmlmodule QtQuick.Controls
11 \ingroup qtquickcontrols-containers
12 \inherits TableView
13 \brief Provides a horizontal header view to accompany a \l TableView.
14
15 \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
16
17 \sa VerticalHeaderView
18*/
19
20/*!
21 \qmltype VerticalHeaderView
22 \inqmlmodule QtQuick.Controls
23 \ingroup qtquickcontrols-containers
24 \inherits TableView
25 \brief Offers a vertical header view to accompany a \l TableView.
26
27 \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
28
29 \sa HorizontalHeaderView
30*/
31
32/*!
33 \qmlproperty TableView QtQuick.Controls::HorizontalHeaderView::syncView
34
35 \include qquickheaderview.qdocinc {syncView} {horizontally}
36*/
37
38/*!
39 \qmlproperty TableView QtQuick.Controls::VerticalHeaderView::syncView
40
41 \include qquickheaderview.qdocinc {syncView} {vertically}
42*/
43
44/*!
45 \qmlproperty QVariant QtQuick.Controls::HorizontalHeaderView::model
46
47 \include qquickheaderview.qdocinc {model} {horizontal}
48*/
49
50/*!
51 \qmlproperty QVariant QtQuick.Controls::VerticalHeaderView::model
52
53 \include qquickheaderview.qdocinc {model} {vertical}
54*/
55
56/*!
57 \qmlproperty string QtQuick.Controls::HorizontalHeaderView::textRole
58
59 \include qquickheaderview.qdocinc {textRole}
60*/
61
62/*!
63 \qmlproperty string QtQuick.Controls::VerticalHeaderView::textRole
64
65 \include qquickheaderview.qdocinc {textRole}
66*/
67
68/*!
69 \qmlproperty bool QtQuick.Controls::HorizontalHeaderView::movableColumns
70 \since 6.8
71
72 \include qquickheaderview.qdocinc {movableColumns}
73*/
74
75/*!
76 \qmlproperty bool QtQuick.Controls::VerticalHeaderView::movableRows
77 \since 6.8
78
79 \include qquickheaderview.qdocinc {movableRows}
80*/
81
83
84static const char *kRequiredProperty_headerView = "headerView";
85static const char *kRequiredProperty_model = "model";
86
87QQuickHeaderViewBasePrivate::QQuickHeaderViewBasePrivate()
88 : QQuickTableViewPrivate()
89{
90}
91
92QQuickHeaderViewBasePrivate::~QQuickHeaderViewBasePrivate()
93{
94}
95
96void QQuickHeaderViewBasePrivate::init()
97{
98 Q_Q(QQuickHeaderViewBase);
99 m_headerDataProxyModel.m_headerView = q;
100 setSizePolicy(orientation() == Qt::Horizontal ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed,
101 orientation() == Qt::Horizontal ? QLayoutPolicy::Fixed : QLayoutPolicy::Preferred);
102 q->setSyncDirection(orientation());
103}
104
105const QPointer<QQuickItem> QQuickHeaderViewBasePrivate::delegateItemAt(int row, int col) const
106{
107 return loadedTableItem(QPoint(col, row))->item;
108}
109
110QVariant QQuickHeaderViewBasePrivate::modelImpl() const
111{
112 // modelImpl is supposed to return the actual model that is
113 // is used (that is, the proxy model), and not the source model.
114 if (m_headerDataProxyModel.sourceModel())
115 return QVariant::fromValue(&m_headerDataProxyModel);
116
117 // If we're not using the proxy model, use the assigned model
118 return QQuickTableViewPrivate::modelImpl();
119}
120
121void QQuickHeaderViewBasePrivate::setModelImpl(const QVariant &newModel)
122{
123 m_modelExplicitlySet = newModel.isValid();
124
125 auto asTableModel = [](QAbstractItemModel *model) -> QAbstractItemModel* {
126 if (qobject_cast<QAbstractTableModel *>(model))
127 return model;
128
129 // Since QQmlTableModel is not derived from QAbstractTableModel (QATM),
130 // we have to check for this model separately. We want it to behave like
131 // QATM to keep existing code working. This model is in the Labs module,
132 // and we cannot link to it to make a qobject_cast. Therefore, we are checking
133 // the class name.
134 // TODO: When QQmlTableModel leaves Labs, change to qobject_cast?
135 if (model && QByteArrayView(model->metaObject()->className()) == "QQmlTableModel")
136 return model;
137 return nullptr;
138 };
139
140 if (auto qabstracttablemodel = asTableModel(qaim(newModel))) {
141 if (qabstracttablemodel != m_headerDataProxyModel.sourceModel()) {
142 m_headerDataProxyModel.setSourceModel(qabstracttablemodel);
143 assignedModel = QVariant::fromValue(std::addressof(m_headerDataProxyModel));
144 scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
145 emit q_func()->modelChanged();
146 }
147 return;
148 }
149
150 m_headerDataProxyModel.setSourceModel(nullptr);
151 QQuickTableViewPrivate::setModelImpl(newModel);
152}
153
154void QQuickHeaderViewBasePrivate::syncModel()
155{
156 if (!m_modelExplicitlySet && assignedSyncView) {
157 // When no model has been explicitly set, but we have a syncView, we
158 // should use the headerData() from the syncView's model as model.
159 // If the model is not a QAbstractItemModel, or if it doesn't contain
160 // any header data, header view will be empty.
161 auto syncView_d = QQuickTableViewPrivate::get(assignedSyncView);
162 auto syncViewModelAsQaim = qaim(syncView_d->assignedModel);
163 if (syncViewModelAsQaim != m_headerDataProxyModel.sourceModel()) {
164 m_headerDataProxyModel.setSourceModel(syncViewModelAsQaim); // can be nullptr
165 assignedModel = QVariant::fromValue(std::addressof(m_headerDataProxyModel));
166 emit q_func()->modelChanged();
167 }
168 }
169
170 QQuickTableViewPrivate::syncModel();
171
172 // For models that are just a list or a number, and especially not a
173 // table, we transpose the view when the orientation is horizontal.
174 // The model (list) will then be laid out horizontally rather than
175 // vertically, which is otherwise the default.
176 isTransposed = false;
177 const auto aim = model->abstractItemModel();
178 if (orientation() == Qt::Horizontal)
179 isTransposed = !aim || aim->columnCount() == 1;
180
181 if (m_textRole.isEmpty() && aim)
182 m_textRole = QLatin1String("display");
183}
184
185void QQuickHeaderViewBasePrivate::syncSyncView()
186{
187 if (assignedSyncDirection != orientation()) {
188 qmlWarning(q_func()) << "Setting syncDirection other than Qt::"
189 << QVariant::fromValue(orientation()).toString()
190 << " is invalid.";
191 assignedSyncDirection = orientation();
192 }
193 QQuickTableViewPrivate::syncSyncView();
194}
195
196QAbstractItemModel *QQuickHeaderViewBasePrivate::selectionSourceModel()
197{
198 // Our proxy model shares no common model items with HeaderView.model. So
199 // selections done in HeaderView cannot be represented in an ItemSelectionModel
200 // that is shared with the syncView (and for the same reason, the mapping functions
201 // modelIndex(cell) and cellAtIndex(index) have not been overridden either).
202 // Instead, we set the internal proxy model as selection source model.
203 return &m_headerDataProxyModel;
204}
205
206void QQuickHeaderViewBasePrivate::initItemCallback(int modelIndex, QObject *object)
207{
208 Q_Q(QQuickHeaderViewBase);
209
210 QQuickTableViewPrivate::initItemCallback(modelIndex, object);
211
212 auto item = qobject_cast<QQuickItem *>(object);
213 if (!item)
214 return;
215
216 QQmlDelegateModelItem *modelItem = tableModel->getModelItem(modelIndex);
217
218 setRequiredProperty(kRequiredProperty_headerView, QVariant::fromValue(q),
219 modelIndex, item, true);
220 setRequiredProperty(kRequiredProperty_model, QVariant::fromValue(modelItem->modelObject()),
221 modelIndex, item, true);
222}
223
224int QQuickHeaderViewBasePrivate::logicalRowIndex(const int visualIndex) const
225{
226 return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? visualIndex : QQuickTableViewPrivate::logicalRowIndex(visualIndex);
227}
228
229int QQuickHeaderViewBasePrivate::logicalColumnIndex(const int visualIndex) const
230{
231 return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? visualIndex : QQuickTableViewPrivate::logicalColumnIndex(visualIndex);
232}
233
234int QQuickHeaderViewBasePrivate::visualRowIndex(const int logicalIndex) const
235{
236 return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? logicalIndex : QQuickTableViewPrivate::visualRowIndex(logicalIndex);
237}
238
239int QQuickHeaderViewBasePrivate::visualColumnIndex(const int logicalIndex) const
240{
241 return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? logicalIndex : QQuickTableViewPrivate::visualColumnIndex(logicalIndex);
242}
243
244QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent)
245 : QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent)
246{
247 Q_D(QQuickHeaderViewBase);
248 d->setOrientation(orient);
249 d->init();
250}
251
252QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent)
253 : QQuickTableView(dd, parent)
254{
255 Q_D(QQuickHeaderViewBase);
256 d->init();
257}
258
259QQuickHeaderViewBase::~QQuickHeaderViewBase()
260{
261}
262
263QString QQuickHeaderViewBase::textRole() const
264{
265 Q_D(const QQuickHeaderViewBase);
266 return d->m_textRole;
267}
268
269void QQuickHeaderViewBase::setTextRole(const QString &role)
270{
271 Q_D(QQuickHeaderViewBase);
272 if (d->m_textRole == role)
273 return;
274
275 d->m_textRole = role;
276 emit textRoleChanged();
277}
278
279Qt::Orientation QQuickHeaderViewBasePrivate::orientation() const
280{
281 return m_headerDataProxyModel.orientation();
282}
283
284void QQuickHeaderViewBasePrivate::setOrientation(Qt::Orientation orientation)
285{
286 if (QQuickHeaderViewBasePrivate::orientation() == orientation)
287 return;
288 m_headerDataProxyModel.setOrientation(orientation);
289}
290
291QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickVerticalHeaderViewPrivate &dd, QQuickItem *parent)
292 : QQuickHeaderViewBase(dd, parent)
293{
294}
295
296/*! \internal
297 \class QHeaderDataProxyModel
298 \brief
299 QHeaderDataProxyModel is a proxy AbstractItemModel type that maps
300 source model's headerData() to correspondent data()
301 */
302QHeaderDataProxyModel::QHeaderDataProxyModel(QObject *parent)
303 : QAbstractItemModel(parent)
304{
305}
306
307QHeaderDataProxyModel::~QHeaderDataProxyModel() = default;
308
309void QHeaderDataProxyModel::setSourceModel(QAbstractItemModel *newSourceModel)
310{
311 if (m_model == newSourceModel)
312 return;
313 beginResetModel();
314 disconnectFromModel();
315 m_model = newSourceModel;
316 connectToModel();
317 endResetModel();
318}
319
320QModelIndex QHeaderDataProxyModel::index(int row, int column, const QModelIndex &parent) const
321{
322 return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex();
323}
324
325QModelIndex QHeaderDataProxyModel::parent(const QModelIndex &child) const
326{
327 Q_UNUSED(child);
328 return QModelIndex();
329}
330
331QModelIndex QHeaderDataProxyModel::sibling(int row, int column, const QModelIndex &) const
332{
333 return index(row, column);
334}
335
336int QHeaderDataProxyModel::rowCount(const QModelIndex &parent) const
337{
338 if (parent.isValid())
339 return 0;
340 return m_model.isNull() ? -1 : (m_orientation == Qt::Horizontal ? 1 : m_model->rowCount(parent));
341}
342
343int QHeaderDataProxyModel::columnCount(const QModelIndex &parent) const
344{
345 if (parent.isValid())
346 return 0;
347 return m_model.isNull() ? -1 : (m_orientation == Qt::Vertical ? 1 : m_model->columnCount(parent));
348}
349
350QVariant QHeaderDataProxyModel::data(const QModelIndex &index, int role) const
351{
352 if (m_model.isNull())
353 return QVariant();
354 if (!hasIndex(index.row(), index.column()))
355 return QModelIndex();
356 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
357 return m_model->headerData(section, m_orientation, role);
358}
359
360bool QHeaderDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
361{
362 if (!hasIndex(index.row(), index.column()))
363 return false;
364 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
365 auto ret = m_model->setHeaderData(section, m_orientation, value, role);
366 emit dataChanged(index, index, { role });
367 return ret;
368}
369
370bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
371{
372 if (!parent.isValid())
373 return rowCount(parent) > 0 && columnCount(parent) > 0;
374 return false;
375}
376
377QHash<int, QByteArray> QHeaderDataProxyModel::roleNames() const
378{
379 using namespace Qt::Literals::StringLiterals;
380
381 auto names = m_model ? m_model->roleNames() : QAbstractItemModel::roleNames();
382 if (m_headerView) {
383 QString textRole = m_headerView->textRole();
384 if (textRole.isEmpty())
385 textRole = u"display"_s;
386 if (!names.values().contains(textRole.toUtf8().constData())) {
387 qmlWarning(m_headerView).nospace() << "The 'textRole' property contains a role that doesn't exist in the model: "
388 << textRole << ". Check your model's roleNames() implementation";
389 }
390 }
391
392 return names;
393}
394
395QVariant QHeaderDataProxyModel::variantValue() const
396{
397 return QVariant::fromValue(static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
398}
399
400void QHeaderDataProxyModel::setOrientation(Qt::Orientation o)
401{
402 if (o == m_orientation)
403 return;
404 beginResetModel();
405 m_orientation = o;
406 endResetModel();
407}
408
409Qt::Orientation QHeaderDataProxyModel::orientation() const
410{
411 return m_orientation;
412}
413
414QPointer<QAbstractItemModel> QHeaderDataProxyModel::sourceModel() const
415{
416 return m_model;
417}
418
419void QHeaderDataProxyModel::connectToModel()
420{
421 if (m_model.isNull())
422 return;
423 connect(m_model, &QAbstractItemModel::headerDataChanged,
424 this, [this](Qt::Orientation orient, int first, int last) {
425 if (orient != orientation())
426 return;
427 if (orient == Qt::Horizontal) {
428 emit dataChanged(createIndex(0, first), createIndex(0, last));
429 } else {
430 emit dataChanged(createIndex(first, 0), createIndex(last, 0));
431 }
432 });
433 connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
434 this, &QHeaderDataProxyModel::modelAboutToBeReset, Qt::UniqueConnection);
435 connect(m_model, &QAbstractItemModel::modelReset,
436 this, &QHeaderDataProxyModel::modelReset, Qt::UniqueConnection);
437 connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
438 this, &QHeaderDataProxyModel::rowsAboutToBeMoved, Qt::UniqueConnection);
439 connect(m_model, &QAbstractItemModel::rowsMoved,
440 this, &QHeaderDataProxyModel::rowsMoved, Qt::UniqueConnection);
441 connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
442 this, &QHeaderDataProxyModel::rowsAboutToBeInserted, Qt::UniqueConnection);
443 connect(m_model, &QAbstractItemModel::rowsInserted,
444 this, &QHeaderDataProxyModel::rowsInserted, Qt::UniqueConnection);
445 connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
446 this, &QHeaderDataProxyModel::rowsAboutToBeRemoved, Qt::UniqueConnection);
447 connect(m_model, &QAbstractItemModel::rowsRemoved,
448 this, &QHeaderDataProxyModel::rowsRemoved, Qt::UniqueConnection);
449 connect(m_model, &QAbstractItemModel::columnsAboutToBeMoved,
450 this, &QHeaderDataProxyModel::columnsAboutToBeMoved, Qt::UniqueConnection);
451 connect(m_model, &QAbstractItemModel::columnsMoved,
452 this, &QHeaderDataProxyModel::columnsMoved, Qt::UniqueConnection);
453 connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
454 this, &QHeaderDataProxyModel::columnsAboutToBeInserted, Qt::UniqueConnection);
455 connect(m_model, &QAbstractItemModel::columnsInserted,
456 this, &QHeaderDataProxyModel::columnsInserted, Qt::UniqueConnection);
457 connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
458 this, &QHeaderDataProxyModel::columnsAboutToBeRemoved, Qt::UniqueConnection);
459 connect(m_model, &QAbstractItemModel::columnsRemoved,
460 this, &QHeaderDataProxyModel::columnsRemoved, Qt::UniqueConnection);
461 connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
462 this, &QHeaderDataProxyModel::layoutAboutToBeChanged, Qt::UniqueConnection);
463 connect(m_model, &QAbstractItemModel::layoutChanged,
464 this, &QHeaderDataProxyModel::layoutChanged, Qt::UniqueConnection);
465}
466
467void QHeaderDataProxyModel::disconnectFromModel()
468{
469 if (m_model.isNull())
470 return;
471 m_model->disconnect(this);
472}
473
474QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent)
475 : QQuickHeaderViewBase(*(new QQuickHorizontalHeaderViewPrivate), parent)
476{
477 setFlickableDirection(FlickableDirection::HorizontalFlick);
478 setResizableColumns(true);
479}
480
481QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView()
482{
483#if QT_CONFIG(quick_draganddrop)
484 Q_D(QQuickHorizontalHeaderView);
485 d->destroySectionDragHandler();
486#endif
487}
488
489bool QQuickHorizontalHeaderView::movableColumns() const
490{
491 Q_D(const QQuickHorizontalHeaderView);
492 return d->m_movableColumns;
493}
494
495void QQuickHorizontalHeaderView::setMovableColumns(bool movableColumns)
496{
497 Q_D(QQuickHorizontalHeaderView);
498 if (d->m_movableColumns == movableColumns)
499 return;
500
501 d->m_movableColumns = movableColumns;
502
503#if QT_CONFIG(quick_draganddrop)
504 if (d->m_movableColumns)
505 d->initSectionDragHandler(Qt::Horizontal);
506 else
507 d->destroySectionDragHandler();
508#endif
509
510 emit movableColumnsChanged();
511}
512
513QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
514 : QQuickHeaderViewBase(*(new QQuickVerticalHeaderViewPrivate), parent)
515{
516 setFlickableDirection(FlickableDirection::VerticalFlick);
517 setResizableRows(true);
518}
519
520QQuickVerticalHeaderView::~QQuickVerticalHeaderView()
521{
522#if QT_CONFIG(quick_draganddrop)
523 Q_D(QQuickVerticalHeaderView);
524 d->destroySectionDragHandler();
525#endif
526}
527
528bool QQuickVerticalHeaderView::movableRows() const
529{
530 Q_D(const QQuickVerticalHeaderView);
531 return d->m_movableRows ;
532}
533
534void QQuickVerticalHeaderView::setMovableRows(bool movableRows)
535{
536 Q_D(QQuickVerticalHeaderView);
537 if (d->m_movableRows == movableRows)
538 return;
539
540 d->m_movableRows = movableRows;
541
542#if QT_CONFIG(quick_draganddrop)
543 if (d->m_movableRows)
544 d->initSectionDragHandler(Qt::Vertical);
545 else
546 d->destroySectionDragHandler();
547#endif
548
549 emit movableRowsChanged();
550}
551
552QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate()
553{
554 setOrientation(Qt::Horizontal);
555};
556
557QQuickHorizontalHeaderViewPrivate::~QQuickHorizontalHeaderViewPrivate() = default;
558
559QQuickVerticalHeaderViewPrivate::QQuickVerticalHeaderViewPrivate()
560{
561 setOrientation(Qt::Vertical);
562};
563
564QQuickVerticalHeaderViewPrivate::~QQuickVerticalHeaderViewPrivate() = default;
565
566QT_END_NAMESPACE
567
568#include "moc_qquickheaderview_p_p.cpp"
569
570#include "moc_qquickheaderview_p.cpp"
static QT_BEGIN_NAMESPACE const char * kRequiredProperty_headerView
\qmltype HorizontalHeaderView \inqmlmodule QtQuick.Controls\inherits TableView
static const char * kRequiredProperty_model