Skip to content

Commit 61fb89f

Browse files
wjyrichdeepin-bot[bot]
authored andcommitted
feat: Added search priority display.
as title. pms:task-377379
1 parent c26b96e commit 61fb89f

2 files changed

Lines changed: 187 additions & 73 deletions

File tree

src/models/searchfilterproxymodel.cpp

Lines changed: 183 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
#include <QDebug>
99
#include <DPinyin>
10+
#include <QMap>
11+
#include <functional>
12+
#include <algorithm>
1013
DCORE_USE_NAMESPACE
1114

1215
SearchFilterProxyModel::SearchFilterProxyModel(QObject *parent)
@@ -23,6 +26,43 @@ bool SearchFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
2326
QModelIndex modelIndex = this->sourceModel()->index(sourceRow, 0, sourceParent);
2427
const QRegularExpression searchPattern = this->filterRegularExpression();
2528

29+
// 计算匹配索引
30+
int matchIndex = calculateWeight(modelIndex);
31+
32+
// 如果索引为0,表示不匹配
33+
return matchIndex >= 0;
34+
}
35+
36+
bool SearchFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
37+
{
38+
int leftIndex = calculateWeight(source_left);
39+
int rightIndex = calculateWeight(source_right);
40+
41+
if (leftIndex != rightIndex) {
42+
// 索引值越小优先级越高,在降序排序中应该排在前面
43+
return leftIndex > rightIndex; // 索引小的返回false(排在前面)
44+
}
45+
46+
// 索引相同时,按启动次数排序:高频使用 > 低频使用
47+
int leftLaunchedTimes = source_left.data(AppItem::LaunchedTimesRole).toInt();
48+
int rightLaunchedTimes = source_right.data(AppItem::LaunchedTimesRole).toInt();
49+
50+
if (leftLaunchedTimes != rightLaunchedTimes) {
51+
bool result = leftLaunchedTimes < rightLaunchedTimes;
52+
return result;
53+
}
54+
55+
// 索引和启动次数都相同时,按照原有的排序规则
56+
return QSortFilterProxyModel::lessThan(source_left, source_right);
57+
}
58+
59+
int SearchFilterProxyModel::calculateWeight(const QModelIndex &modelIndex) const
60+
{
61+
const QRegularExpression searchPattern = this->filterRegularExpression();
62+
if (searchPattern.pattern().isEmpty()) {
63+
return 0;
64+
}
65+
2666
const QString & displayName = modelIndex.data(Qt::DisplayRole).toString();
2767
const QString & name = modelIndex.data(AppsModel::NameRole).toString();
2868
const QString & vendor = modelIndex.data(AppItem::VendorRole).toString();
@@ -37,7 +77,7 @@ bool SearchFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
3777
if(vendor == "deepin") {
3878
targetName = genericName;
3979
if(targetName.isEmpty()) {
40-
targetName = name;
80+
targetName = name;
4181
}
4282
}else{
4383
targetName = name;
@@ -55,99 +95,169 @@ bool SearchFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
5595
}
5696
}
5797

58-
QRegularExpression searchNumberCheck("^\\d+$");
59-
bool isNumberSearch = searchNumberCheck.match(searchPatternDelBlank).hasMatch();
60-
61-
QRegularExpression searchEnglishCheck("^[a-zA-Z0-9\\s]+$");
98+
QRegularExpression searchEnglishCheck("^[a-zA-Z0-9\\s\\-\\.]+$");
6299
bool isEnglishSearch = searchEnglishCheck.match(searchPattern.pattern()).hasMatch();
63100

64-
QRegularExpression searchHasLetterCheck("[a-zA-Z]");
65-
bool hasLetter = searchHasLetterCheck.match(searchPattern.pattern()).hasMatch();
66-
67-
if (isNumberSearch || isEnglishSearch) {
68-
bool hasMatch = false;
69-
// Handle number prefix matching eg: 360zip
70-
if (isNumberSearch) {
71-
QRegularExpression numberRegex("\\d+");
72-
QRegularExpressionMatchIterator matches = numberRegex.globalMatch(targetName);
73-
74-
while (matches.hasNext()) {
75-
QRegularExpressionMatch match = matches.next();
76-
QString numberInDisplayName = match.captured(0);
77-
hasMatch = true;
78-
if (numberInDisplayName.startsWith(searchPatternDelBlank)) {
101+
// 计算匹配权重
102+
QString searchPatternLower = searchPatternDelBlank.toLower();
103+
QString displayNameLower = displayName.toLower().remove(" ");
104+
QString targetNameLower = targetName.toLower().remove(" ");
105+
QString transliteratedLower = transliterated.toLower();
106+
QString jianpinLower = jianpin.toLower();
107+
QString nameFirstLettersLower = nameFirstLetters.toLower();
108+
109+
// 使用 QVector 存储匹配类型和对应的函数,按优先级顺序插入
110+
QVector<QPair<QString, std::function<bool()>>> matchTypes;
111+
112+
// 完全匹配
113+
matchTypes.push_back(qMakePair(QString("displayName_exact"), [&]() -> bool {
114+
return (displayNameLower == searchPatternLower);
115+
}));
116+
117+
matchTypes.push_back(qMakePair(QString("targetName_exact"), [&]() -> bool {
118+
return (targetNameLower == searchPatternLower);
119+
}));
120+
121+
// 中文拼音匹配
122+
matchTypes.push_back(qMakePair(QString("transliterated_start"), [&]() -> bool {
123+
return transliteratedLower.startsWith(searchPatternLower);
124+
}));
125+
126+
matchTypes.push_back(qMakePair(QString("jianpin_exact"), [&]() -> bool {
127+
QString jianpinNormalized = QString(jianpinLower).remove(",").remove(" ");
128+
return (jianpinNormalized == searchPatternLower);
129+
}));
130+
131+
matchTypes.push_back(qMakePair(QString("jianpin_start"), [&]() -> bool {
132+
QString jianpinNormalized = QString(jianpinLower).remove(",").remove(" ");
133+
return jianpinNormalized.startsWith(searchPatternLower);
134+
}));
135+
136+
// 检查是否为中文应用(不以英文字母开头)
137+
matchTypes.push_back(qMakePair(QString("displayName_start_chinese"), [&]() -> bool {
138+
if (!displayNameLower.startsWith(searchPatternLower)) return false;
139+
QRegularExpression startsWithEnglishCheck("^[a-zA-Z][a-zA-Z0-9]*");
140+
return !startsWithEnglishCheck.match(displayName).hasMatch();
141+
}));
142+
143+
// 英文应用的 displayName 开头匹配
144+
matchTypes.push_back(qMakePair(QString("displayName_start_english"), [&]() -> bool {
145+
if (!displayNameLower.startsWith(searchPatternLower)) return false;
146+
QRegularExpression startsWithEnglishCheck("^[a-zA-Z][a-zA-Z0-9]*");
147+
return startsWithEnglishCheck.match(displayName).hasMatch();
148+
}));
149+
150+
matchTypes.push_back(qMakePair(QString("displayName_word_start"), [&]() -> bool {
151+
if (displayNameLower.contains(searchPatternLower)) {
152+
QStringList displayWords = displayName.split(" ", Qt::SkipEmptyParts);
153+
for (const QString &word : displayWords) {
154+
if (word.toLower().startsWith(searchPatternLower)) {
79155
return true;
80156
}
81157
}
82158
}
159+
return false;
160+
}));
83161

84-
// Handle English prefix matching
85-
if (isEnglishSearch) {
86-
QString targetNameLower = targetName.toLower().remove(" ");
87-
88-
// Extract all capitalized words from displayName (both at beginning and middle)
89-
//eg: x11Vnc Server -> VNC
90-
QString targetNameUpper;
91-
QRegularExpression capitalizedWordRegex("\\b[A-Z][A-Za-z0-9]*");
92-
QRegularExpressionMatchIterator capitalizedMatches = capitalizedWordRegex.globalMatch(targetName);
93-
94-
while (capitalizedMatches.hasNext()) {
95-
QRegularExpressionMatch match = capitalizedMatches.next();
96-
QString capitalizedWord = match.captured(0).toLower();
97-
if (!targetNameUpper.isEmpty()) {
98-
targetNameUpper += " ";
99-
}
100-
targetNameUpper += capitalizedWord;
101-
}
102-
103-
104-
// Check prefix matching for various name formats
105-
if (
106-
displayName.startsWith(searchPatternDelBlank) ||
107-
targetNameLower.startsWith(searchPatternDelBlank) ||
108-
transliterated.startsWith(searchPatternDelBlank) ||
109-
jianpin.startsWith(searchPatternDelBlank) ||
110-
targetNameUpper.startsWith(searchPatternDelBlank) ||
111-
nameFirstLetters.startsWith(searchPatternDelBlank)) {
112-
return true;
113-
}
162+
matchTypes.push_back(qMakePair(QString("nameFirstLetters_start"), [&]() -> bool {
163+
return nameFirstLettersLower.startsWith(searchPatternLower);
164+
}));
165+
166+
matchTypes.push_back(qMakePair(QString("targetName_start"), [&]() -> bool {
167+
return targetNameLower.startsWith(searchPatternLower);
168+
}));
114169

115-
// Also check if search pattern matches the prefix of any word in targetName
170+
matchTypes.push_back(qMakePair(QString("targetName_word_start"), [&]() -> bool {
171+
if (targetNameLower.contains(searchPatternLower)) {
172+
QStringList words = targetName.split(" ", Qt::SkipEmptyParts);
116173
for (const QString &word : words) {
117-
if (word.toLower().startsWith(searchPatternDelBlank)) {
174+
if (word.toLower().startsWith(searchPatternLower)) {
118175
return true;
119176
}
120177
}
178+
}
179+
return false;
180+
}));
181+
182+
matchTypes.push_back(qMakePair(QString("displayName_middle"), [&]() -> bool {
183+
if (displayNameLower.contains(searchPatternLower)) {
184+
QStringList displayWords = displayName.split(" ", Qt::SkipEmptyParts);
185+
bool isDisplayWordStart = false;
186+
for (const QString &word : displayWords) {
187+
if (word.toLower().startsWith(searchPatternLower)) {
188+
isDisplayWordStart = true;
189+
break;
190+
}
191+
}
192+
if (!isDisplayWordStart) {
193+
return true;
194+
}
195+
}
196+
return false;
197+
}));
121198

122-
// Also check if search pattern matches the prefix of any word in transliterated
199+
matchTypes.push_back(qMakePair(QString("transliterated_word_start"), [&]() -> bool {
200+
if (transliteratedLower.contains(searchPatternLower)) {
123201
QStringList transliteratedWords = transliterated.split(" ", Qt::SkipEmptyParts);
124202
for (const QString &word : transliteratedWords) {
125-
if (word.toLower().startsWith(searchPatternDelBlank)) {
203+
if (word.toLower().startsWith(searchPatternLower)) {
126204
return true;
127205
}
128206
}
207+
}
208+
return false;
209+
}));
129210

130-
// For English searches with letters, if prefix matching fails, fall back to contains matching
131-
if (hasLetter && (displayName.contains(searchPatternDelBlank) ||
132-
targetNameLower.contains(searchPatternDelBlank) ||
133-
transliterated.contains(searchPatternDelBlank) ||
134-
jianpin.contains(searchPatternDelBlank) ||
135-
nameFirstLetters.contains(searchPatternDelBlank))) {
136-
return true;
137-
}
211+
matchTypes.push_back(qMakePair(QString("targetName_middle"), [&]() -> bool {
212+
return (targetNameLower.contains(searchPatternLower));
213+
}));
138214

139-
hasMatch = true;
140-
}
215+
matchTypes.push_back(qMakePair(QString("transliterated_middle"), [&]() -> bool {
216+
return transliteratedLower.contains(searchPatternLower);
217+
}));
218+
219+
matchTypes.push_back(qMakePair(QString("nameFirstLetters_middle"), [&]() -> bool {
220+
return nameFirstLettersLower.contains(searchPatternLower);
221+
}));
222+
223+
matchTypes.push_back(qMakePair(QString("jianpin_middle"), [&]() -> bool {
224+
QString jianpinNormalized = QString(jianpinLower).remove(",").remove(" ");
225+
return jianpinNormalized.contains(searchPatternLower);
226+
}));
141227

142-
// If we had number matches but none were prefix matches, return false
143-
if (hasMatch && isNumberSearch) {
144-
return false;
228+
// 英文搜索特殊情况处理
229+
auto getCapitalizedWords = [&]() -> QString {
230+
QRegularExpression capitalizedWordRegex("\\b[A-Z][A-Za-z0-9]*");
231+
QStringList capitalizedWords;
232+
233+
auto matches = capitalizedWordRegex.globalMatch(targetName);
234+
while (matches.hasNext()) {
235+
capitalizedWords << matches.next().captured(0).toLower();
145236
}
146-
}
147237

148-
return displayName.contains(searchPatternDelBlank) ||
149-
targetName.contains(searchPatternDelBlank) ||
150-
transliterated.contains(searchPatternDelBlank) ||
151-
jianpin.contains(searchPatternDelBlank) ||
152-
nameFirstLetters.contains(searchPatternDelBlank);
238+
return capitalizedWords.join(" ");
239+
};
240+
241+
matchTypes.push_back(qMakePair(QString("capitalized_word_start"), [&]() -> bool {
242+
if (!isEnglishSearch) return false;
243+
return getCapitalizedWords().startsWith(searchPatternLower);
244+
}));
245+
246+
matchTypes.push_back(qMakePair(QString("capitalized_word_middle"), [&]() -> bool {
247+
if (!isEnglishSearch) return false;
248+
return getCapitalizedWords().contains(searchPatternLower);
249+
}));
250+
251+
// 计算匹配索引(索引越小优先级越高)
252+
auto it = std::find_if(matchTypes.begin(), matchTypes.end(),
253+
[](const auto& matchType) { return matchType.second(); });
254+
255+
// 如果没有匹配,返回-1表示不匹配
256+
if (it == matchTypes.end())
257+
return -1;
258+
259+
const int matchIndex = std::distance(matchTypes.begin(), it);
260+
261+
// 返回索引值+1,确保返回值大于0(0表示不匹配)
262+
return matchIndex;
153263
}

src/models/searchfilterproxymodel.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class SearchFilterProxyModel : public QSortFilterProxyModel
2929
// QSortFilterProxyModel interface
3030
protected:
3131
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
32+
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
33+
3234
private:
3335
explicit SearchFilterProxyModel(QObject *parent = nullptr);
36+
37+
int calculateWeight(const QModelIndex &modelIndex) const;
3438
};

0 commit comments

Comments
 (0)