Changeset View
Changeset View
Standalone View
Standalone View
lib/resultsmodel.cpp
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | |||||
46 | public: | 46 | public: | ||
47 | SortProxyModel(QObject *parent) : QSortFilterProxyModel(parent) | 47 | SortProxyModel(QObject *parent) : QSortFilterProxyModel(parent) | ||
48 | { | 48 | { | ||
49 | setDynamicSortFilter(true); | 49 | setDynamicSortFilter(true); | ||
50 | sort(0, Qt::DescendingOrder); | 50 | sort(0, Qt::DescendingOrder); | ||
51 | } | 51 | } | ||
52 | ~SortProxyModel() override = default; | 52 | ~SortProxyModel() override = default; | ||
53 | 53 | | |||
54 | void setQueryString(const QString &queryString) { | ||||
55 | const QStringList words = queryString.split(QLatin1Char(' '), QString::SkipEmptyParts); | ||||
56 | if (m_words != words) { | ||||
57 | m_words = words; | ||||
58 | invalidate(); | ||||
59 | } | ||||
60 | } | ||||
61 | | ||||
62 | bool categoryHasMatchWithAllWords(const QModelIndex &categoryIdx) const { | ||||
63 | for (int i = 0; i < sourceModel()->rowCount(categoryIdx); ++i) { | ||||
64 | const QModelIndex idx = sourceModel()->index(i, 0, categoryIdx); | ||||
65 | const QString display = idx.data(Qt::DisplayRole).toString(); | ||||
66 | | ||||
67 | bool containsAllWords = true; | ||||
68 | for (const QString &word : m_words) { | ||||
69 | if (!display.contains(word, Qt::CaseInsensitive)) { | ||||
70 | containsAllWords = false; | ||||
71 | } | ||||
72 | } | ||||
73 | | ||||
74 | if (containsAllWords) { | ||||
75 | return true; | ||||
76 | } | ||||
77 | } | ||||
78 | | ||||
79 | return false; | ||||
80 | } | ||||
81 | | ||||
54 | protected: | 82 | protected: | ||
55 | bool lessThan(const QModelIndex &sourceA, const QModelIndex &sourceB) const override | 83 | bool lessThan(const QModelIndex &sourceA, const QModelIndex &sourceB) const override | ||
56 | { | 84 | { | ||
85 | // prefer categories that have a match containing the query string in the display role | ||||
86 | if (!sourceA.parent().isValid() && !sourceB.parent().isValid()) { | ||||
87 | const bool hasMatchWithAllWordsA = categoryHasMatchWithAllWords(sourceA); | ||||
88 | const bool hasMatchWithAllWordsB = categoryHasMatchWithAllWords(sourceB); | ||||
89 | | ||||
90 | if (hasMatchWithAllWordsA != hasMatchWithAllWordsB) { | ||||
91 | return !hasMatchWithAllWordsA && hasMatchWithAllWordsB; | ||||
92 | } | ||||
93 | } | ||||
94 | | ||||
57 | const int typeA = sourceA.data(ResultsModel::TypeRole).toInt(); | 95 | const int typeA = sourceA.data(ResultsModel::TypeRole).toInt(); | ||
58 | const int typeB = sourceB.data(ResultsModel::TypeRole).toInt(); | 96 | const int typeB = sourceB.data(ResultsModel::TypeRole).toInt(); | ||
59 | 97 | | |||
60 | if (typeA != typeB) { | 98 | if (typeA != typeB) { | ||
61 | return typeA < typeB; | 99 | return typeA < typeB; | ||
62 | } | 100 | } | ||
63 | 101 | | |||
64 | const qreal relevanceA = sourceA.data(ResultsModel::RelevanceRole).toReal(); | 102 | const qreal relevanceA = sourceA.data(ResultsModel::RelevanceRole).toReal(); | ||
65 | const qreal relevanceB = sourceB.data(ResultsModel::RelevanceRole).toReal(); | 103 | const qreal relevanceB = sourceB.data(ResultsModel::RelevanceRole).toReal(); | ||
66 | 104 | | |||
67 | if (!qFuzzyCompare(relevanceA, relevanceB)) { | 105 | if (!qFuzzyCompare(relevanceA, relevanceB)) { | ||
68 | return relevanceA < relevanceB; | 106 | return relevanceA < relevanceB; | ||
69 | } | 107 | } | ||
70 | 108 | | |||
71 | return QSortFilterProxyModel::lessThan(sourceA, sourceB); | 109 | return QSortFilterProxyModel::lessThan(sourceA, sourceB); | ||
72 | } | 110 | } | ||
111 | | ||||
112 | private: | ||||
113 | QStringList m_words; | ||||
73 | }; | 114 | }; | ||
74 | 115 | | |||
75 | /** | 116 | /** | ||
76 | * Distributes the number of matches shown per category | 117 | * Distributes the number of matches shown per category | ||
77 | * | 118 | * | ||
78 | * Each category may occupy a maximum of 1/(n+1) of the given @c limit, | 119 | * Each category may occupy a maximum of 1/(n+1) of the given @c limit, | ||
79 | * this means the further down you get, the less matches there are. | 120 | * this means the further down you get, the less matches there are. | ||
80 | * There is at least one match shown per category. | 121 | * There is at least one match shown per category. | ||
▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Line(s) | 322 | ResultsModel::ResultsModel(QObject *parent) | |||
282 | : QSortFilterProxyModel(parent) | 323 | : QSortFilterProxyModel(parent) | ||
283 | , d(new Private(this)) | 324 | , d(new Private(this)) | ||
284 | { | 325 | { | ||
285 | connect(d->resultsModel, &RunnerResultsModel::queryStringChanged, this, &ResultsModel::queryStringChanged); | 326 | connect(d->resultsModel, &RunnerResultsModel::queryStringChanged, this, &ResultsModel::queryStringChanged); | ||
286 | connect(d->resultsModel, &RunnerResultsModel::queryingChanged, this, &ResultsModel::queryingChanged); | 327 | connect(d->resultsModel, &RunnerResultsModel::queryingChanged, this, &ResultsModel::queryingChanged); | ||
287 | connect(d->resultsModel, &RunnerResultsModel::runnerChanged, this, &ResultsModel::runnerChanged); | 328 | connect(d->resultsModel, &RunnerResultsModel::runnerChanged, this, &ResultsModel::runnerChanged); | ||
288 | connect(d->resultsModel, &RunnerResultsModel::queryStringChangeRequested, this, &ResultsModel::queryStringChangeRequested); | 329 | connect(d->resultsModel, &RunnerResultsModel::queryStringChangeRequested, this, &ResultsModel::queryStringChangeRequested); | ||
289 | 330 | | |||
331 | connect(d->resultsModel, &RunnerResultsModel::queryStringChanged, d->sortModel, &SortProxyModel::setQueryString); | ||||
332 | | ||||
290 | connect(d->distributionModel, &CategoryDistributionProxyModel::limitChanged, this, &ResultsModel::limitChanged); | 333 | connect(d->distributionModel, &CategoryDistributionProxyModel::limitChanged, this, &ResultsModel::limitChanged); | ||
291 | 334 | | |||
292 | // The data flows as follows: | 335 | // The data flows as follows: | ||
293 | // - RunnerResultsModel | 336 | // - RunnerResultsModel | ||
294 | // - SortProxyModel | 337 | // - SortProxyModel | ||
295 | // - CategoryDistributionProxyModel | 338 | // - CategoryDistributionProxyModel | ||
296 | // - KDescendantsProxyModel | 339 | // - KDescendantsProxyModel | ||
297 | // - HideRootLevelProxyModel | 340 | // - HideRootLevelProxyModel | ||
▲ Show 20 Lines • Show All 118 Lines • Show Last 20 Lines |