77
88#include < QDebug>
99#include < DPinyin>
10+ #include < QMap>
11+ #include < functional>
12+ #include < algorithm>
1013DCORE_USE_NAMESPACE
1114
1215SearchFilterProxyModel::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}
0 commit comments