┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/filterbar
diff options
context:
space:
mode:
authorAlessio Bonfiglio <[email protected]>2026-03-11 20:52:55 +0100
committerMéven Car <[email protected]>2026-03-12 10:51:45 +0000
commit3084a4e11e54b71065a408c7d288489bc72ea8a2 (patch)
tree77b44ab7462b05af02822a2ab264d32796bfa7e6 /src/filterbar
parent287ff66e678f4f5a4edea28fc2699d73cefc3233 (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/filterbar')
-rw-r--r--src/filterbar/filterbar.cpp100
-rw-r--r--src/filterbar/filterbar.h22
2 files changed, 118 insertions, 4 deletions
diff --git a/src/filterbar/filterbar.cpp b/src/filterbar/filterbar.cpp
index 6cb8d5e2a..08bff24fc 100644
--- a/src/filterbar/filterbar.cpp
+++ b/src/filterbar/filterbar.cpp
@@ -2,18 +2,25 @@
* SPDX-FileCopyrightText: 2006-2010 Peter Penz <[email protected]>
* SPDX-FileCopyrightText: 2006 Gregor Kališnik <[email protected]>
* SPDX-FileCopyrightText: 2012 Stuart Citrin <[email protected]>
+ * SPDX-FileCopyrightText: 2026 Alessio Bonfiglio <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "filterbar.h"
+#include <KConfigGroup>
#include <KLocalizedString>
+#include <KSharedConfig>
+#include <KColorScheme>
+#include <QAction>
#include <QApplication>
+#include <QComboBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
+#include <QPalette>
#include <QToolButton>
FilterBar::FilterBar(QWidget *parent)
@@ -35,8 +42,35 @@ FilterBar::FilterBar(QWidget *parent)
m_filterInput->setClearButtonEnabled(true);
m_filterInput->setPlaceholderText(i18n("Filter…"));
connect(m_filterInput, &QLineEdit::textChanged, this, &FilterBar::filterChanged);
+ connect(m_filterInput, &QLineEdit::textChanged, this, &FilterBar::updateInvalidPatternView);
setFocusProxy(m_filterInput);
+ m_invalidPatternAction = new QAction(m_filterInput);
+ m_invalidPatternAction->setCheckable(false);
+ m_invalidPatternAction->setIcon(QIcon::fromTheme(QStringLiteral("error-symbolic")));
+ m_invalidPatternAction->setToolTip(i18n("Invalid expression"));
+ m_filterInput->addAction(m_invalidPatternAction, QLineEdit::TrailingPosition);
+ m_invalidPatternAction->setVisible(false);
+
+ // Create case sensitive button
+ m_caseSensitiveButton = new QToolButton(contentsContainer);
+ m_caseSensitiveButton->setAutoRaise(true);
+ m_caseSensitiveButton->setCheckable(true);
+ m_caseSensitiveButton->setIcon(QIcon::fromTheme(QStringLiteral("format-text-superscript"), QIcon::fromTheme(QStringLiteral("format-text-bold"))));
+ m_caseSensitiveButton->setToolTip(i18nc("@info:tooltip", "Match case"));
+ connect(m_caseSensitiveButton, &QToolButton::toggled, this, &FilterBar::caseSensitiveChanged);
+ connect(m_caseSensitiveButton, &QToolButton::toggled, this, &FilterBar::updateInvalidPatternView);
+
+ // Create filter mode combobox
+ m_filterModeComboBox = new QComboBox(contentsContainer);
+ m_filterModeComboBox->addItem(i18nc("@item:inlistbox", "Plain Text"), KFileItemModelFilter::FilterMode::PlainText);
+ m_filterModeComboBox->addItem(i18nc("@item:inlistbox", "Glob Pattern"), KFileItemModelFilter::FilterMode::Glob);
+ m_filterModeComboBox->addItem(i18nc("@item:inlistbox", "Regular Expression"), KFileItemModelFilter::FilterMode::Regex);
+ connect(m_filterModeComboBox, &QComboBox::currentIndexChanged, this, [this](int index) {
+ Q_EMIT filterModeChanged(m_filterModeComboBox->itemData(index).value<KFileItemModelFilter::FilterMode>());
+ });
+ connect(m_filterModeComboBox, &QComboBox::currentIndexChanged, this, &FilterBar::updateInvalidPatternView);
+
// Create close button
QToolButton *closeButton = new QToolButton(contentsContainer);
closeButton->setAutoRaise(true);
@@ -49,13 +83,25 @@ FilterBar::FilterBar(QWidget *parent)
hLayout->setContentsMargins(0, 0, 0, 0);
hLayout->addWidget(m_lockButton);
hLayout->addWidget(m_filterInput);
+ hLayout->addWidget(m_caseSensitiveButton);
+ hLayout->addWidget(m_filterModeComboBox);
hLayout->addWidget(closeButton);
- setTabOrder(m_lockButton, closeButton);
- setTabOrder(closeButton, m_filterInput);
+ setTabOrder({m_lockButton, m_caseSensitiveButton, m_filterModeComboBox, closeButton, m_filterInput});
+
+ KConfigGroup filterBarConfig(KSharedConfig::openStateConfig(), QStringLiteral("FilterBar"));
+ bool caseSensitiveEnabled = filterBarConfig.readEntry("caseSensitive", false);
+ int filterModeComboBoxIndex = filterBarConfig.readEntry("filterMode", m_filterModeComboBox->findData(KFileItemModelFilter::FilterMode::Glob));
+ m_caseSensitiveButton->setChecked(caseSensitiveEnabled);
+ m_filterModeComboBox->setCurrentIndex(filterModeComboBoxIndex);
}
-FilterBar::~FilterBar() = default;
+FilterBar::~FilterBar()
+{
+ KConfigGroup filterBarConfig(KSharedConfig::openStateConfig(), QStringLiteral("FilterBar"));
+ filterBarConfig.writeEntry("caseSensitive", this->m_caseSensitiveButton->isChecked());
+ filterBarConfig.writeEntry("filterMode", this->m_filterModeComboBox->currentIndex());
+}
void FilterBar::closeFilterBar()
{
@@ -71,6 +117,16 @@ void FilterBar::selectAll()
m_filterInput->selectAll();
}
+KFileItemModelFilter::FilterMode FilterBar::filterMode() const
+{
+ return m_filterModeComboBox->itemData(m_filterModeComboBox->currentIndex()).value<KFileItemModelFilter::FilterMode>();
+}
+
+bool FilterBar::isCaseSensitive() const
+{
+ return m_caseSensitiveButton->isChecked();
+}
+
void FilterBar::clear()
{
m_filterInput->clear();
@@ -93,6 +149,39 @@ void FilterBar::slotToggleLockButton(bool checked)
}
}
+void FilterBar::updateInvalidPatternView()
+{
+ bool valid = true;
+
+ KFileItemModelFilter::FilterMode current_filter_mode = filterMode();
+ if (current_filter_mode != KFileItemModelFilter::FilterMode::PlainText) {
+ QRegularExpression regExp = QRegularExpression();
+ QString pattern = m_filterInput->text();
+
+ QRegularExpression::PatternOptions options =
+ m_caseSensitiveButton->isChecked() ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
+ if (current_filter_mode == KFileItemModelFilter::FilterMode::Regex) {
+ regExp.setPattern(pattern);
+ regExp.setPatternOptions(options);
+ } else if (current_filter_mode == KFileItemModelFilter::FilterMode::Glob) {
+ regExp.setPattern(QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion));
+ regExp.setPatternOptions(options);
+ }
+
+ valid = regExp.isValid();
+ }
+
+ if (valid) {
+ m_filterInput->setPalette(QPalette());
+ m_invalidPatternAction->setVisible(false);
+ } else {
+ auto pal = m_filterInput->palette();
+ KColorScheme::adjustBackground(pal, KColorScheme::NegativeBackground);
+ m_filterInput->setPalette(pal);
+ m_invalidPatternAction->setVisible(true);
+ }
+}
+
void FilterBar::showEvent(QShowEvent *event)
{
if (!event->spontaneous()) {
@@ -137,7 +226,10 @@ void FilterBar::keyPressEvent(QKeyEvent *event)
int FilterBar::preferredHeight() const
{
- return std::max(m_filterInput->sizeHint().height(), m_lockButton->sizeHint().height());
+ return std::max({m_filterInput->sizeHint().height(),
+ m_lockButton->sizeHint().height(),
+ m_caseSensitiveButton->sizeHint().height(),
+ m_filterModeComboBox->sizeHint().height()});
}
#include "moc_filterbar.cpp"
diff --git a/src/filterbar/filterbar.h b/src/filterbar/filterbar.h
index 1424f4cb8..924c6dba3 100644
--- a/src/filterbar/filterbar.h
+++ b/src/filterbar/filterbar.h
@@ -10,9 +10,12 @@
#define FILTERBAR_H
#include "animatedheightwidget.h"
+#include "kitemviews/private/kfileitemmodelfilter.h"
class QLineEdit;
class QToolButton;
+class QComboBox;
+class QAction;
/**
* @brief Provides an input field for filtering the currently shown items.
@@ -35,6 +38,9 @@ public:
*/
void selectAll();
+ KFileItemModelFilter::FilterMode filterMode() const;
+ bool isCaseSensitive() const;
+
public Q_SLOTS:
/** Clears the input field. */
void clear();
@@ -51,6 +57,16 @@ Q_SIGNALS:
void filterChanged(const QString &nameFilter);
/**
+ * Emitted when the case sensitive mode has been changed
+ */
+ void caseSensitiveChanged(bool caseSensitive);
+
+ /**
+ * Emitted when the filter mode has been changed
+ */
+ void filterModeChanged(KFileItemModelFilter::FilterMode mode);
+
+ /**
* Emitted as soon as the filterbar should get closed.
*/
void closeRequest();
@@ -70,6 +86,12 @@ protected:
private:
QLineEdit *m_filterInput;
QToolButton *m_lockButton;
+ QToolButton *m_caseSensitiveButton;
+ QComboBox *m_filterModeComboBox;
+ QAction *m_invalidPatternAction;
+
+ /** Enable or disable the alterative view for when a pattern is invalid */
+ void updateInvalidPatternView();
};
#endif