mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-17 06:01:33 -06:00
Improve search results filtering implementation
PR #23430. Closes #23396.
This commit is contained in:
committed by
GitHub
parent
1be4e646e1
commit
85f1c774f6
@@ -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,9 +509,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user