Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 207 additions & 21 deletions panels/dock/taskmanager/rolecombinemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,133 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): No handling for mappings that become invalid after minor column removal.

Mappings referencing removed columns are not updated, which may leave invalid entries. Please ensure such mappings are removed or invalidated.


connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) {
beginInsertRows(index(parent.row(), parent.column()), first, last);
// 对于QAbstractListModel,parent通常是无效的,我们直接使用QModelIndex()
beginInsertRows(QModelIndex(), first, last);

// 先调整现有映射中后续行的索引
QMap<QPair<int, int>, QPair<int, int>> newIndexMap;
for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) {
int row = it.key().first;
int col = it.key().second;

if (row >= first) {
// 将后续行的索引向后移动
int newRow = row + (last - first + 1);
newIndexMap[qMakePair(newRow, col)] = it.value();
} else {
// 保持前面行的映射不变
newIndexMap[it.key()] = it.value();
}
}
m_indexMap = newIndexMap;

// 为新插入的行创建映射
int columnCount = sourceModel()->columnCount();
for (int i = first; i <= last; i++) {
QModelIndex majorIndex = sourceModel()->index(i, 0);
QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor);
if (majorIndex.isValid() && minorIndex.isValid())
m_indexMap[qMakePair(i, 0)] = qMakePair(minorIndex.row(), minorIndex.column());
for (int j = 0; j < columnCount; j++) {
QModelIndex majorIndex = sourceModel()->index(i, j);
QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor);
if (majorIndex.isValid() && minorIndex.isValid())
m_indexMap[qMakePair(i, j)] = qMakePair(minorIndex.row(), minorIndex.column());
}
}
endInsertRows();
});

connect(sourceModel(), &QAbstractItemModel::columnsInserted, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) {
beginInsertColumns(index(parent.row(), parent.column()), first, last);
beginInsertColumns(QModelIndex(), first, last);

// 先调整现有映射中后续列的索引
QMap<QPair<int, int>, QPair<int, int>> newIndexMap;
for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) {
int row = it.key().first;
int col = it.key().second;

if (col >= first) {
// 将后续列的索引向后移动
int newCol = col + (last - first + 1);
newIndexMap[qMakePair(row, newCol)] = it.value();
} else {
// 保持前面列的映射不变
newIndexMap[it.key()] = it.value();
}
}
m_indexMap = newIndexMap;

// 为新插入的列创建映射
int rowCount = sourceModel()->rowCount();
for (int j = first; j <= last; j++) {
QModelIndex majorIndex = sourceModel()->index(0, j);
QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor);
if (majorIndex.isValid() && minorIndex.isValid())
m_indexMap[qMakePair(0, j)] = qMakePair(minorIndex.row(), minorIndex.column());
for (int i = 0; i < rowCount; i++) {
QModelIndex majorIndex = sourceModel()->index(i, j);
QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor);
if (majorIndex.isValid() && minorIndex.isValid())
m_indexMap[qMakePair(i, j)] = qMakePair(minorIndex.row(), minorIndex.column());
}
}
endInsertColumns();
});

connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
beginRemoveRows(index(parent.row(), parent.column()), first, last);
beginRemoveRows(QModelIndex(), first, last);

// 删除被移除行的映射
int columnCount = sourceModel()->columnCount();
for (int i = first; i <= last; i++) {
if (m_indexMap.contains(qMakePair(i, 0))) {
m_indexMap.remove(qMakePair(i, 0));
for (int j = 0; j < columnCount; j++) {
m_indexMap.remove(qMakePair(i, j));
}
}

// 调整后续行的索引映射
QMap<QPair<int, int>, QPair<int, int>> newIndexMap;
for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) {
int row = it.key().first;
int col = it.key().second;

if (row > last) {
// 将后续行的索引向前移动
int newRow = row - (last - first + 1);
newIndexMap[qMakePair(newRow, col)] = it.value();
} else if (row < first) {
// 保持前面行的映射不变
newIndexMap[it.key()] = it.value();
}
// 被删除行的映射已经在上面移除了
}
m_indexMap = newIndexMap;

endRemoveRows();
});

connect(sourceModel(), &QAbstractItemModel::columnsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
beginRemoveColumns(index(parent.row(), parent.column()), first, last);
beginRemoveColumns(QModelIndex(), first, last);

// 删除被移除列的映射
int rowCount = sourceModel()->rowCount();
for (int j = first; j <= last; j++) {
if (m_indexMap.contains(qMakePair(0, j))) {
m_indexMap.remove(qMakePair(0, j));
for (int i = 0; i < rowCount; i++) {
m_indexMap.remove(qMakePair(i, j));
}
}

// 调整后续列的索引映射
QMap<QPair<int, int>, QPair<int, int>> newIndexMap;
for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) {
int row = it.key().first;
int col = it.key().second;

if (col > last) {
// 将后续列的索引向前移动
int newCol = col - (last - first + 1);
newIndexMap[qMakePair(row, newCol)] = it.value();
} else if (col < first) {
// 保持前面列的映射不变
newIndexMap[it.key()] = it.value();
}
// 被删除列的映射已经在上面移除了
}
m_indexMap = newIndexMap;

endRemoveColumns();
});

Expand Down Expand Up @@ -104,13 +195,73 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel
}
});

// 添加对minor模型删除操作的处理
connect(m_minor, &QAbstractItemModel::rowsRemoved, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) {
// 当minor模型删除行时,需要更新映射并可能触发数据变化信号
QList<QModelIndex> affectedMajorIndexes;

// 找到受影响的major索引
for (auto it = m_indexMap.begin(); it != m_indexMap.end();) {
int minorRow = it.value().first;
int minorCol = it.value().second;

if (minorRow >= first && minorRow <= last) {
// 这个映射指向的minor行被删除了,需要重新建立映射
int majorRow = it.key().first;
int majorCol = it.key().second;
auto majorIndex = sourceModel()->index(majorRow, majorCol);

if (majorIndex.isValid()) {
affectedMajorIndexes.append(majorIndex);
// 尝试重新建立映射
QModelIndex newMinorIndex = func(majorIndex.data(majorRoles), m_minor);
if (newMinorIndex.isValid()) {
it.value() = qMakePair(newMinorIndex.row(), newMinorIndex.column());
++it;
} else {
// 无法建立新映射,删除这个映射
it = m_indexMap.erase(it);
}
} else {
it = m_indexMap.erase(it);
}
} else if (minorRow > last) {
// 调整后续行的索引
int newMinorRow = minorRow - (last - first + 1);
it.value().first = newMinorRow;
++it;
} else {
++it;
}
}

// 对受影响的major索引发送数据变化信号
for (const auto &majorIndex : affectedMajorIndexes) {
Q_EMIT dataChanged(majorIndex, majorIndex, m_minorRolesMap.values());
}
});

connect(m_minor, &QAbstractItemModel::columnsRemoved, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) {
// 当minor模型删除列时,需要更新映射
for (auto it = m_indexMap.begin(); it != m_indexMap.end(); ++it) {
int minorRow = it.value().first;
int minorCol = it.value().second;

if (minorCol > last) {
// 调整后续列的索引
int newMinorCol = minorCol - (last - first + 1);
it.value().second = newMinorCol;
}
}
});

connect(m_minor, &QAbstractItemModel::rowsInserted, this,
[this, majorRoles, func](const QModelIndex &parent, int first, int last){
auto rowCount = sourceModel()->rowCount();
auto columnCount = sourceModel()->columnCount();
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
// alreay bind, pass this
// already bind, pass this
if (m_indexMap.contains(qMakePair(i ,j)))
continue;

Expand All @@ -127,8 +278,25 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel
// create minor role map
auto minorRolenames = m_minor->roleNames();
m_roleNames = createRoleNames();
std::for_each(minorRolenames.constBegin(), minorRolenames.constEnd(), [&minorRolenames, this](auto &roleName) {
m_minorRolesMap.insert(m_roleNames.key(roleName), minorRolenames.key(roleName));

// 修复角色映射逻辑:应该映射到新创建的minor角色,而不是major角色
auto majorRoleNames = sourceModel()->roleNames();

std::for_each(minorRolenames.constBegin(), minorRolenames.constEnd(), [&minorRolenames, &majorRoleNames, this](auto &roleName) {
int minorRoleKey = minorRolenames.key(roleName);

// 在组合角色中找到对应的key,但排除major模型已有的key
int combinedRoleKey = -1;
for (auto it = m_roleNames.constBegin(); it != m_roleNames.constEnd(); ++it) {
if (it.value() == roleName && !majorRoleNames.contains(it.key())) {
combinedRoleKey = it.key();
break;
}
Comment on lines +285 to +294

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Role mapping logic may not handle duplicate role names across major and minor models.

If major and minor models share a role name but have different keys, the logic skips mapping for keys already in the major model, potentially omitting roles from the combined model. Please confirm if this is intentional.

}

if (combinedRoleKey != -1) {
m_minorRolesMap.insert(combinedRoleKey, minorRoleKey);
}
});
}

Expand All @@ -152,19 +320,31 @@ QHash<int, QByteArray> RoleCombineModel::roleNames() const

int RoleCombineModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0; // 平坦列表模型:有效的parent表示某个项目,项目没有子项
return sourceModel()->rowCount();
}

int RoleCombineModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0; // 平坦列表模型:有效的parent表示某个项目,项目没有子项
return sourceModel()->columnCount();
}

QVariant RoleCombineModel::data(const QModelIndex &index, int role) const
{
if (m_minorRolesMap.contains(role)) {
int row, column;
std::tie(row, column) = m_indexMap.value(qMakePair(index.row(), index.column()), qMakePair(-1, -1));
auto majorKey = qMakePair(index.row(), index.column());
auto mapping = m_indexMap.value(majorKey, qMakePair(-1, -1));
int row = mapping.first;
int column = mapping.second;

// 检查映射是否有效
if (row == -1 || column == -1) {
return QVariant(); // 返回空值而不是用无效索引访问
}

return m_minor->data(m_minor->index(row, column), m_minorRolesMap[role]);
} else {
return sourceModel()->data(sourceModel()->index(index.row(), index.column()), role);
Expand All @@ -176,6 +356,12 @@ bool RoleCombineModel::hasIndex(int row, int column, const QModelIndex &parent)
return sourceModel()->hasIndex(row, column, parent);
}

bool RoleCombineModel::hasChildren(const QModelIndex &parent) const
{
// 平坦列表模型:只有根节点有子项,其他项目都没有子项
return !parent.isValid() && rowCount(parent) > 0;
}

QModelIndex RoleCombineModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
Expand Down
1 change: 1 addition & 0 deletions panels/dock/taskmanager/rolecombinemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;

Check warning on line 24 in panels/dock/taskmanager/rolecombinemodel.h

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'roleNames' shadows outer function

bool hasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE virtual QModelIndex parent(const QModelIndex &child) const override;

Expand Down
Loading