Skip to content

Commit 85f1c77

Browse files
authored
Improve search results filtering implementation
PR qbittorrent#23430. Closes qbittorrent#23396.
1 parent 1be4e64 commit 85f1c77

File tree

4 files changed

+126
-51
lines changed

4 files changed

+126
-51
lines changed

src/gui/search/searchjobwidget.cpp

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ namespace
8585
}
8686
}
8787

88+
using Utils::Misc::SizeUnit;
89+
8890
SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidget *parent)
8991
: GUIApplicationComponent(app, parent)
9092
, m_nameFilteringMode {u"Search/FilteringMode"_s}
@@ -99,6 +101,8 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
99101
header()->setStretchLastSection(false);
100102
header()->setTextElideMode(Qt::ElideRight);
101103

104+
fillFilterComboBoxes();
105+
102106
// Set Search results list model
103107
m_searchListModel = new QStandardItemModel(0, SearchSortModel::NB_SEARCH_COLUMNS, this);
104108
m_searchListModel->setHeaderData(SearchSortModel::NAME, Qt::Horizontal, tr("Name", "i.e: file name"));
@@ -116,6 +120,13 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
116120
m_proxyModel = new SearchSortModel(this);
117121
m_proxyModel->setDynamicSortFilter(true);
118122
m_proxyModel->setSourceModel(m_searchListModel);
123+
m_proxyModel->enableNameFilter(m_nameFilteringMode.get(NameFilteringMode::OnlyNames) == NameFilteringMode::OnlyNames);
124+
m_proxyModel->setSeedsFilter(m_ui->minSeeds->value(), m_ui->maxSeeds->value());
125+
m_proxyModel->setSizeFilter(sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex()))
126+
, sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
127+
128+
updateResultsCount();
129+
119130
m_ui->resultsBrowser->setModel(m_proxyModel);
120131

121132
m_ui->resultsBrowser->hideColumn(SearchSortModel::DL_LINK); // Hide url column
@@ -154,33 +165,24 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
154165
connect(header(), &QHeaderView::sectionMoved, this, &SearchJobWidget::saveSettings);
155166
connect(header(), &QHeaderView::sortIndicatorChanged, this, &SearchJobWidget::saveSettings);
156167

157-
fillFilterComboBoxes();
158-
159168
m_lineEditSearchResultsFilter = new LineEdit(this);
160169
m_lineEditSearchResultsFilter->setPlaceholderText(tr("Filter search results..."));
161170
m_lineEditSearchResultsFilter->setContextMenuPolicy(Qt::CustomContextMenu);
162171
connect(m_lineEditSearchResultsFilter, &QWidget::customContextMenuRequested, this, &SearchJobWidget::showFilterContextMenu);
163172
connect(m_lineEditSearchResultsFilter, &LineEdit::textChanged, this, &SearchJobWidget::filterSearchResults);
164173
m_ui->horizontalLayout->insertWidget(0, m_lineEditSearchResultsFilter);
165174

166-
connect(m_ui->filterMode, qOverload<int>(&QComboBox::currentIndexChanged)
167-
, this, &SearchJobWidget::updateFilter);
168-
connect(m_ui->minSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
169-
connect(m_ui->minSeeds, qOverload<int>(&QSpinBox::valueChanged)
170-
, this, &SearchJobWidget::updateFilter);
171-
connect(m_ui->maxSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
172-
connect(m_ui->maxSeeds, qOverload<int>(&QSpinBox::valueChanged)
173-
, this, &SearchJobWidget::updateFilter);
174-
connect(m_ui->minSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
175-
connect(m_ui->minSize, qOverload<double>(&QDoubleSpinBox::valueChanged)
176-
, this, &SearchJobWidget::updateFilter);
177-
connect(m_ui->maxSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
178-
connect(m_ui->maxSize, qOverload<double>(&QDoubleSpinBox::valueChanged)
179-
, this, &SearchJobWidget::updateFilter);
180-
connect(m_ui->minSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged)
181-
, this, &SearchJobWidget::updateFilter);
182-
connect(m_ui->maxSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged)
183-
, this, &SearchJobWidget::updateFilter);
175+
connect(m_ui->filterMode, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateNameFilter);
176+
connect(m_ui->minSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSeedsFilter);
177+
connect(m_ui->minSeeds, qOverload<int>(&QSpinBox::valueChanged), this, &SearchJobWidget::updateSeedsFilter);
178+
connect(m_ui->maxSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSeedsFilter);
179+
connect(m_ui->maxSeeds, qOverload<int>(&QSpinBox::valueChanged), this, &SearchJobWidget::updateSeedsFilter);
180+
connect(m_ui->minSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSizeFilter);
181+
connect(m_ui->minSize, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SearchJobWidget::updateSizeFilter);
182+
connect(m_ui->maxSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSizeFilter);
183+
connect(m_ui->maxSize, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SearchJobWidget::updateSizeFilter);
184+
connect(m_ui->minSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateSizeFilter);
185+
connect(m_ui->maxSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateSizeFilter);
184186

185187
connect(m_ui->resultsBrowser, &QAbstractItemView::doubleClicked, this, &SearchJobWidget::onItemDoubleClicked);
186188

@@ -193,7 +195,6 @@ SearchJobWidget::SearchJobWidget(const QString &id, const QString &searchPattern
193195
{
194196
m_searchPattern = searchPattern;
195197
m_proxyModel->setNameFilter(m_searchPattern);
196-
updateFilter();
197198

198199
appendSearchResults(searchResults);
199200
}
@@ -301,8 +302,8 @@ void SearchJobWidget::assignSearchHandler(SearchHandler *searchHandler)
301302
m_searchPattern = m_searchHandler->pattern();
302303

303304
m_proxyModel->setNameFilter(m_searchPattern);
304-
updateFilter();
305305

306+
updateResultsCount();
306307
setStatus(Status::Ongoing);
307308
}
308309

@@ -454,26 +455,34 @@ void SearchJobWidget::updateResultsCount()
454455
emit resultsCountUpdated();
455456
}
456457

457-
void SearchJobWidget::updateFilter()
458+
void SearchJobWidget::updateNameFilter()
458459
{
459-
using Utils::Misc::SizeUnit;
460+
const auto filteringMode = static_cast<NameFilteringMode>(m_ui->filterMode->itemData(m_ui->filterMode->currentIndex()).toInt());
461+
m_proxyModel->enableNameFilter(filteringMode == NameFilteringMode::OnlyNames);
462+
m_nameFilteringMode = filteringMode;
460463

461-
m_proxyModel->enableNameFilter(filteringMode() == NameFilteringMode::OnlyNames);
464+
updateResultsCount();
465+
}
466+
467+
void SearchJobWidget::updateSeedsFilter()
468+
{
462469
// we update size and seeds filter parameters in the model even if they are disabled
463470
m_proxyModel->setSeedsFilter(m_ui->minSeeds->value(), m_ui->maxSeeds->value());
464-
m_proxyModel->setSizeFilter(
465-
sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex())),
466-
sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
467471

468-
m_nameFilteringMode = filteringMode();
472+
updateResultsCount();
473+
}
474+
475+
void SearchJobWidget::updateSizeFilter()
476+
{
477+
// we update size and seeds filter parameters in the model even if they are disabled
478+
m_proxyModel->setSizeFilter(sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex()))
479+
, sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
469480

470-
m_proxyModel->invalidate();
471481
updateResultsCount();
472482
}
473483

474484
void SearchJobWidget::fillFilterComboBoxes()
475485
{
476-
using Utils::Misc::SizeUnit;
477486
using Utils::Misc::unitString;
478487

479488
QStringList unitStrings;
@@ -500,16 +509,15 @@ void SearchJobWidget::fillFilterComboBoxes()
500509

501510
m_ui->filterMode->addItem(tr("Torrent names only"), static_cast<int>(NameFilteringMode::OnlyNames));
502511
m_ui->filterMode->addItem(tr("Everywhere"), static_cast<int>(NameFilteringMode::Everywhere));
503-
504-
const QVariant selectedMode = static_cast<int>(m_nameFilteringMode.get(NameFilteringMode::OnlyNames));
505-
const int index = m_ui->filterMode->findData(selectedMode);
512+
const auto selectedFilteringMode = static_cast<int>(m_nameFilteringMode.get(NameFilteringMode::OnlyNames));
513+
const int index = m_ui->filterMode->findData(selectedFilteringMode);
506514
m_ui->filterMode->setCurrentIndex((index == -1) ? 0 : index);
507515
}
508516

509517
void SearchJobWidget::filterSearchResults(const QString &name)
510518
{
511519
const QString pattern = (Preferences::instance()->getRegexAsFilteringPatternForSearchJob()
512-
? name : Utils::String::wildcardToRegexPattern(name));
520+
? name : Utils::String::wildcardToRegexPattern(name));
513521
m_proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
514522
updateResultsCount();
515523
}
@@ -557,11 +565,6 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
557565
menu->popup(event->globalPos());
558566
}
559567

560-
SearchJobWidget::NameFilteringMode SearchJobWidget::filteringMode() const
561-
{
562-
return static_cast<NameFilteringMode>(m_ui->filterMode->itemData(m_ui->filterMode->currentIndex()).toInt());
563-
}
564-
565568
void SearchJobWidget::loadSettings()
566569
{
567570
header()->restoreState(Preferences::instance()->getSearchTabHeaderState());

src/gui/search/searchjobwidget.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ private slots:
106106

107107
void loadSettings();
108108
void saveSettings() const;
109-
void updateFilter();
109+
void updateNameFilter();
110+
void updateSeedsFilter();
111+
void updateSizeFilter();
110112
void filterSearchResults(const QString &name);
111113
void showFilterContextMenu();
112114
void contextMenuEvent(QContextMenuEvent *event) override;
@@ -119,7 +121,6 @@ private slots:
119121
void downloadTorrent(const QModelIndex &rowIndex, AddTorrentOption option = AddTorrentOption::Default);
120122
void addTorrentToSession(const QString &source, AddTorrentOption option = AddTorrentOption::Default);
121123
void fillFilterComboBoxes();
122-
NameFilteringMode filteringMode() const;
123124
QHeaderView *header() const;
124125
int visibleColumnsCount() const;
125126
void setRowColor(int row, const QColor &color);

src/gui/search/searchsortmodel.cpp

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Bittorrent Client using Qt and libtorrent.
3+
* Copyright (C) 2025 Vladimir Golovnev <[email protected]>
34
* Copyright (C) 2013 sledgehammer999 <[email protected]>
45
*
56
* This program is free software; you can redistribute it and/or
@@ -39,34 +40,103 @@ SearchSortModel::SearchSortModel(QObject *parent)
3940

4041
void SearchSortModel::enableNameFilter(const bool enabled)
4142
{
43+
if (m_isNameFilterEnabled == enabled)
44+
return;
45+
46+
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
47+
beginFilterChange();
48+
m_isNameFilterEnabled = enabled;
49+
endFilterChange(Direction::Rows);
50+
#else
4251
m_isNameFilterEnabled = enabled;
52+
invalidateRowsFilter();
53+
#endif
4354
}
4455

4556
void SearchSortModel::setNameFilter(const QString &searchTerm)
4657
{
58+
if (m_searchTerm == searchTerm)
59+
return;
60+
61+
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
62+
beginFilterChange();
63+
4764
m_searchTerm = searchTerm;
4865
if ((searchTerm.length() > 2) && searchTerm.startsWith(u'"') && searchTerm.endsWith(u'"'))
4966
m_searchTermWords = QStringList(m_searchTerm.sliced(1, (m_searchTerm.length() - 2)));
5067
else
5168
m_searchTermWords = searchTerm.split(u' ', Qt::SkipEmptyParts);
69+
70+
endFilterChange(Direction::Rows);
71+
#else
72+
m_searchTerm = searchTerm;
73+
if ((searchTerm.length() > 2) && searchTerm.startsWith(u'"') && searchTerm.endsWith(u'"'))
74+
m_searchTermWords = QStringList(m_searchTerm.sliced(1, (m_searchTerm.length() - 2)));
75+
else
76+
m_searchTermWords = searchTerm.split(u' ', Qt::SkipEmptyParts);
77+
78+
invalidateRowsFilter();
79+
#endif
5280
}
5381

54-
void SearchSortModel::setSizeFilter(const qint64 minSize, const qint64 maxSize)
82+
void SearchSortModel::setSizeFilter(qint64 minSize, qint64 maxSize)
5583
{
56-
m_minSize = std::max(static_cast<qint64>(0), minSize);
57-
m_maxSize = std::max(static_cast<qint64>(-1), maxSize);
84+
minSize = std::max<qint64>(0, minSize);
85+
maxSize = std::max<qint64>(-1, maxSize);
86+
87+
if ((m_minSize == minSize) && (m_maxSize == maxSize))
88+
return;
89+
90+
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
91+
beginFilterChange();
92+
m_minSize = minSize;
93+
m_maxSize = maxSize;
94+
endFilterChange(Direction::Rows);
95+
#else
96+
m_minSize = minSize;
97+
m_maxSize = maxSize;
98+
invalidateRowsFilter();
99+
#endif
58100
}
59101

60-
void SearchSortModel::setSeedsFilter(const int minSeeds, const int maxSeeds)
102+
void SearchSortModel::setSeedsFilter(int minSeeds, int maxSeeds)
61103
{
62-
m_minSeeds = std::max(0, minSeeds);
63-
m_maxSeeds = std::max(-1, maxSeeds);
104+
minSeeds = std::max(0, minSeeds);
105+
maxSeeds = std::max(-1, maxSeeds);
106+
107+
if ((m_minSeeds == minSeeds) && (m_maxSeeds == maxSeeds))
108+
return;
109+
110+
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
111+
beginFilterChange();
112+
m_minSeeds = minSeeds;
113+
m_maxSeeds = maxSeeds;
114+
endFilterChange(Direction::Rows);
115+
#else
116+
m_minSeeds = minSeeds;
117+
m_maxSeeds = maxSeeds;
118+
invalidateRowsFilter();
119+
#endif
64120
}
65121

66-
void SearchSortModel::setLeechesFilter(const int minLeeches, const int maxLeeches)
122+
void SearchSortModel::setLeechesFilter(int minLeeches, int maxLeeches)
67123
{
68-
m_minLeeches = std::max(0, minLeeches);
69-
m_maxLeeches = std::max(-1, maxLeeches);
124+
minLeeches = std::max(0, minLeeches);
125+
maxLeeches = std::max(-1, maxLeeches);
126+
127+
if ((m_minLeeches == minLeeches) && (m_maxLeeches == maxLeeches))
128+
return;
129+
130+
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
131+
beginFilterChange();
132+
m_minLeeches = minLeeches;
133+
m_maxLeeches = maxLeeches;
134+
endFilterChange(Direction::Rows);
135+
#else
136+
m_minLeeches = minLeeches;
137+
m_maxLeeches = maxLeeches;
138+
invalidateRowsFilter();
139+
#endif
70140
}
71141

72142
bool SearchSortModel::isNameFilterEnabled() const

src/gui/search/searchsortmodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Bittorrent Client using Qt and libtorrent.
3+
* Copyright (C) 2025 Vladimir Golovnev <[email protected]>
34
* Copyright (C) 2013 sledgehammer999 <[email protected]>
45
*
56
* This program is free software; you can redistribute it and/or

0 commit comments

Comments
 (0)