Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9b4fe73

Browse files
committedMay 15, 2025·
feat: introduce RoleGroupModel
Log:
1 parent 2df60b4 commit 9b4fe73

File tree

5 files changed

+397
-0
lines changed

5 files changed

+397
-0
lines changed
 

‎panels/dock/taskmanager/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ add_library(dock-taskmanager SHARED ${DBUS_INTERFACES}
5757
appitem.h
5858
rolecombinemodel.cpp
5959
rolecombinemodel.h
60+
rolegroupmodel.cpp
61+
rolegroupmodel.h
6062
itemmodel.cpp
6163
itemmodel.h
6264
desktopfileabstractparser.cpp
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#include "rolegroupmodel.h"
6+
7+
#include <QList>
8+
9+
RoleGroupModel::RoleGroupModel(QAbstractItemModel *sourceModel, int role, QObject *parent)
10+
: QAbstractProxyModel(parent)
11+
, m_roleForDeduplication(role)
12+
{
13+
RoleGroupModel::setSourceModel(sourceModel);
14+
}
15+
16+
void RoleGroupModel::setDeduplicationRole(const int &role)
17+
{
18+
if (role != m_roleForDeduplication) {
19+
m_roleForDeduplication = role;
20+
rebuildTreeSource();
21+
}
22+
}
23+
24+
int RoleGroupModel::deduplicationRole() const
25+
{
26+
return m_roleForDeduplication;
27+
}
28+
29+
void RoleGroupModel::setSourceModel(QAbstractItemModel *model)
30+
{
31+
if (sourceModel()) {
32+
sourceModel()->disconnect(this);
33+
}
34+
35+
QAbstractProxyModel::setSourceModel(model);
36+
37+
rebuildTreeSource();
38+
if (sourceModel() == nullptr)
39+
return;
40+
41+
connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
42+
adjustMap(first, (last - first) + 1);
43+
44+
for (int i = first; i <= last; i++) {
45+
auto sourceIndex = sourceModel()->index(i, 0);
46+
auto data = sourceIndex.data(m_roleForDeduplication).toString();
47+
if (data.isEmpty()) {
48+
continue;
49+
}
50+
51+
auto list = m_map.value(data, nullptr);
52+
if (nullptr == list) {
53+
beginInsertRows(QModelIndex(), m_rowMap.size(), m_rowMap.size());
54+
list = new QList<int>();
55+
m_map.insert(data, list);
56+
m_rowMap.append(list);
57+
endInsertRows();
58+
}
59+
beginInsertRows(index(m_rowMap.indexOf(list), 0), list->size(), list->size());
60+
list->append(i);
61+
endInsertRows();
62+
}
63+
});
64+
65+
connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
66+
for (int i = 0; i < m_rowMap.count(); ++i) {
67+
auto sourceRows = m_rowMap.value(i);
68+
for (int j = 0; j < sourceRows->size(); ++j) {
69+
if (first <= sourceRows->value(j) && last >= sourceRows->value(j)) {
70+
beginRemoveRows(index(m_rowMap.indexOf(sourceRows), 0), j, j);
71+
sourceRows->removeAt(j);
72+
endRemoveRows();
73+
--j;
74+
}
75+
}
76+
if (sourceRows->size() == 0) {
77+
beginRemoveRows(QModelIndex(), m_rowMap.indexOf(sourceRows), m_rowMap.indexOf(sourceRows));
78+
m_map.remove(m_map.key(sourceRows));
79+
m_rowMap.removeOne(sourceRows);
80+
delete sourceRows;
81+
endRemoveRows();
82+
}
83+
}
84+
adjustMap(first, -((last - first) + 1));
85+
});
86+
87+
connect(sourceModel(), &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) {
88+
// TODO: if roles contains m_roleForDeduplication, need update from topLeft 2 bottomRight or just send dataChanged
89+
if (roles.contains(m_roleForDeduplication)) {
90+
rebuildTreeSource();
91+
return;
92+
}
93+
94+
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
95+
auto data = sourceModel()->index(i, 0).data(m_roleForDeduplication).toString();
96+
auto list = m_map.value(data, nullptr);
97+
if (list == nullptr)
98+
continue;
99+
100+
auto index = createIndex(list->indexOf(i), 0, m_rowMap.indexOf(list));
101+
Q_EMIT dataChanged(index, index, roles);
102+
}
103+
});
104+
105+
connect(sourceModel(), &QAbstractItemModel::modelReset, this, [this]() {
106+
rebuildTreeSource();
107+
});
108+
}
109+
110+
int RoleGroupModel::rowCount(const QModelIndex &parent) const
111+
{
112+
if (parent.isValid()) {
113+
auto list = m_rowMap.value(parent.row(), nullptr);
114+
return nullptr == list ? 0 : list->size();
115+
}
116+
117+
return m_rowMap.size();
118+
}
119+
120+
int RoleGroupModel::columnCount(const QModelIndex &parent) const
121+
{
122+
if (sourceModel()) {
123+
return sourceModel()->columnCount(parent);
124+
}
125+
return 0;
126+
}
127+
128+
QHash<int, QByteArray> RoleGroupModel::roleNames() const
129+
{
130+
if (sourceModel()) {
131+
return sourceModel()->roleNames();
132+
}
133+
return QAbstractProxyModel::roleNames();
134+
}
135+
136+
QVariant RoleGroupModel::data(const QModelIndex &index, int role) const
137+
{
138+
auto parentPos = static_cast<int>(index.internalId());
139+
auto list = m_rowMap.value(index.row(), nullptr);
140+
if (parentPos == -1) {
141+
if (nullptr == list || list->size() == 0) {
142+
return QVariant();
143+
}
144+
return sourceModel()->index(list->first(), 0).data(role);
145+
}
146+
147+
list = m_rowMap.value(parentPos);
148+
if (list == nullptr) {
149+
return QVariant();
150+
}
151+
152+
return sourceModel()->index(list->value(index.row()), 0).data(role);
153+
}
154+
155+
QModelIndex RoleGroupModel::index(int row, int column, const QModelIndex &parent) const
156+
{
157+
auto list = m_map.value(parent.data(m_roleForDeduplication).toString(), nullptr);
158+
if (parent.isValid() && nullptr != list) {
159+
return createIndex(row, column, m_rowMap.indexOf(list));
160+
}
161+
162+
return createIndex(row, column, -1);
163+
}
164+
165+
QModelIndex RoleGroupModel::parent(const QModelIndex &child) const
166+
{
167+
auto pos = static_cast<int>(child.internalId());
168+
if (pos == -1)
169+
return QModelIndex();
170+
171+
return createIndex(pos, 0);
172+
}
173+
174+
QModelIndex RoleGroupModel::mapToSource(const QModelIndex &proxyIndex) const
175+
{
176+
auto parentIndex = proxyIndex.parent();
177+
QList<int> *list = nullptr;
178+
179+
if (parentIndex.isValid()) {
180+
list = m_rowMap.value(parentIndex.row());
181+
} else {
182+
list = m_map.value(proxyIndex.data(m_roleForDeduplication).toString());
183+
}
184+
185+
if (nullptr == list)
186+
return QModelIndex();
187+
188+
if (parentIndex.isValid()) {
189+
return sourceModel()->index(list->value(proxyIndex.row()), 0);
190+
}
191+
192+
return sourceModel()->index(list->value(0), 0);
193+
}
194+
195+
QModelIndex RoleGroupModel::mapFromSource(const QModelIndex &sourceIndex) const
196+
{
197+
auto data = sourceIndex.data(m_roleForDeduplication).toString();
198+
if (data.isEmpty()) {
199+
return QModelIndex();
200+
}
201+
202+
auto list = m_map.value(data, nullptr);
203+
if (nullptr == list || 0 == list->size()) {
204+
return {};
205+
}
206+
207+
if (sourceIndex.row() == list->first()) {
208+
return createIndex(m_rowMap.indexOf(list), 0, -1);
209+
}
210+
211+
auto pos = list->indexOf(sourceIndex.row());
212+
return createIndex(pos, 0, m_rowMap.indexOf(list));
213+
}
214+
215+
void RoleGroupModel::rebuildTreeSource()
216+
{
217+
beginResetModel();
218+
qDeleteAll(m_rowMap);
219+
m_map.clear();
220+
m_rowMap.clear();
221+
222+
if (sourceModel() == nullptr) {
223+
endResetModel();
224+
return;
225+
}
226+
227+
for (int i = 0; i < sourceModel()->rowCount(); i++) {
228+
auto index = sourceModel()->index(i, 0);
229+
auto data = index.data(m_roleForDeduplication).toString();
230+
if (data.isEmpty()) {
231+
continue;
232+
}
233+
234+
auto list = m_map.value(data, nullptr);
235+
if (nullptr == list) {
236+
list = new QList<int>();
237+
m_map.insert(data, list);
238+
m_rowMap.append(list);
239+
}
240+
list->append(i);
241+
}
242+
endResetModel();
243+
}
244+
245+
void RoleGroupModel::adjustMap(int base, int offset)
246+
{
247+
for (int i = 0; i < m_rowMap.count(); ++i) {
248+
auto sourceRows = m_rowMap.value(i);
249+
for (int j = 0; j < sourceRows->size(); ++j) {
250+
if (sourceRows->value(j) < base)
251+
continue;
252+
(*sourceRows)[j] += offset;
253+
}
254+
}
255+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include <QAbstractProxyModel>
8+
9+
class RoleGroupModel : public QAbstractProxyModel
10+
{
11+
Q_OBJECT
12+
13+
public:
14+
explicit RoleGroupModel(QAbstractItemModel *sourceModel, int role, QObject *parent = nullptr);
15+
void setSourceModel(QAbstractItemModel *sourceModel) override;
16+
17+
void setDeduplicationRole(const int &role);
18+
int deduplicationRole() const;
19+
20+
QHash<int, QByteArray> roleNames() const override;
21+
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
22+
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
23+
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
24+
25+
bool hasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const;
26+
Q_INVOKABLE virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
27+
Q_INVOKABLE virtual QModelIndex parent(const QModelIndex &child) const override;
28+
29+
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
30+
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
31+
32+
Q_SIGNALS:
33+
void deduplicationRoleChanged(int role);
34+
35+
private:
36+
void rebuildTreeSource();
37+
void adjustMap(int base, int offset);
38+
39+
private:
40+
int m_roleForDeduplication;
41+
42+
// for order
43+
QList<QList<int> *> m_rowMap;
44+
45+
// data 2 source row
46+
QHash<QString, QList<int> *> m_map;
47+
};

‎tests/panels/dock/taskmanager/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,21 @@ target_include_directories(rolecombinemodel_tests PRIVATE
2525
)
2626

2727
add_test(NAME rolecombinemodel COMMAND rolecombinemodel_tests)
28+
29+
add_executable(rolegroupmodel_tests
30+
rolegroupmodeltests.cpp
31+
)
32+
33+
target_link_libraries(rolegroupmodel_tests
34+
GTest::GTest
35+
GTest::Main
36+
Qt${QT_VERSION_MAJOR}::Core
37+
Qt${QT_VERSION_MAJOR}::Test
38+
dock-taskmanager
39+
)
40+
target_include_directories(rolegroupmodel_tests PRIVATE
41+
${CMAKE_SOURCE_DIR}/panels/dock/taskmanager/
42+
)
43+
44+
add_test(NAME rolegroupmodel COMMAND rolegroupmodel_tests)
45+
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.