Improve search results filtering implementation

PR #23430.
Closes #23396.
This commit is contained in:
Vladimir Golovnev
2025-11-02 14:47:39 +03:00
committed by GitHub
parent 1be4e646e1
commit 85f1c774f6
4 changed files with 126 additions and 51 deletions

View File

@@ -85,6 +85,8 @@ namespace
}
}
using Utils::Misc::SizeUnit;
SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidget *parent)
: GUIApplicationComponent(app, parent)
, m_nameFilteringMode {u"Search/FilteringMode"_s}
@@ -99,6 +101,8 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
header()->setStretchLastSection(false);
header()->setTextElideMode(Qt::ElideRight);
fillFilterComboBoxes();
// Set Search results list model
m_searchListModel = new QStandardItemModel(0, SearchSortModel::NB_SEARCH_COLUMNS, this);
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
m_proxyModel = new SearchSortModel(this);
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSourceModel(m_searchListModel);
m_proxyModel->enableNameFilter(m_nameFilteringMode.get(NameFilteringMode::OnlyNames) == NameFilteringMode::OnlyNames);
m_proxyModel->setSeedsFilter(m_ui->minSeeds->value(), m_ui->maxSeeds->value());
m_proxyModel->setSizeFilter(sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex()))
, sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
updateResultsCount();
m_ui->resultsBrowser->setModel(m_proxyModel);
m_ui->resultsBrowser->hideColumn(SearchSortModel::DL_LINK); // Hide url column
@@ -154,8 +165,6 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
connect(header(), &QHeaderView::sectionMoved, this, &SearchJobWidget::saveSettings);
connect(header(), &QHeaderView::sortIndicatorChanged, this, &SearchJobWidget::saveSettings);
fillFilterComboBoxes();
m_lineEditSearchResultsFilter = new LineEdit(this);
m_lineEditSearchResultsFilter->setPlaceholderText(tr("Filter search results..."));
m_lineEditSearchResultsFilter->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -163,24 +172,17 @@ SearchJobWidget::SearchJobWidget(const QString &id, IGUIApplication *app, QWidge
connect(m_lineEditSearchResultsFilter, &LineEdit::textChanged, this, &SearchJobWidget::filterSearchResults);
m_ui->horizontalLayout->insertWidget(0, m_lineEditSearchResultsFilter);
connect(m_ui->filterMode, qOverload<int>(&QComboBox::currentIndexChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSeeds, qOverload<int>(&QSpinBox::valueChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->maxSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
connect(m_ui->maxSeeds, qOverload<int>(&QSpinBox::valueChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSize, qOverload<double>(&QDoubleSpinBox::valueChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->maxSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);
connect(m_ui->maxSize, qOverload<double>(&QDoubleSpinBox::valueChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->maxSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->filterMode, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateNameFilter);
connect(m_ui->minSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSeedsFilter);
connect(m_ui->minSeeds, qOverload<int>(&QSpinBox::valueChanged), this, &SearchJobWidget::updateSeedsFilter);
connect(m_ui->maxSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSeedsFilter);
connect(m_ui->maxSeeds, qOverload<int>(&QSpinBox::valueChanged), this, &SearchJobWidget::updateSeedsFilter);
connect(m_ui->minSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->minSize, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->maxSize, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->maxSize, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->minSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->maxSizeUnit, qOverload<int>(&QComboBox::currentIndexChanged), this, &SearchJobWidget::updateSizeFilter);
connect(m_ui->resultsBrowser, &QAbstractItemView::doubleClicked, this, &SearchJobWidget::onItemDoubleClicked);
@@ -193,7 +195,6 @@ SearchJobWidget::SearchJobWidget(const QString &id, const QString &searchPattern
{
m_searchPattern = searchPattern;
m_proxyModel->setNameFilter(m_searchPattern);
updateFilter();
appendSearchResults(searchResults);
}
@@ -301,8 +302,8 @@ void SearchJobWidget::assignSearchHandler(SearchHandler *searchHandler)
m_searchPattern = m_searchHandler->pattern();
m_proxyModel->setNameFilter(m_searchPattern);
updateFilter();
updateResultsCount();
setStatus(Status::Ongoing);
}
@@ -454,26 +455,34 @@ void SearchJobWidget::updateResultsCount()
emit resultsCountUpdated();
}
void SearchJobWidget::updateFilter()
void SearchJobWidget::updateNameFilter()
{
using Utils::Misc::SizeUnit;
const auto filteringMode = static_cast<NameFilteringMode>(m_ui->filterMode->itemData(m_ui->filterMode->currentIndex()).toInt());
m_proxyModel->enableNameFilter(filteringMode == NameFilteringMode::OnlyNames);
m_nameFilteringMode = filteringMode;
m_proxyModel->enableNameFilter(filteringMode() == NameFilteringMode::OnlyNames);
updateResultsCount();
}
void SearchJobWidget::updateSeedsFilter()
{
// we update size and seeds filter parameters in the model even if they are disabled
m_proxyModel->setSeedsFilter(m_ui->minSeeds->value(), m_ui->maxSeeds->value());
m_proxyModel->setSizeFilter(
sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex())),
sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
m_nameFilteringMode = filteringMode();
updateResultsCount();
}
void SearchJobWidget::updateSizeFilter()
{
// we update size and seeds filter parameters in the model even if they are disabled
m_proxyModel->setSizeFilter(sizeInBytes(m_ui->minSize->value(), static_cast<SizeUnit>(m_ui->minSizeUnit->currentIndex()))
, sizeInBytes(m_ui->maxSize->value(), static_cast<SizeUnit>(m_ui->maxSizeUnit->currentIndex())));
m_proxyModel->invalidate();
updateResultsCount();
}
void SearchJobWidget::fillFilterComboBoxes()
{
using Utils::Misc::SizeUnit;
using Utils::Misc::unitString;
QStringList unitStrings;
@@ -500,16 +509,15 @@ void SearchJobWidget::fillFilterComboBoxes()
m_ui->filterMode->addItem(tr("Torrent names only"), static_cast<int>(NameFilteringMode::OnlyNames));
m_ui->filterMode->addItem(tr("Everywhere"), static_cast<int>(NameFilteringMode::Everywhere));
const QVariant selectedMode = static_cast<int>(m_nameFilteringMode.get(NameFilteringMode::OnlyNames));
const int index = m_ui->filterMode->findData(selectedMode);
const auto selectedFilteringMode = static_cast<int>(m_nameFilteringMode.get(NameFilteringMode::OnlyNames));
const int index = m_ui->filterMode->findData(selectedFilteringMode);
m_ui->filterMode->setCurrentIndex((index == -1) ? 0 : index);
}
void SearchJobWidget::filterSearchResults(const QString &name)
{
const QString pattern = (Preferences::instance()->getRegexAsFilteringPatternForSearchJob()
? name : Utils::String::wildcardToRegexPattern(name));
? name : Utils::String::wildcardToRegexPattern(name));
m_proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
updateResultsCount();
}
@@ -557,11 +565,6 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
menu->popup(event->globalPos());
}
SearchJobWidget::NameFilteringMode SearchJobWidget::filteringMode() const
{
return static_cast<NameFilteringMode>(m_ui->filterMode->itemData(m_ui->filterMode->currentIndex()).toInt());
}
void SearchJobWidget::loadSettings()
{
header()->restoreState(Preferences::instance()->getSearchTabHeaderState());

View File

@@ -106,7 +106,9 @@ private:
void loadSettings();
void saveSettings() const;
void updateFilter();
void updateNameFilter();
void updateSeedsFilter();
void updateSizeFilter();
void filterSearchResults(const QString &name);
void showFilterContextMenu();
void contextMenuEvent(QContextMenuEvent *event) override;
@@ -119,7 +121,6 @@ private:
void downloadTorrent(const QModelIndex &rowIndex, AddTorrentOption option = AddTorrentOption::Default);
void addTorrentToSession(const QString &source, AddTorrentOption option = AddTorrentOption::Default);
void fillFilterComboBoxes();
NameFilteringMode filteringMode() const;
QHeaderView *header() const;
int visibleColumnsCount() const;
void setRowColor(int row, const QColor &color);

View File

@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2013 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
@@ -39,34 +40,103 @@ SearchSortModel::SearchSortModel(QObject *parent)
void SearchSortModel::enableNameFilter(const bool enabled)
{
if (m_isNameFilterEnabled == enabled)
return;
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
m_isNameFilterEnabled = enabled;
endFilterChange(Direction::Rows);
#else
m_isNameFilterEnabled = enabled;
invalidateRowsFilter();
#endif
}
void SearchSortModel::setNameFilter(const QString &searchTerm)
{
if (m_searchTerm == searchTerm)
return;
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
m_searchTerm = searchTerm;
if ((searchTerm.length() > 2) && searchTerm.startsWith(u'"') && searchTerm.endsWith(u'"'))
m_searchTermWords = QStringList(m_searchTerm.sliced(1, (m_searchTerm.length() - 2)));
else
m_searchTermWords = searchTerm.split(u' ', Qt::SkipEmptyParts);
endFilterChange(Direction::Rows);
#else
m_searchTerm = searchTerm;
if ((searchTerm.length() > 2) && searchTerm.startsWith(u'"') && searchTerm.endsWith(u'"'))
m_searchTermWords = QStringList(m_searchTerm.sliced(1, (m_searchTerm.length() - 2)));
else
m_searchTermWords = searchTerm.split(u' ', Qt::SkipEmptyParts);
invalidateRowsFilter();
#endif
}
void SearchSortModel::setSizeFilter(const qint64 minSize, const qint64 maxSize)
void SearchSortModel::setSizeFilter(qint64 minSize, qint64 maxSize)
{
m_minSize = std::max(static_cast<qint64>(0), minSize);
m_maxSize = std::max(static_cast<qint64>(-1), maxSize);
minSize = std::max<qint64>(0, minSize);
maxSize = std::max<qint64>(-1, maxSize);
if ((m_minSize == minSize) && (m_maxSize == maxSize))
return;
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
m_minSize = minSize;
m_maxSize = maxSize;
endFilterChange(Direction::Rows);
#else
m_minSize = minSize;
m_maxSize = maxSize;
invalidateRowsFilter();
#endif
}
void SearchSortModel::setSeedsFilter(const int minSeeds, const int maxSeeds)
void SearchSortModel::setSeedsFilter(int minSeeds, int maxSeeds)
{
m_minSeeds = std::max(0, minSeeds);
m_maxSeeds = std::max(-1, maxSeeds);
minSeeds = std::max(0, minSeeds);
maxSeeds = std::max(-1, maxSeeds);
if ((m_minSeeds == minSeeds) && (m_maxSeeds == maxSeeds))
return;
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
m_minSeeds = minSeeds;
m_maxSeeds = maxSeeds;
endFilterChange(Direction::Rows);
#else
m_minSeeds = minSeeds;
m_maxSeeds = maxSeeds;
invalidateRowsFilter();
#endif
}
void SearchSortModel::setLeechesFilter(const int minLeeches, const int maxLeeches)
void SearchSortModel::setLeechesFilter(int minLeeches, int maxLeeches)
{
m_minLeeches = std::max(0, minLeeches);
m_maxLeeches = std::max(-1, maxLeeches);
minLeeches = std::max(0, minLeeches);
maxLeeches = std::max(-1, maxLeeches);
if ((m_minLeeches == minLeeches) && (m_maxLeeches == maxLeeches))
return;
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
beginFilterChange();
m_minLeeches = minLeeches;
m_maxLeeches = maxLeeches;
endFilterChange(Direction::Rows);
#else
m_minLeeches = minLeeches;
m_maxLeeches = maxLeeches;
invalidateRowsFilter();
#endif
}
bool SearchSortModel::isNameFilterEnabled() const

View File

@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2013 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or