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() || !hasIndex(index.row(), index.column()))
353 return QVariant();
354 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
355 return m_model->headerData(section, m_orientation, role);
356}
357
358bool QHeaderDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
359{
360 if (!hasIndex(index.row(), index.column()))
361 return false;
362 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
363 auto ret = m_model->setHeaderData(section, m_orientation, value, role);
364 return ret;
365}
366
367bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
368{
369 if (!parent.isValid())
370 return rowCount(parent) > 0 && columnCount(parent) > 0;
371 return false;
372}
373
374QHash<int, QByteArray> QHeaderDataProxyModel::roleNames() const
375{
376 using namespace Qt::Literals::StringLiterals;
377
378 auto names = m_model ? m_model->roleNames() : QAbstractItemModel::roleNames();
379 if (m_headerView) {
380 QString textRole = m_headerView->textRole();
381 if (textRole.isEmpty())
382 textRole = u"display"_s;
383 if (!names.values().contains(textRole.toUtf8().constData())) {
384 qmlWarning(m_headerView).nospace() << "The 'textRole' property contains a role that doesn't exist in the model: "
385 << textRole << ". Check your model's roleNames() implementation";
386 }
387 }
388
389 return names;
390}
391
392QVariant QHeaderDataProxyModel::variantValue() const
393{
394 return QVariant::fromValue(static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
395}
396
397void QHeaderDataProxyModel::setOrientation(Qt::Orientation o)
398{
399 if (o == m_orientation)
400 return;
401 beginResetModel();
402 m_orientation = o;
403 endResetModel();
404}
405
406Qt::Orientation QHeaderDataProxyModel::orientation() const
407{
408 return m_orientation;
409}
410
411QPointer<QAbstractItemModel> QHeaderDataProxyModel::sourceModel() const
412{
413 return m_model;
414}
415
416void QHeaderDataProxyModel::connectToModel()
417{
418 if (m_model.isNull())
419 return;
420 connect(m_model, &QAbstractItemModel::headerDataChanged,
421 this, [this](Qt::Orientation orient, int first, int last) {
422 if (orient != orientation())
423 return;
424 if (orient == Qt::Horizontal) {
425 emit dataChanged(createIndex(0, first), createIndex(0, last));
426 } else {
427 emit dataChanged(createIndex(first, 0), createIndex(last, 0));
428 }
429 });
430 connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
431 this, &QHeaderDataProxyModel::modelAboutToBeReset, Qt::UniqueConnection);
432 connect(m_model, &QAbstractItemModel::modelReset,
433 this, &QHeaderDataProxyModel::modelReset, Qt::UniqueConnection);
434 connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
435 this, &QHeaderDataProxyModel::rowsAboutToBeMoved, Qt::UniqueConnection);
436 connect(m_model, &QAbstractItemModel::rowsMoved,
437 this, &QHeaderDataProxyModel::rowsMoved, Qt::UniqueConnection);
438 connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
439 this, &QHeaderDataProxyModel::rowsAboutToBeInserted, Qt::UniqueConnection);
440 connect(m_model, &QAbstractItemModel::rowsInserted,
441 this, &QHeaderDataProxyModel::rowsInserted, Qt::UniqueConnection);
442 connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
443 this, &QHeaderDataProxyModel::rowsAboutToBeRemoved, Qt::UniqueConnection);
444 connect(m_model, &QAbstractItemModel::rowsRemoved,
445 this, &QHeaderDataProxyModel::rowsRemoved, Qt::UniqueConnection);
446 connect(m_model, &QAbstractItemModel::columnsAboutToBeMoved,
447 this, &QHeaderDataProxyModel::columnsAboutToBeMoved, Qt::UniqueConnection);
448 connect(m_model, &QAbstractItemModel::columnsMoved,
449 this, &QHeaderDataProxyModel::columnsMoved, Qt::UniqueConnection);
450 connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
451 this, &QHeaderDataProxyModel::columnsAboutToBeInserted, Qt::UniqueConnection);
452 connect(m_model, &QAbstractItemModel::columnsInserted,
453 this, &QHeaderDataProxyModel::columnsInserted, Qt::UniqueConnection);
454 connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
455 this, &QHeaderDataProxyModel::columnsAboutToBeRemoved, Qt::UniqueConnection);
456 connect(m_model, &QAbstractItemModel::columnsRemoved,
457 this, &QHeaderDataProxyModel::columnsRemoved, Qt::UniqueConnection);
458 connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
459 this, &QHeaderDataProxyModel::layoutAboutToBeChanged, Qt::UniqueConnection);
460 connect(m_model, &QAbstractItemModel::layoutChanged,
461 this, &QHeaderDataProxyModel::layoutChanged, Qt::UniqueConnection);
462}
463
464void QHeaderDataProxyModel::disconnectFromModel()
465{
466 if (m_model.isNull())
467 return;
468 m_model->disconnect(this);
469}
470
471QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent)
472 : QQuickHeaderViewBase(*(new QQuickHorizontalHeaderViewPrivate), parent)
473{
474 setFlickableDirection(FlickableDirection::HorizontalFlick);
475 setResizableColumns(true);
476}
477
478QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView()
479{
480#if QT_CONFIG(quick_draganddrop)
481 Q_D(QQuickHorizontalHeaderView);
482 d->destroySectionDragHandler();
483#endif
484}
485
486bool QQuickHorizontalHeaderView::movableColumns() const
487{
488 Q_D(const QQuickHorizontalHeaderView);
489 return d->m_movableColumns;
490}
491
492void QQuickHorizontalHeaderView::setMovableColumns(bool movableColumns)
493{
494 Q_D(QQuickHorizontalHeaderView);
495 if (d->m_movableColumns == movableColumns)
496 return;
497
498 d->m_movableColumns = movableColumns;
499
500#if QT_CONFIG(quick_draganddrop)
501 if (d->m_movableColumns)
502 d->initSectionDragHandler(Qt::Horizontal);
503 else
504 d->destroySectionDragHandler();
505#endif
506
507 emit movableColumnsChanged();
508}
509
510QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
511 : QQuickHeaderViewBase(*(new QQuickVerticalHeaderViewPrivate), parent)
512{
513 setFlickableDirection(FlickableDirection::VerticalFlick);
514 setResizableRows(true);
515}
516
517QQuickVerticalHeaderView::~QQuickVerticalHeaderView()
518{
519#if QT_CONFIG(quick_draganddrop)
520 Q_D(QQuickVerticalHeaderView);
521 d->destroySectionDragHandler();
522#endif
523}
524
525bool QQuickVerticalHeaderView::movableRows() const
526{
527 Q_D(const QQuickVerticalHeaderView);
528 return d->m_movableRows ;
529}
530
531void QQuickVerticalHeaderView::setMovableRows(bool movableRows)
532{
533 Q_D(QQuickVerticalHeaderView);
534 if (d->m_movableRows == movableRows)
535 return;
536
537 d->m_movableRows = movableRows;
538
539#if QT_CONFIG(quick_draganddrop)
540 if (d->m_movableRows)
541 d->initSectionDragHandler(Qt::Vertical);
542 else
543 d->destroySectionDragHandler();
544#endif
545
546 emit movableRowsChanged();
547}
548
549QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate()
550{
551 setOrientation(Qt::Horizontal);
552};
553
554QQuickHorizontalHeaderViewPrivate::~QQuickHorizontalHeaderViewPrivate() = default;
555
556QQuickVerticalHeaderViewPrivate::QQuickVerticalHeaderViewPrivate()
557{
558 setOrientation(Qt::Vertical);
559};
560
561QQuickVerticalHeaderViewPrivate::~QQuickVerticalHeaderViewPrivate() = default;
562
563QT_END_NAMESPACE
564
565#include "moc_qquickheaderview_p_p.cpp"
566
567#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