diff options
| author | Alessio Bonfiglio <[email protected]> | 2026-03-11 20:52:55 +0100 |
|---|---|---|
| committer | Méven Car <[email protected]> | 2026-03-12 10:51:45 +0000 |
| commit | 3084a4e11e54b71065a408c7d288489bc72ea8a2 (patch) | |
| tree | 77b44ab7462b05af02822a2ab264d32796bfa7e6 /src/kitemviews | |
| parent | 287ff66e678f4f5a4edea28fc2699d73cefc3233 (diff) | |
filterbar: Add support to match case and glob patterns for the filter bar
Currently, Dolphin's filter bar defaults to plain text, but it actually has a hidden regex functionality too: it tries to auto-detect and switch to a regular expression if characters like '*', '?', or '[' are present in the search string.
This approach has a couple of issues. First, the regex/wildcard functionality is completely hidden from the user. Second, the auto-detection is flawed because those are perfectly valid characters in Linux filenames. If a user tries to filter for a file literally named [draft].txt, the auto-switching kicks in and causes unexpected behavior.
This MR fixes this by making the filtering modes explicit through a ComboBox at the side of the filter bar, with the options 'Plain Text', 'Glob' and 'Regular Expression'. It also adds a button to toggle the case sensitive matching. A visual feedback for when the user is inputting an invalid expression has also been implemented by turning the bar background red and making appear an error symbol.
Diffstat (limited to 'src/kitemviews')
| -rw-r--r-- | src/kitemviews/kfileitemmodel.cpp | 28 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemmodel.h | 6 | ||||
| -rw-r--r-- | src/kitemviews/private/kfileitemmodelfilter.cpp | 62 | ||||
| -rw-r--r-- | src/kitemviews/private/kfileitemmodelfilter.h | 36 |
4 files changed, 114 insertions, 18 deletions
diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index c8bac0b9d..1f291d40c 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -817,6 +817,34 @@ QStringList KFileItemModel::mimeTypeFilters() const return m_filter.mimeTypes(); } +void KFileItemModel::setFilterMode(KFileItemModelFilter::FilterMode mode) +{ + if (m_filter.filterMode() != mode) { + dispatchPendingItemsToInsert(); + m_filter.setFilterMode(mode); + applyFilters(); + } +} + +KFileItemModelFilter::FilterMode KFileItemModel::filterMode() const +{ + return m_filter.filterMode(); +} + +void KFileItemModel::setFilterCaseSensitive(bool caseSensitive) +{ + if (m_filter.isCaseSensitive() != caseSensitive) { + dispatchPendingItemsToInsert(); + m_filter.setCaseSensitive(caseSensitive); + applyFilters(); + } +} + +bool KFileItemModel::isFilterCaseSensitive() const +{ + return m_filter.isCaseSensitive(); +} + void KFileItemModel::setExcludeMimeTypeFilter(const QStringList &filters) { if (m_filter.excludeMimeTypes() != filters) { diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 3749b0c1b..2a6e710c3 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -184,6 +184,12 @@ public: void setMimeTypeFilters(const QStringList &filters); QStringList mimeTypeFilters() const; + void setFilterMode(KFileItemModelFilter::FilterMode mode); + KFileItemModelFilter::FilterMode filterMode() const; + + void setFilterCaseSensitive(bool caseSensitive); + bool isFilterCaseSensitive() const; + void setExcludeMimeTypeFilter(const QStringList &filters); QStringList excludeMimeTypeFilter() const; diff --git a/src/kitemviews/private/kfileitemmodelfilter.cpp b/src/kitemviews/private/kfileitemmodelfilter.cpp index 45c62e7ca..48d2f6276 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.cpp +++ b/src/kitemviews/private/kfileitemmodelfilter.cpp @@ -13,7 +13,8 @@ #include <KFileItem> KFileItemModelFilter::KFileItemModelFilter() - : m_useRegExp(false) + : m_filterMode(Glob) + , m_caseSensitive(false) , m_regExp(nullptr) , m_lowerCasePattern() , m_pattern() @@ -31,16 +32,29 @@ void KFileItemModelFilter::setPattern(const QString &filter) m_pattern = filter; m_lowerCasePattern = filter.toLower(); - if (filter.contains(QLatin1Char('*')) || filter.contains(QLatin1Char('?')) || filter.contains(QLatin1Char('['))) { - if (!m_regExp) { - m_regExp = new QRegularExpression(); - m_regExp->setPatternOptions(QRegularExpression::CaseInsensitiveOption); - } - m_regExp->setPattern(QRegularExpression::wildcardToRegularExpression(filter)); - m_useRegExp = m_regExp->isValid(); - } else { - m_useRegExp = false; - } + updateFilter(); +} + +void KFileItemModelFilter::setFilterMode(FilterMode mode) +{ + m_filterMode = mode; + updateFilter(); +} + +KFileItemModelFilter::FilterMode KFileItemModelFilter::filterMode() const +{ + return m_filterMode; +} + +void KFileItemModelFilter::setCaseSensitive(bool caseSensitive) +{ + m_caseSensitive = caseSensitive; + updateFilter(); +} + +bool KFileItemModelFilter::isCaseSensitive() const +{ + return m_caseSensitive; } QString KFileItemModelFilter::pattern() const @@ -48,6 +62,26 @@ QString KFileItemModelFilter::pattern() const return m_pattern; } +void KFileItemModelFilter::updateFilter() +{ + if (m_filterMode == PlainText) { + return; + } + + if (!m_regExp) { + m_regExp = new QRegularExpression(); + } + + QRegularExpression::PatternOptions options = m_caseSensitive ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption; + if (m_filterMode == Regex) { + m_regExp->setPattern(m_pattern); + m_regExp->setPatternOptions(options); + } else if (m_filterMode == Glob) { + m_regExp->setPattern(QRegularExpression::wildcardToRegularExpression(m_pattern, QRegularExpression::UnanchoredWildcardConversion)); + m_regExp->setPatternOptions(options); + } +} + void KFileItemModelFilter::setMimeTypes(const QStringList &types) { m_mimeTypes = types; @@ -98,8 +132,10 @@ bool KFileItemModelFilter::matches(const KFileItem &item) const bool KFileItemModelFilter::matchesPattern(const KFileItem &item) const { - if (m_useRegExp) { - return m_regExp->match(item.text()).hasMatch(); + if (m_filterMode == Glob || m_filterMode == Regex) { + return m_regExp->isValid() && m_regExp->match(item.text()).hasMatch(); + } else if (m_caseSensitive) { + return item.text().contains(m_pattern); } else { return item.text().toLower().contains(m_lowerCasePattern); } diff --git a/src/kitemviews/private/kfileitemmodelfilter.h b/src/kitemviews/private/kfileitemmodelfilter.h index ce6cbeebb..9d93d42cc 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.h +++ b/src/kitemviews/private/kfileitemmodelfilter.h @@ -28,16 +28,36 @@ public: KFileItemModelFilter(); virtual ~KFileItemModelFilter(); + /** Filtering modes of KFileItemModelFilter */ + enum FilterMode { + /** Substring matching. */ + PlainText = 0, + /** Matching with glob, default. */ + Glob, + /** Matching with regex. */ + Regex + }; + /** * Sets the pattern that is used for a comparison with the item - * in KFileItemModelFilter::matches(). Per default the pattern - * defines a sub-string. As soon as the pattern contains at least - * a '*', '?' or '[' the pattern represents a regular expression. + * in KFileItemModelFilter::matches(). */ void setPattern(const QString &pattern); QString pattern() const; /** + * Sets the filtering mode used in KFileItemModelFilter::matches(). + */ + void setFilterMode(FilterMode mode); + FilterMode filterMode() const; + + /** + * Enable or disable the case sensitive filtering. + */ + void setCaseSensitive(bool caseSensitive); + bool isCaseSensitive() const; + + /** * Set the list of mimetypes that are used for comparison with the * item in KFileItemModelFilter::matchesMimeType. */ @@ -73,8 +93,14 @@ private: */ bool matchesType(const KFileItem &item) const; - bool m_useRegExp; // If true, m_regExp is used for filtering, - // otherwise m_lowerCaseFilter is used. + /** + * Instantiate and configure m_regExp according to m_filterMode and m_caseSensitive. + */ + void updateFilter(); + + FilterMode m_filterMode; // The current filtering mode. + bool m_caseSensitive; // If true the matching will be case sensitive. + QRegularExpression *m_regExp; QString m_lowerCasePattern; // Lowercase version of m_filter for // faster comparison in matches(). |
