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
67QT_BEGIN_NAMESPACE
68
69QQuickHeaderViewBasePrivate::QQuickHeaderViewBasePrivate()
70 : QQuickTableViewPrivate()
71{
72}
73
74QQuickHeaderViewBasePrivate::~QQuickHeaderViewBasePrivate()
75{
76}
77
78const QPointer<QQuickItem> QQuickHeaderViewBasePrivate::delegateItemAt(int row, int col) const
79{
80 return loadedTableItem(cell: QPoint(col, row))->item;
81}
82
83QVariant QQuickHeaderViewBasePrivate::modelImpl() const
84{
85 if (auto model = m_headerDataProxyModel.sourceModel())
86 return QVariant::fromValue(value: model.data());
87#if QT_CONFIG(transposeproxymodel)
88 if (auto model = m_transposeProxyModel.sourceModel())
89 return QVariant::fromValue(value: model);
90#endif
91 return QQuickTableViewPrivate::modelImpl();
92}
93
94template <typename P, typename M>
95inline bool proxyModelSetter(QQuickHeaderViewBase *const q, P &proxyModel, M *model)
96{
97 if (model) {
98 if (model == proxyModel.sourceModel())
99 return true;
100 proxyModel.setSourceModel(model);
101 const auto &modelVariant = QVariant::fromValue(std::addressof(proxyModel));
102 bool isProxyModelChanged = (modelVariant != QQuickTableViewPrivate::get(q)->QQuickTableViewPrivate::modelImpl());
103 QQuickTableViewPrivate::get(q)->QQuickTableViewPrivate::setModelImpl(modelVariant);
104 //Necessary, since TableView's assigned model not changed, but proxy's source changed
105 if (!isProxyModelChanged)
106 emit q->modelChanged();
107 return true;
108 }
109 proxyModel.setSourceModel(nullptr);
110 return false;
111}
112
113void QQuickHeaderViewBasePrivate::setModelImpl(const QVariant &newModel)
114{
115 Q_Q(QQuickHeaderViewBase);
116 m_modelExplicitlySetByUser = true;
117 // Case 1: newModel is QAbstractTableModel
118 if (proxyModelSetter(q, proxyModel&: m_headerDataProxyModel, model: newModel.value<QAbstractTableModel *>()))
119 return;
120#if QT_CONFIG(transposeproxymodel)
121 // Case 2: newModel is QAbstractItemModel but not QAbstractTableModel
122 if (orientation() == Qt::Horizontal
123 && proxyModelSetter(q, proxyModel&: m_transposeProxyModel, model: newModel.value<QAbstractItemModel *>()))
124 return;
125#endif
126
127 QQuickTableViewPrivate::setModelImpl(newModel);
128}
129
130void QQuickHeaderViewBasePrivate::syncModel()
131{
132 Q_Q(QQuickHeaderViewBase);
133
134 if (assignedSyncView && !m_modelExplicitlySetByUser) {
135 auto newModel = assignedSyncView->model();
136 if (auto m = newModel.value<QAbstractItemModel *>())
137 proxyModelSetter(q, proxyModel&: m_headerDataProxyModel, model: m);
138 }
139
140 QQuickTableViewPrivate::syncModel();
141
142 isTransposed = false;
143 const auto aim = model->abstractItemModel();
144 if (orientation() == Qt::Horizontal) {
145 // For models that are just a list or a number, and especially not a
146 // table, we transpose the view when the orientation is horizontal.
147 // The model (list) will then be laid out horizontally rather than
148 // vertically, which is the otherwise the default.
149 isTransposed = !aim || aim->columnCount() == 1;
150 }
151 if (m_textRole.isEmpty() && aim)
152 m_textRole = QLatin1String("display");
153}
154
155void QQuickHeaderViewBasePrivate::syncSyncView()
156{
157 Q_Q(QQuickHeaderViewBase);
158 if (assignedSyncDirection != orientation()) {
159 qmlWarning(me: q_func()) << "Setting syncDirection other than Qt::"
160 << QVariant::fromValue(value: orientation()).toString()
161 << " is invalid.";
162 assignedSyncDirection = orientation();
163 }
164 if (assignedSyncView) {
165 QBoolBlocker fixupGuard(inUpdateContentSize, true);
166 if (orientation() == Qt::Horizontal) {
167 q->setLeftMargin(assignedSyncView->leftMargin());
168 q->setRightMargin(assignedSyncView->rightMargin());
169 } else {
170 q->setTopMargin(assignedSyncView->topMargin());
171 q->setBottomMargin(assignedSyncView->bottomMargin());
172 }
173 }
174 QQuickTableViewPrivate::syncSyncView();
175}
176
177QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent)
178 : QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent)
179{
180 d_func()->setOrientation(orient);
181 setSyncDirection(orient);
182}
183
184QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent)
185 : QQuickTableView(dd, parent)
186{
187}
188
189QQuickHeaderViewBase::~QQuickHeaderViewBase()
190{
191}
192
193QString QQuickHeaderViewBase::textRole() const
194{
195 Q_D(const QQuickHeaderViewBase);
196 return d->m_textRole;
197}
198
199void QQuickHeaderViewBase::setTextRole(const QString &role)
200{
201 Q_D(QQuickHeaderViewBase);
202 if (d->m_textRole == role)
203 return;
204
205 d->m_textRole = role;
206 emit textRoleChanged();
207}
208
209Qt::Orientation QQuickHeaderViewBasePrivate::orientation() const
210{
211 return m_headerDataProxyModel.orientation();
212}
213
214void QQuickHeaderViewBasePrivate::setOrientation(Qt::Orientation orientation)
215{
216 if (QQuickHeaderViewBasePrivate::orientation() == orientation)
217 return;
218 m_headerDataProxyModel.setOrientation(orientation);
219}
220
221QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickVerticalHeaderViewPrivate &dd, QQuickItem *parent)
222 : QQuickHeaderViewBase(dd, parent)
223{
224}
225
226/*! \internal
227 \class QHeaderDataProxyModel
228 \brief
229 QHeaderDataProxyModel is a proxy AbstractItemModel type that maps
230 source model's headerData() to correspondent data()
231 */
232QHeaderDataProxyModel::QHeaderDataProxyModel(QObject *parent)
233 : QAbstractItemModel(parent)
234{
235}
236
237QHeaderDataProxyModel::~QHeaderDataProxyModel() = default;
238
239void QHeaderDataProxyModel::setSourceModel(QAbstractItemModel *newSourceModel)
240{
241 if (m_model == newSourceModel)
242 return;
243 beginResetModel();
244 disconnectFromModel();
245 m_model = newSourceModel;
246 connectToModel();
247 endResetModel();
248}
249
250QModelIndex QHeaderDataProxyModel::index(int row, int column, const QModelIndex &parent) const
251{
252 return hasIndex(row, column, parent) ? createIndex(arow: row, acolumn: column) : QModelIndex();
253}
254
255QModelIndex QHeaderDataProxyModel::parent(const QModelIndex &child) const
256{
257 Q_UNUSED(child);
258 return QModelIndex();
259}
260
261QModelIndex QHeaderDataProxyModel::sibling(int row, int column, const QModelIndex &) const
262{
263 return index(row, column);
264}
265
266int QHeaderDataProxyModel::rowCount(const QModelIndex &parent) const
267{
268 if (parent.isValid())
269 return 0;
270 return m_model.isNull() ? -1 : (m_orientation == Qt::Horizontal ? 1 : m_model->rowCount(parent));
271}
272
273int QHeaderDataProxyModel::columnCount(const QModelIndex &parent) const
274{
275 if (parent.isValid())
276 return 0;
277 return m_model.isNull() ? -1 : (m_orientation == Qt::Vertical ? 1 : m_model->columnCount(parent));
278}
279
280QVariant QHeaderDataProxyModel::data(const QModelIndex &index, int role) const
281{
282 if (m_model.isNull())
283 return QVariant();
284 if (!hasIndex(row: index.row(), column: index.column()))
285 return QModelIndex();
286 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
287 return m_model->headerData(section, orientation: m_orientation, role);
288}
289
290bool QHeaderDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
291{
292 if (!hasIndex(row: index.row(), column: index.column()))
293 return false;
294 auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
295 auto ret = m_model->setHeaderData(section, orientation: m_orientation, value, role);
296 emit dataChanged(topLeft: index, bottomRight: index, roles: { role });
297 return ret;
298}
299
300bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
301{
302 if (!parent.isValid())
303 return rowCount(parent) > 0 && columnCount(parent) > 0;
304 return false;
305}
306
307QVariant QHeaderDataProxyModel::variantValue() const
308{
309 return QVariant::fromValue(value: static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
310}
311
312void QHeaderDataProxyModel::setOrientation(Qt::Orientation o)
313{
314 if (o == m_orientation)
315 return;
316 beginResetModel();
317 m_orientation = o;
318 endResetModel();
319}
320
321Qt::Orientation QHeaderDataProxyModel::orientation() const
322{
323 return m_orientation;
324}
325
326QPointer<QAbstractItemModel> QHeaderDataProxyModel::sourceModel() const
327{
328 return m_model;
329}
330
331void QHeaderDataProxyModel::connectToModel()
332{
333 if (m_model.isNull())
334 return;
335 connect(sender: m_model, signal: &QAbstractItemModel::headerDataChanged,
336 context: this, slot: [this](Qt::Orientation orient, int first, int last) {
337 if (orient != orientation())
338 return;
339 if (orient == Qt::Horizontal) {
340 emit dataChanged(topLeft: createIndex(arow: 0, acolumn: first), bottomRight: createIndex(arow: 0, acolumn: last));
341 } else {
342 emit dataChanged(topLeft: createIndex(arow: first, acolumn: 0), bottomRight: createIndex(arow: last, acolumn: 0));
343 }
344 });
345 connect(sender: m_model, signal: &QAbstractItemModel::modelAboutToBeReset,
346 context: this, slot: &QHeaderDataProxyModel::modelAboutToBeReset, type: Qt::UniqueConnection);
347 connect(sender: m_model, signal: &QAbstractItemModel::modelReset,
348 context: this, slot: &QHeaderDataProxyModel::modelReset, type: Qt::UniqueConnection);
349 connect(sender: m_model, signal: &QAbstractItemModel::rowsAboutToBeMoved,
350 context: this, slot: &QHeaderDataProxyModel::rowsAboutToBeMoved, type: Qt::UniqueConnection);
351 connect(sender: m_model, signal: &QAbstractItemModel::rowsMoved,
352 context: this, slot: &QHeaderDataProxyModel::rowsMoved, type: Qt::UniqueConnection);
353 connect(sender: m_model, signal: &QAbstractItemModel::rowsAboutToBeInserted,
354 context: this, slot: &QHeaderDataProxyModel::rowsAboutToBeInserted, type: Qt::UniqueConnection);
355 connect(sender: m_model, signal: &QAbstractItemModel::rowsInserted,
356 context: this, slot: &QHeaderDataProxyModel::rowsInserted, type: Qt::UniqueConnection);
357 connect(sender: m_model, signal: &QAbstractItemModel::rowsAboutToBeRemoved,
358 context: this, slot: &QHeaderDataProxyModel::rowsAboutToBeRemoved, type: Qt::UniqueConnection);
359 connect(sender: m_model, signal: &QAbstractItemModel::rowsRemoved,
360 context: this, slot: &QHeaderDataProxyModel::rowsRemoved, type: Qt::UniqueConnection);
361 connect(sender: m_model, signal: &QAbstractItemModel::columnsAboutToBeMoved,
362 context: this, slot: &QHeaderDataProxyModel::columnsAboutToBeMoved, type: Qt::UniqueConnection);
363 connect(sender: m_model, signal: &QAbstractItemModel::columnsMoved,
364 context: this, slot: &QHeaderDataProxyModel::columnsMoved, type: Qt::UniqueConnection);
365 connect(sender: m_model, signal: &QAbstractItemModel::columnsAboutToBeInserted,
366 context: this, slot: &QHeaderDataProxyModel::columnsAboutToBeInserted, type: Qt::UniqueConnection);
367 connect(sender: m_model, signal: &QAbstractItemModel::columnsInserted,
368 context: this, slot: &QHeaderDataProxyModel::columnsInserted, type: Qt::UniqueConnection);
369 connect(sender: m_model, signal: &QAbstractItemModel::columnsAboutToBeRemoved,
370 context: this, slot: &QHeaderDataProxyModel::columnsAboutToBeRemoved, type: Qt::UniqueConnection);
371 connect(sender: m_model, signal: &QAbstractItemModel::columnsRemoved,
372 context: this, slot: &QHeaderDataProxyModel::columnsRemoved, type: Qt::UniqueConnection);
373 connect(sender: m_model, signal: &QAbstractItemModel::layoutAboutToBeChanged,
374 context: this, slot: &QHeaderDataProxyModel::layoutAboutToBeChanged, type: Qt::UniqueConnection);
375 connect(sender: m_model, signal: &QAbstractItemModel::layoutChanged,
376 context: this, slot: &QHeaderDataProxyModel::layoutChanged, type: Qt::UniqueConnection);
377}
378
379void QHeaderDataProxyModel::disconnectFromModel()
380{
381 if (m_model.isNull())
382 return;
383 m_model->disconnect(receiver: this);
384}
385
386QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent)
387 : QQuickHeaderViewBase(Qt::Horizontal, parent)
388{
389 setFlickableDirection(FlickableDirection::HorizontalFlick);
390 setResizableColumns(true);
391}
392
393QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView()
394{
395}
396
397QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
398 : QQuickHeaderViewBase(Qt::Vertical, parent)
399{
400 setFlickableDirection(FlickableDirection::VerticalFlick);
401 setResizableRows(true);
402}
403
404QQuickVerticalHeaderView::~QQuickVerticalHeaderView()
405{
406}
407
408QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate() = default;
409
410QQuickHorizontalHeaderViewPrivate::~QQuickHorizontalHeaderViewPrivate() = default;
411
412QQuickVerticalHeaderViewPrivate::QQuickVerticalHeaderViewPrivate() = default;
413
414QQuickVerticalHeaderViewPrivate::~QQuickVerticalHeaderViewPrivate() = default;
415
416QT_END_NAMESPACE
417
418#include "moc_qquickheaderview_p_p.cpp"
419
420#include "moc_qquickheaderview_p.cpp"
421

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