1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
From 55b6d6e68616f2102fe3b7a4b4182ec6e403a3b1 Mon Sep 17 00:00:00 2001
From: Vladislav Kachegov <vladkachegov@gmail.com>
Date: Mon, 24 Feb 2025 15:02:49 +0300
Subject: [PATCH] Implement Dolphin-style natural string sorting
Enabled natural string sorting to match Dolphin's order, improving human-friendly
string comparison. Updated the function comment for clarity and noted potential
refactoring to a framework utility for maintainability.
BUG: 387969
---
part/archivesortfiltermodel.cpp | 42 +++++++++++++++++++++++++++++++++
part/archivesortfiltermodel.h | 4 ++++
2 files changed, 46 insertions(+)
diff --git a/part/archivesortfiltermodel.cpp b/part/archivesortfiltermodel.cpp
index dabac5ce6..b2fa8a489 100644
--- a/part/archivesortfiltermodel.cpp
+++ b/part/archivesortfiltermodel.cpp
@@ -10,11 +10,48 @@
using namespace Kerfuffle;
+namespace
+{
+/**
+ * Performs a natural string comparison.
+ * This function compares strings in a way that is similar to natural human sorting order.
+ * It is adapted from the Dolphin KFileItemModel::stringCompare implementation.
+ *
+ * @note Consider refactoring this logic to a framework-level utility
+ * for improved maintainability and reusability across multiple projects.
+ *
+ * @return Integer less than, equal to, or greater than zero if the first argument is
+ * found, respectively, to be less than, to match, or be greater than the second.
+ */
+int naturalStringCompare(const QString &a, const QString &b, const QCollator &collator)
+{
+ // Split extension, taking into account it can be empty
+ constexpr QString::SectionFlags flags = QString::SectionSkipEmpty | QString::SectionIncludeLeadingSep;
+
+ // Sort by baseName first
+ const QString aBaseName = a.section(QLatin1Char('.'), 0, 0, flags);
+ const QString bBaseName = b.section(QLatin1Char('.'), 0, 0, flags);
+
+ const int res = collator.compare(aBaseName, bBaseName);
+ if (res != 0 || (aBaseName.length() == a.length() && bBaseName.length() == b.length())) {
+ return res;
+ }
+
+ // sliced() has undefined behavior when pos < 0 or pos > size().
+ Q_ASSERT(aBaseName.length() <= a.length() && aBaseName.length() >= 0);
+ Q_ASSERT(bBaseName.length() <= b.length() && bBaseName.length() >= 0);
+
+ // baseNames were equal, sort by extension
+ return collator.compare(a.sliced(aBaseName.length()), b.sliced(bBaseName.length()));
+}
+}
+
ArchiveSortFilterModel::ArchiveSortFilterModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
// always enable recursive fitlering
setRecursiveFilteringEnabled(true);
+ m_collator.setNumericMode(true);
}
ArchiveSortFilterModel::~ArchiveSortFilterModel()
@@ -36,6 +73,11 @@ bool ArchiveSortFilterModel::lessThan(const QModelIndex &leftIndex, const QModel
return false;
} else {
switch (col) {
+ case DisplayName: {
+ const auto leftFullName = left->property(property.constData()).toString();
+ const auto rightFullName = right->property(property.constData()).toString();
+ return naturalStringCompare(leftFullName, rightFullName, m_collator) < 0;
+ }
case Size:
case CompressedSize:
if (left->property(property.constData()).toULongLong() < right->property(property.constData()).toULongLong()) {
diff --git a/part/archivesortfiltermodel.h b/part/archivesortfiltermodel.h
index 3fa392d1b..3cd30b3ff 100644
--- a/part/archivesortfiltermodel.h
+++ b/part/archivesortfiltermodel.h
@@ -6,6 +6,7 @@
#ifndef ARCHIVESORTFILTERMODEL_H
#define ARCHIVESORTFILTERMODEL_H
+#include <QCollator>
#include <QSortFilterProxyModel>
class ArchiveSortFilterModel : public QSortFilterProxyModel
@@ -17,6 +18,9 @@ public:
~ArchiveSortFilterModel() override;
bool lessThan(const QModelIndex &leftIndex, const QModelIndex &rightIndex) const override;
+
+private:
+ QCollator m_collator;
};
#endif // ARCHIVESORTFILTERMODEL_H
--
GitLab
|