From 0b607bfe14048e32dd81855e1497dda3465b5183 Mon Sep 17 00:00:00 2001 From: Yulong Bai Date: Thu, 11 Apr 2019 17:30:49 +0200 Subject: Add HorizontalHeaderView and VerticalHeaderView [ChangeLog][Controls] Add HorizontalHeaderView and VerticalHeaderView. They are controls associated with TableView. Support flicking synchronization Support default, fusion, imagine, material and universal delegate styles. Fixes: QTPM-1300 Change-Id: Ie3f913dd616cda0d4e5a22a3d95baf71692370fe Reviewed-by: Volker Hilsheimer --- src/quicktemplates2/qquickheaderview.cpp | 373 +++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 src/quicktemplates2/qquickheaderview.cpp (limited to 'src/quicktemplates2/qquickheaderview.cpp') diff --git a/src/quicktemplates2/qquickheaderview.cpp b/src/quicktemplates2/qquickheaderview.cpp new file mode 100644 index 00000000..b6569881 --- /dev/null +++ b/src/quicktemplates2/qquickheaderview.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +QT_BEGIN_NAMESPACE + +QQuickHeaderViewBasePrivate::QQuickHeaderViewBasePrivate() + : QQuickTableViewPrivate() +{ +} + +QQuickHeaderViewBasePrivate::~QQuickHeaderViewBasePrivate() +{ +} + +const QPointer QQuickHeaderViewBasePrivate::delegateItemAt(int row, int col) const +{ + return loadedTableItem(QPoint(col, row))->item; +} + +QVariant QQuickHeaderViewBasePrivate::modelImpl() const +{ + if (auto model = m_headerDataProxyModel.sourceModel()) + return QVariant::fromValue(model.data()); + if (auto model = m_transposeProxyModel.sourceModel()) + return QVariant::fromValue(model); + return QQuickTableViewPrivate::modelImpl(); +} + +template +inline bool proxyModelSetter(QQuickHeaderViewBase *const q, P &proxyModel, M *model) +{ + if (model) { + if (model == proxyModel.sourceModel()) + return true; + proxyModel.setSourceModel(model); + const auto &modelVariant = QVariant::fromValue(std::addressof(proxyModel)); + bool isProxyModelChanged = (modelVariant != QQuickTableViewPrivate::get(q)->QQuickTableViewPrivate::modelImpl()); + QQuickTableViewPrivate::get(q)->QQuickTableViewPrivate::setModelImpl(modelVariant); + //Necessary, since TableView's assigned model not changed, but proxy's source changed + if (!isProxyModelChanged) + emit q->modelChanged(); + return true; + } + proxyModel.setSourceModel(nullptr); + return false; +} + +void QQuickHeaderViewBasePrivate::setModelImpl(const QVariant &newModel) +{ + Q_Q(QQuickHeaderViewBase); + m_modelExplicitlySetByUser = true; + // Case 1: newModel is QAbstractTableModel + if (proxyModelSetter(q, m_headerDataProxyModel, newModel.value())) + return; + // Case 2: newModel is QAbstractItemModel but not QAbstractTableModel + if (orientation() == Qt::Horizontal + && proxyModelSetter(q, m_transposeProxyModel, newModel.value())) + return; + + QQuickTableViewPrivate::setModelImpl(newModel); +} + +void QQuickHeaderViewBasePrivate::syncModel() +{ + Q_Q(QQuickHeaderViewBase); + if (assignedSyncView && !m_modelExplicitlySetByUser) { + auto newModel = assignedSyncView->model(); + if (auto m = newModel.value()) { + proxyModelSetter(q, m_headerDataProxyModel, m); + } else if (orientation() == Qt::Horizontal) { + if (auto m = newModel.value()) + proxyModelSetter(q, m_transposeProxyModel, m); + } else { + QQuickTableViewPrivate::setModelImpl(newModel); + } + } + + QQuickTableViewPrivate::syncModel(); +} + +void QQuickHeaderViewBasePrivate::syncSyncView() +{ + Q_Q(QQuickHeaderViewBase); + if (assignedSyncDirection != orientation()) { + qmlWarning(q_func()) << "Setting syncDirection other than Qt::" + << QVariant::fromValue(orientation()).toString() + << " is invalid."; + assignedSyncDirection = orientation(); + } + if (assignedSyncView) { + QBoolBlocker fixupGuard(inUpdateContentSize, true); + if (orientation() == Qt::Horizontal) { + q->setLeftMargin(assignedSyncView->leftMargin()); + q->setRightMargin(assignedSyncView->rightMargin()); + } else { + q->setTopMargin(assignedSyncView->topMargin()); + q->setBottomMargin(assignedSyncView->bottomMargin()); + } + } + QQuickTableViewPrivate::syncSyncView(); +} + +QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent) + : QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent) +{ + d_func()->setOrientation(orient); + setSyncDirection(orient); +} + +QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent) + : QQuickTableView(dd, parent) +{ +} + +QQuickHeaderViewBase::~QQuickHeaderViewBase() +{ +} + +QString QQuickHeaderViewBase::textRole() const +{ + Q_D(const QQuickHeaderViewBase); + return d->m_textRole; +} + +void QQuickHeaderViewBase::setTextRole(const QString &role) +{ + Q_D(QQuickHeaderViewBase); + if (d->m_textRole == role) + return; + + d->m_textRole = role; + emit textRoleChanged(); +} + +Qt::Orientation QQuickHeaderViewBasePrivate::orientation() const +{ + return m_headerDataProxyModel.orientation(); +} + +void QQuickHeaderViewBasePrivate::setOrientation(Qt::Orientation orientation) +{ + if (QQuickHeaderViewBasePrivate::orientation() == orientation) + return; + m_headerDataProxyModel.setOrientation(orientation); +} + +QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickVerticalHeaderViewPrivate &dd, QQuickItem *parent) + : QQuickHeaderViewBase(dd, parent) +{ +} + +/*! \internal + \class QHeaderDataProxyModel + \brief + QHeaderDataProxyModel is a proxy AbstractItemModel type that maps + source model's headerData() to correspondent data() + */ +QHeaderDataProxyModel::QHeaderDataProxyModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +QHeaderDataProxyModel::~QHeaderDataProxyModel() = default; + +void QHeaderDataProxyModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (m_model == newSourceModel) + return; + beginResetModel(); + disconnectFromModel(); + m_model = newSourceModel; + connectToModel(); + endResetModel(); +} + +QModelIndex QHeaderDataProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex(); +} + +QModelIndex QHeaderDataProxyModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child) + return QModelIndex(); +} + +QModelIndex QHeaderDataProxyModel::sibling(int row, int column, const QModelIndex &idx) const +{ + return index(row, column, idx); +} + +int QHeaderDataProxyModel::rowCount(const QModelIndex &parent) const +{ + return m_model.isNull() ? -1 : (m_orientation == Qt::Horizontal ? 1 : m_model->rowCount(parent)); +} + +int QHeaderDataProxyModel::columnCount(const QModelIndex &parent) const +{ + return m_model.isNull() ? -1 : (m_orientation == Qt::Vertical ? 1 : m_model->columnCount(parent)); +} + +QVariant QHeaderDataProxyModel::data(const QModelIndex &index, int role) const +{ + if (m_model.isNull()) + return QVariant(); + if (!hasIndex(index.row(), index.column())) + return QModelIndex(); + auto section = m_orientation == Qt::Vertical ? index.row() : index.column(); + return m_model->headerData(section, m_orientation, role); +} + +bool QHeaderDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!hasIndex(index.row(), index.column())) + return false; + auto section = m_orientation == Qt::Vertical ? index.row() : index.column(); + auto ret = m_model->setHeaderData(section, m_orientation, value, role); + emit dataChanged(index, index, { role }); + return ret; +} + +bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return false; +} + +QVariant QHeaderDataProxyModel::variantValue() const +{ + return QVariant::fromValue(static_cast(const_cast(this))); +} + +void QHeaderDataProxyModel::setOrientation(Qt::Orientation o) +{ + if (o == m_orientation) + return; + beginResetModel(); + m_orientation = o; + endResetModel(); +} + +Qt::Orientation QHeaderDataProxyModel::orientation() const +{ + return m_orientation; +} + +QPointer QHeaderDataProxyModel::sourceModel() const +{ + return m_model; +} + +void QHeaderDataProxyModel::connectToModel() +{ + if (m_model.isNull()) + return; + connect(m_model, &QAbstractItemModel::headerDataChanged, + [this](Qt::Orientation orient, int first, int last) { + if (orient != orientation()) + return; + if (orient == Qt::Horizontal) { + emit dataChanged(createIndex(0, first), createIndex(0, last)); + } else { + emit dataChanged(createIndex(first, 0), createIndex(last, 0)); + } + }); + connect(m_model, &QAbstractItemModel::modelAboutToBeReset, + this, &QHeaderDataProxyModel::modelAboutToBeReset, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::modelReset, + this, &QHeaderDataProxyModel::modelReset, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved, + this, &QHeaderDataProxyModel::rowsAboutToBeMoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsMoved, + this, &QHeaderDataProxyModel::rowsMoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, + this, &QHeaderDataProxyModel::rowsAboutToBeInserted, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsInserted, + this, &QHeaderDataProxyModel::rowsInserted, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &QHeaderDataProxyModel::rowsAboutToBeRemoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::rowsRemoved, + this, &QHeaderDataProxyModel::rowsRemoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsAboutToBeMoved, + this, &QHeaderDataProxyModel::columnsAboutToBeMoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsMoved, + this, &QHeaderDataProxyModel::columnsMoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted, + this, &QHeaderDataProxyModel::columnsAboutToBeInserted, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsInserted, + this, &QHeaderDataProxyModel::columnsInserted, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved, + this, &QHeaderDataProxyModel::columnsAboutToBeRemoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::columnsRemoved, + this, &QHeaderDataProxyModel::columnsRemoved, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged, + this, &QHeaderDataProxyModel::layoutAboutToBeChanged, Qt::UniqueConnection); + connect(m_model, &QAbstractItemModel::layoutChanged, + this, &QHeaderDataProxyModel::layoutChanged, Qt::UniqueConnection); +} + +void QHeaderDataProxyModel::disconnectFromModel() +{ + if (m_model.isNull()) + return; + m_model->disconnect(this); +} + +QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent) + : QQuickHeaderViewBase(Qt::Horizontal, parent) +{ + setFlickableDirection(FlickableDirection::HorizontalFlick); +} + +QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView() +{ +} + +QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent) + : QQuickHeaderViewBase(Qt::Vertical, parent) +{ + setFlickableDirection(FlickableDirection::VerticalFlick); +} + +QQuickVerticalHeaderView::~QQuickVerticalHeaderView() +{ +} + +QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate() = default; + +QQuickHorizontalHeaderViewPrivate::~QQuickHorizontalHeaderViewPrivate() = default; + +QQuickVerticalHeaderViewPrivate::QQuickVerticalHeaderViewPrivate() = default; + +QQuickVerticalHeaderViewPrivate::~QQuickVerticalHeaderViewPrivate() = default; + +QT_END_NAMESPACE -- cgit v1.2.3