-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add a helper model for easier debugging
添加了一个用于将单列 model 的 role 转化为列的代理 model,便于使用 TreeView 可视化单列 model 中各个 role 对应的值。 此代理 model 的第一列仍为原始数据,roles 属性对应原始数据中的 需要映射为单独的列的 role,每个 role 对应一个新的列。 示例用法: TreeView { delegate: TreeViewDelegate { isTreeNode: false } model: DDT.ListToTableProxyModel { sourceModel: DDT.TraySortOrderModel roles: [ DDT.TraySortOrderModel.SectionTypeRole, DDT.TraySortOrderModel.VisibilityRole, DDT.TraySortOrderModel.VisualIndexRole, DDT.TraySortOrderModel.SurfaceIdRole, DDT.TraySortOrderModel.DelegateTypeRole, DDT.TraySortOrderModel.ForbiddenSectionsRole, DDT.TraySortOrderModel.IsForceDockRole ] } Component.onCompleted: { setColumnWidth(0, 0) // 隐藏第一列 } } Log:
- Loading branch information
Showing
5 changed files
with
569 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,349 @@ | ||
/* | ||
SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]> | ||
SPDX-FileContributor: David Faure <[email protected]> | ||
SPDX-License-Identifier: LGPL-2.0-or-later | ||
Source code extracted from KItemModels with minimized changes. | ||
*/ | ||
|
||
#include "kextracolumnsproxymodel.h" | ||
|
||
#include <QItemSelection> | ||
|
||
class KExtraColumnsProxyModelPrivate | ||
{ | ||
Q_DECLARE_PUBLIC(KExtraColumnsProxyModel) | ||
KExtraColumnsProxyModel *const q_ptr; | ||
|
||
public: | ||
KExtraColumnsProxyModelPrivate(KExtraColumnsProxyModel *model) | ||
: q_ptr(model) | ||
{ | ||
} | ||
|
||
void _ec_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); | ||
void _ec_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); | ||
|
||
// Configuration (doesn't change once source model is plugged in) | ||
QVector<QString> m_extraHeaders; | ||
|
||
// for layoutAboutToBeChanged/layoutChanged | ||
QVector<QPersistentModelIndex> layoutChangePersistentIndexes; | ||
QVector<int> layoutChangeProxyColumns; | ||
QModelIndexList proxyIndexes; | ||
}; | ||
|
||
KExtraColumnsProxyModel::KExtraColumnsProxyModel(QObject *parent) | ||
: QIdentityProxyModel(parent) | ||
, d_ptr(new KExtraColumnsProxyModelPrivate(this)) | ||
{ | ||
} | ||
|
||
KExtraColumnsProxyModel::~KExtraColumnsProxyModel() | ||
{ | ||
} | ||
|
||
void KExtraColumnsProxyModel::appendColumn(const QString &header) | ||
{ | ||
Q_D(KExtraColumnsProxyModel); | ||
d->m_extraHeaders.append(header); | ||
} | ||
|
||
void KExtraColumnsProxyModel::setExtraColumnTitle(int idx, QString title) | ||
{ | ||
Q_D(KExtraColumnsProxyModel); | ||
d->m_extraHeaders[idx] = title; | ||
} | ||
|
||
void KExtraColumnsProxyModel::removeExtraColumn(int idx) | ||
{ | ||
Q_D(KExtraColumnsProxyModel); | ||
d->m_extraHeaders.remove(idx); | ||
} | ||
|
||
bool KExtraColumnsProxyModel::setExtraColumnData(const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role) | ||
{ | ||
Q_UNUSED(parent); | ||
Q_UNUSED(row); | ||
Q_UNUSED(extraColumn); | ||
Q_UNUSED(data); | ||
Q_UNUSED(role); | ||
return false; | ||
} | ||
|
||
void KExtraColumnsProxyModel::extraColumnDataChanged(const QModelIndex &parent, int row, int extraColumn, const QVector<int> &roles) | ||
{ | ||
const QModelIndex idx = index(row, proxyColumnForExtraColumn(extraColumn), parent); | ||
Q_EMIT dataChanged(idx, idx, roles); | ||
} | ||
|
||
void KExtraColumnsProxyModel::setSourceModel(QAbstractItemModel *model) | ||
{ | ||
if (sourceModel()) { | ||
disconnect(sourceModel(), | ||
SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_ec_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
disconnect(sourceModel(), | ||
SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_ec_sourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
} | ||
|
||
QIdentityProxyModel::setSourceModel(model); | ||
|
||
if (model) { | ||
// The handling of persistent model indexes assumes mapToSource can be called for any index | ||
// This breaks for the extra column, so we'll have to do it ourselves | ||
disconnect(model, | ||
SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
disconnect(model, | ||
SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
connect(model, | ||
SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_ec_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
connect(model, | ||
SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), | ||
this, | ||
SLOT(_ec_sourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); | ||
} | ||
} | ||
|
||
QModelIndex KExtraColumnsProxyModel::mapToSource(const QModelIndex &proxyIndex) const | ||
{ | ||
if (!proxyIndex.isValid()) { // happens in e.g. rowCount(mapToSource(parent)) | ||
return QModelIndex(); | ||
} | ||
const int column = proxyIndex.column(); | ||
if (column >= sourceModel()->columnCount()) { | ||
qDebug() << "Returning invalid index in mapToSource"; | ||
return QModelIndex(); | ||
} | ||
return QIdentityProxyModel::mapToSource(proxyIndex); | ||
} | ||
|
||
QModelIndex KExtraColumnsProxyModel::buddy(const QModelIndex &proxyIndex) const | ||
{ | ||
const int column = proxyIndex.column(); | ||
if (column >= sourceModel()->columnCount()) { | ||
return proxyIndex; | ||
} | ||
return QIdentityProxyModel::buddy(proxyIndex); | ||
} | ||
|
||
QModelIndex KExtraColumnsProxyModel::sibling(int row, int column, const QModelIndex &idx) const | ||
{ | ||
if (row == idx.row() && column == idx.column()) { | ||
return idx; | ||
} | ||
return index(row, column, parent(idx)); | ||
} | ||
|
||
QItemSelection KExtraColumnsProxyModel::mapSelectionToSource(const QItemSelection &selection) const | ||
{ | ||
QItemSelection sourceSelection; | ||
|
||
if (!sourceModel()) { | ||
return sourceSelection; | ||
} | ||
|
||
// mapToSource will give invalid index for our additional columns, so truncate the selection | ||
// to the columns known by the source model | ||
const int sourceColumnCount = sourceModel()->columnCount(); | ||
QItemSelection::const_iterator it = selection.constBegin(); | ||
const QItemSelection::const_iterator end = selection.constEnd(); | ||
for (; it != end; ++it) { | ||
Q_ASSERT(it->model() == this); | ||
QModelIndex topLeft = it->topLeft(); | ||
Q_ASSERT(topLeft.isValid()); | ||
Q_ASSERT(topLeft.model() == this); | ||
topLeft = topLeft.sibling(topLeft.row(), 0); | ||
QModelIndex bottomRight = it->bottomRight(); | ||
Q_ASSERT(bottomRight.isValid()); | ||
Q_ASSERT(bottomRight.model() == this); | ||
if (bottomRight.column() >= sourceColumnCount) { | ||
bottomRight = bottomRight.sibling(bottomRight.row(), sourceColumnCount - 1); | ||
} | ||
// This can lead to duplicate source indexes, so use merge(). | ||
const QItemSelectionRange range(mapToSource(topLeft), mapToSource(bottomRight)); | ||
QItemSelection newSelection; | ||
newSelection << range; | ||
sourceSelection.merge(newSelection, QItemSelectionModel::Select); | ||
} | ||
|
||
return sourceSelection; | ||
} | ||
|
||
int KExtraColumnsProxyModel::columnCount(const QModelIndex &parent) const | ||
{ | ||
Q_D(const KExtraColumnsProxyModel); | ||
return QIdentityProxyModel::columnCount(parent) + d->m_extraHeaders.count(); | ||
} | ||
|
||
QVariant KExtraColumnsProxyModel::data(const QModelIndex &index, int role) const | ||
{ | ||
Q_D(const KExtraColumnsProxyModel); | ||
const int extraCol = extraColumnForProxyColumn(index.column()); | ||
if (extraCol >= 0 && !d->m_extraHeaders.isEmpty()) { | ||
return extraColumnData(index.parent(), index.row(), extraCol, role); | ||
} | ||
return sourceModel()->data(mapToSource(index), role); | ||
} | ||
|
||
bool KExtraColumnsProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) | ||
{ | ||
Q_D(const KExtraColumnsProxyModel); | ||
const int extraCol = extraColumnForProxyColumn(index.column()); | ||
if (extraCol >= 0 && !d->m_extraHeaders.isEmpty()) { | ||
return setExtraColumnData(index.parent(), index.row(), extraCol, value, role); | ||
} | ||
return sourceModel()->setData(mapToSource(index), value, role); | ||
} | ||
|
||
Qt::ItemFlags KExtraColumnsProxyModel::flags(const QModelIndex &index) const | ||
{ | ||
const int extraCol = extraColumnForProxyColumn(index.column()); | ||
if (extraCol >= 0) { | ||
// extra columns are readonly | ||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled; | ||
} | ||
return sourceModel() != nullptr ? sourceModel()->flags(mapToSource(index)) : Qt::NoItemFlags; | ||
} | ||
|
||
bool KExtraColumnsProxyModel::hasChildren(const QModelIndex &index) const | ||
{ | ||
if (index.column() > 0) { | ||
return false; | ||
} | ||
return QIdentityProxyModel::hasChildren(index); | ||
} | ||
|
||
QVariant KExtraColumnsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
{ | ||
Q_D(const KExtraColumnsProxyModel); | ||
if (orientation == Qt::Horizontal) { | ||
const int extraCol = extraColumnForProxyColumn(section); | ||
if (extraCol >= 0) { | ||
// Only text is supported, in headers for extra columns | ||
if (role == Qt::DisplayRole) { | ||
return d->m_extraHeaders.at(extraCol); | ||
} | ||
return QVariant(); | ||
} | ||
} | ||
return QIdentityProxyModel::headerData(section, orientation, role); | ||
} | ||
|
||
QModelIndex KExtraColumnsProxyModel::index(int row, int column, const QModelIndex &parent) const | ||
{ | ||
const int extraCol = extraColumnForProxyColumn(column); | ||
if (extraCol >= 0) { | ||
// We store the internal pointer of the index for column 0 in the proxy index for extra columns. | ||
// This will be useful in the parent method. | ||
return createIndex(row, column, QIdentityProxyModel::index(row, 0, parent).internalPointer()); | ||
} | ||
return QIdentityProxyModel::index(row, column, parent); | ||
} | ||
|
||
QModelIndex KExtraColumnsProxyModel::parent(const QModelIndex &child) const | ||
{ | ||
const int extraCol = extraColumnForProxyColumn(child.column()); | ||
if (extraCol >= 0) { | ||
// Create an index for column 0 and use that to get the parent. | ||
const QModelIndex proxySibling = createIndex(child.row(), 0, child.internalPointer()); | ||
return QIdentityProxyModel::parent(proxySibling); | ||
} | ||
return QIdentityProxyModel::parent(child); | ||
} | ||
|
||
int KExtraColumnsProxyModel::extraColumnForProxyColumn(int proxyColumn) const | ||
{ | ||
if (sourceModel() != nullptr) { | ||
const int sourceColumnCount = sourceModel()->columnCount(); | ||
if (proxyColumn >= sourceColumnCount) { | ||
return proxyColumn - sourceColumnCount; | ||
} | ||
} | ||
return -1; | ||
} | ||
|
||
int KExtraColumnsProxyModel::proxyColumnForExtraColumn(int extraColumn) const | ||
{ | ||
return sourceModel()->columnCount() + extraColumn; | ||
} | ||
|
||
void KExtraColumnsProxyModelPrivate::_ec_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, | ||
QAbstractItemModel::LayoutChangeHint hint) | ||
{ | ||
Q_Q(KExtraColumnsProxyModel); | ||
|
||
QList<QPersistentModelIndex> parents; | ||
parents.reserve(sourceParents.size()); | ||
for (const QPersistentModelIndex &parent : sourceParents) { | ||
if (!parent.isValid()) { | ||
parents << QPersistentModelIndex(); | ||
continue; | ||
} | ||
const QModelIndex mappedParent = q->mapFromSource(parent); | ||
Q_ASSERT(mappedParent.isValid()); | ||
parents << mappedParent; | ||
} | ||
|
||
Q_EMIT q->layoutAboutToBeChanged(parents, hint); | ||
|
||
const QModelIndexList persistentIndexList = q->persistentIndexList(); | ||
layoutChangePersistentIndexes.reserve(persistentIndexList.size()); | ||
layoutChangeProxyColumns.reserve(persistentIndexList.size()); | ||
|
||
for (QModelIndex proxyPersistentIndex : persistentIndexList) { | ||
proxyIndexes << proxyPersistentIndex; | ||
Q_ASSERT(proxyPersistentIndex.isValid()); | ||
const int column = proxyPersistentIndex.column(); | ||
layoutChangeProxyColumns << column; | ||
if (column >= q->sourceModel()->columnCount()) { | ||
proxyPersistentIndex = proxyPersistentIndex.sibling(proxyPersistentIndex.row(), 0); | ||
} | ||
const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); | ||
Q_ASSERT(srcPersistentIndex.isValid()); | ||
layoutChangePersistentIndexes << srcPersistentIndex; | ||
} | ||
} | ||
|
||
void KExtraColumnsProxyModelPrivate::_ec_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) | ||
{ | ||
Q_Q(KExtraColumnsProxyModel); | ||
for (int i = 0; i < proxyIndexes.size(); ++i) { | ||
const QModelIndex proxyIdx = proxyIndexes.at(i); | ||
QModelIndex newProxyIdx = q->mapFromSource(layoutChangePersistentIndexes.at(i)); | ||
if (proxyIdx.column() >= q->sourceModel()->columnCount()) { | ||
newProxyIdx = newProxyIdx.sibling(newProxyIdx.row(), layoutChangeProxyColumns.at(i)); | ||
} | ||
q->changePersistentIndex(proxyIdx, newProxyIdx); | ||
} | ||
|
||
layoutChangePersistentIndexes.clear(); | ||
layoutChangeProxyColumns.clear(); | ||
proxyIndexes.clear(); | ||
|
||
QList<QPersistentModelIndex> parents; | ||
parents.reserve(sourceParents.size()); | ||
for (const QPersistentModelIndex &parent : sourceParents) { | ||
if (!parent.isValid()) { | ||
parents << QPersistentModelIndex(); | ||
continue; | ||
} | ||
const QModelIndex mappedParent = q->mapFromSource(parent); | ||
Q_ASSERT(mappedParent.isValid()); | ||
parents << mappedParent; | ||
} | ||
|
||
Q_EMIT q->layoutChanged(parents, hint); | ||
} | ||
|
||
#include "moc_kextracolumnsproxymodel.cpp" |
Oops, something went wrong.