From 3084a4e11e54b71065a408c7d288489bc72ea8a2 Mon Sep 17 00:00:00 2001 From: Alessio Bonfiglio Date: Wed, 11 Mar 2026 20:52:55 +0100 Subject: 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. --- src/kitemviews/private/kfileitemmodelfilter.cpp | 62 +++++++++++++++++++------ src/kitemviews/private/kfileitemmodelfilter.h | 36 ++++++++++++-- 2 files changed, 80 insertions(+), 18 deletions(-) (limited to 'src/kitemviews/private') 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 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,15 +28,35 @@ 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(). -- cgit v1.3