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 | QT_BEGIN_NAMESPACE |
68 | |
69 | QQuickHeaderViewBasePrivate::() |
70 | : QQuickTableViewPrivate() |
71 | { |
72 | } |
73 | |
74 | QQuickHeaderViewBasePrivate::() |
75 | { |
76 | } |
77 | |
78 | const QPointer<QQuickItem> QQuickHeaderViewBasePrivate::(int row, int col) const |
79 | { |
80 | return loadedTableItem(cell: QPoint(col, row))->item; |
81 | } |
82 | |
83 | QVariant QQuickHeaderViewBasePrivate::() 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 | |
94 | template <typename P, typename M> |
95 | inline bool (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 | |
113 | void QQuickHeaderViewBasePrivate::(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 | |
130 | void QQuickHeaderViewBasePrivate::() |
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 | |
155 | void QQuickHeaderViewBasePrivate::() |
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 | |
177 | QQuickHeaderViewBase::(Qt::Orientation orient, QQuickItem *parent) |
178 | : QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent) |
179 | { |
180 | d_func()->setOrientation(orient); |
181 | setSyncDirection(orient); |
182 | } |
183 | |
184 | QQuickHeaderViewBase::(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent) |
185 | : QQuickTableView(dd, parent) |
186 | { |
187 | } |
188 | |
189 | QQuickHeaderViewBase::() |
190 | { |
191 | } |
192 | |
193 | QString QQuickHeaderViewBase::() const |
194 | { |
195 | Q_D(const QQuickHeaderViewBase); |
196 | return d->m_textRole; |
197 | } |
198 | |
199 | void QQuickHeaderViewBase::(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 | |
209 | Qt::Orientation QQuickHeaderViewBasePrivate::() const |
210 | { |
211 | return m_headerDataProxyModel.orientation(); |
212 | } |
213 | |
214 | void QQuickHeaderViewBasePrivate::(Qt::Orientation orientation) |
215 | { |
216 | if (QQuickHeaderViewBasePrivate::orientation() == orientation) |
217 | return; |
218 | m_headerDataProxyModel.setOrientation(orientation); |
219 | } |
220 | |
221 | 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 | */ |
232 | QHeaderDataProxyModel::(QObject *parent) |
233 | : QAbstractItemModel(parent) |
234 | { |
235 | } |
236 | |
237 | QHeaderDataProxyModel::() = default; |
238 | |
239 | void QHeaderDataProxyModel::(QAbstractItemModel *newSourceModel) |
240 | { |
241 | if (m_model == newSourceModel) |
242 | return; |
243 | beginResetModel(); |
244 | disconnectFromModel(); |
245 | m_model = newSourceModel; |
246 | connectToModel(); |
247 | endResetModel(); |
248 | } |
249 | |
250 | QModelIndex QHeaderDataProxyModel::(int row, int column, const QModelIndex &parent) const |
251 | { |
252 | return hasIndex(row, column, parent) ? createIndex(arow: row, acolumn: column) : QModelIndex(); |
253 | } |
254 | |
255 | QModelIndex QHeaderDataProxyModel::(const QModelIndex &child) const |
256 | { |
257 | Q_UNUSED(child); |
258 | return QModelIndex(); |
259 | } |
260 | |
261 | QModelIndex QHeaderDataProxyModel::(int row, int column, const QModelIndex &) const |
262 | { |
263 | return index(row, column); |
264 | } |
265 | |
266 | int QHeaderDataProxyModel::(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 | |
273 | int 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 | |
280 | QVariant QHeaderDataProxyModel::(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 | |
290 | bool QHeaderDataProxyModel::(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 | |
300 | bool QHeaderDataProxyModel::(const QModelIndex &parent) const |
301 | { |
302 | if (!parent.isValid()) |
303 | return rowCount(parent) > 0 && columnCount(parent) > 0; |
304 | return false; |
305 | } |
306 | |
307 | QVariant QHeaderDataProxyModel::() const |
308 | { |
309 | return QVariant::fromValue(value: static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this))); |
310 | } |
311 | |
312 | void QHeaderDataProxyModel::(Qt::Orientation o) |
313 | { |
314 | if (o == m_orientation) |
315 | return; |
316 | beginResetModel(); |
317 | m_orientation = o; |
318 | endResetModel(); |
319 | } |
320 | |
321 | Qt::Orientation QHeaderDataProxyModel::() const |
322 | { |
323 | return m_orientation; |
324 | } |
325 | |
326 | QPointer<QAbstractItemModel> QHeaderDataProxyModel::() const |
327 | { |
328 | return m_model; |
329 | } |
330 | |
331 | void QHeaderDataProxyModel::() |
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 | |
379 | void QHeaderDataProxyModel::() |
380 | { |
381 | if (m_model.isNull()) |
382 | return; |
383 | m_model->disconnect(receiver: this); |
384 | } |
385 | |
386 | QQuickHorizontalHeaderView::(QQuickItem *parent) |
387 | : QQuickHeaderViewBase(Qt::Horizontal, parent) |
388 | { |
389 | setFlickableDirection(FlickableDirection::HorizontalFlick); |
390 | setResizableColumns(true); |
391 | } |
392 | |
393 | QQuickHorizontalHeaderView::() |
394 | { |
395 | } |
396 | |
397 | QQuickVerticalHeaderView::(QQuickItem *parent) |
398 | : QQuickHeaderViewBase(Qt::Vertical, parent) |
399 | { |
400 | setFlickableDirection(FlickableDirection::VerticalFlick); |
401 | setResizableRows(true); |
402 | } |
403 | |
404 | QQuickVerticalHeaderView::() |
405 | { |
406 | } |
407 | |
408 | QQuickHorizontalHeaderViewPrivate::() = default; |
409 | |
410 | QQuickHorizontalHeaderViewPrivate::() = default; |
411 | |
412 | QQuickVerticalHeaderViewPrivate::() = default; |
413 | |
414 | QQuickVerticalHeaderViewPrivate::() = default; |
415 | |
416 | QT_END_NAMESPACE |
417 | |
418 | #include "moc_qquickheaderview_p_p.cpp" |
419 | |
420 | #include "moc_qquickheaderview_p.cpp" |
421 | |