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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/quicktemplates/qquickheaderview.cpp