refactor: only update filterbar comboboxes when necessary (#1335)
* refactor: Torrent::update() returns a delta bitset Setting up for followup PRs where, instead of doing expensive work every time there is a change, we can be more fine-grained and do the work only if the relevant Torrent properties changed. * chore: make uncrustify happy * refactor: update filterbar counts more selectively Only rebuild the activity and tracker combobox models when the model's size changes or when the relevant Torrent properties change. Previously, rebuild would happen on any Torrent property change even if the properties were unrelated to activity or trackers. * chore: remove redundant "private:" key
This commit is contained in:
parent
f37253a3ab
commit
33a421e97f
|
@ -6,6 +6,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <cstdint> // uint64_t
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -95,6 +96,12 @@ QString getCountString(int n)
|
|||
return QStringLiteral("%L1").arg(n);
|
||||
}
|
||||
|
||||
Torrent::fields_t constexpr TrackerFields = {
|
||||
(uint64_t(1) << Torrent::TRACKER_STATS)
|
||||
};
|
||||
|
||||
auto constexpr ActivityFields = FilterMode::TorrentFields;
|
||||
|
||||
} // namespace
|
||||
|
||||
void FilterBar::refreshTrackers()
|
||||
|
@ -229,14 +236,14 @@ FilterBar::FilterBar(Prefs& prefs, TorrentModel const& torrents, TorrentFilter c
|
|||
connect(&prefs_, SIGNAL(changed(int)), this, SLOT(refreshPref(int)));
|
||||
connect(activity_combo_, SIGNAL(currentIndexChanged(int)), this, SLOT(onActivityIndexChanged(int)));
|
||||
connect(tracker_combo_, SIGNAL(currentIndexChanged(int)), this, SLOT(onTrackerIndexChanged(int)));
|
||||
connect(&torrents_, SIGNAL(modelReset()), this, SLOT(recountSoon()));
|
||||
connect(&torrents_, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(recountSoon()));
|
||||
connect(&torrents_, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(recountSoon()));
|
||||
connect(&torrents_, &TorrentModel::modelReset, this, &FilterBar::recountAllSoon);
|
||||
connect(&torrents_, &TorrentModel::rowsInserted, this, &FilterBar::recountAllSoon);
|
||||
connect(&torrents_, &TorrentModel::rowsRemoved, this, &FilterBar::recountAllSoon);
|
||||
connect(&torrents_, &TorrentModel::torrentsChanged, this, &FilterBar::onTorrentsChanged);
|
||||
connect(recount_timer_, SIGNAL(timeout()), this, SLOT(recount()));
|
||||
connect(&qApp->faviconCache(), &FaviconCache::pixmapReady, this, &FilterBar::recountTrackersSoon);
|
||||
|
||||
recountSoon();
|
||||
refreshTrackers();
|
||||
recountAllSoon();
|
||||
is_bootstrapping_ = false;
|
||||
|
||||
// initialize our state
|
||||
|
@ -299,10 +306,17 @@ void FilterBar::refreshPref(int key)
|
|||
|
||||
void FilterBar::onTorrentsChanged(torrent_ids_t const& ids, Torrent::fields_t const& changed_fields)
|
||||
{
|
||||
Q_UNUSED(ids);
|
||||
Q_UNUSED(changed_fields);
|
||||
Q_UNUSED(ids)
|
||||
|
||||
recountSoon();
|
||||
if ((changed_fields & TrackerFields).any())
|
||||
{
|
||||
recountTrackersSoon();
|
||||
}
|
||||
|
||||
if ((changed_fields & ActivityFields).any())
|
||||
{
|
||||
recountActivitySoon();
|
||||
}
|
||||
}
|
||||
|
||||
void FilterBar::onTextChanged(QString const& str)
|
||||
|
@ -352,8 +366,10 @@ void FilterBar::onActivityIndexChanged(int i)
|
|||
****
|
||||
***/
|
||||
|
||||
void FilterBar::recountSoon()
|
||||
void FilterBar::recountSoon(Pending const& pending)
|
||||
{
|
||||
pending_ |= pending;
|
||||
|
||||
if (!recount_timer_->isActive())
|
||||
{
|
||||
recount_timer_->setSingleShot(true);
|
||||
|
@ -365,16 +381,25 @@ void FilterBar::recount()
|
|||
{
|
||||
QAbstractItemModel* model = activity_combo_->model();
|
||||
|
||||
auto const torrents_per_mode = filter_.countTorrentsPerMode();
|
||||
decltype(pending_) pending = {};
|
||||
std::swap(pending_, pending);
|
||||
|
||||
for (int row = 0, n = model->rowCount(); row < n; ++row)
|
||||
if (pending[ACTIVITY])
|
||||
{
|
||||
QModelIndex index = model->index(row, 0);
|
||||
int const mode = index.data(ACTIVITY_ROLE).toInt();
|
||||
int const count = torrents_per_mode[mode];
|
||||
model->setData(index, count, FilterBarComboBox::CountRole);
|
||||
model->setData(index, getCountString(count), FilterBarComboBox::CountStringRole);
|
||||
auto const torrents_per_mode = filter_.countTorrentsPerMode();
|
||||
|
||||
for (int row = 0, n = model->rowCount(); row < n; ++row)
|
||||
{
|
||||
auto const index = model->index(row, 0);
|
||||
auto const mode = index.data(ACTIVITY_ROLE).toInt();
|
||||
auto const count = torrents_per_mode[mode];
|
||||
model->setData(index, count, FilterBarComboBox::CountRole);
|
||||
model->setData(index, getCountString(count), FilterBarComboBox::CountStringRole);
|
||||
}
|
||||
}
|
||||
|
||||
refreshTrackers();
|
||||
if (pending[TRACKERS])
|
||||
{
|
||||
refreshTrackers();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Torrent.h"
|
||||
|
@ -40,16 +42,16 @@ private:
|
|||
FilterBarComboBox* createActivityCombo();
|
||||
void refreshTrackers();
|
||||
|
||||
private slots:
|
||||
void recountSoon();
|
||||
void recount();
|
||||
void refreshPref(int key);
|
||||
void onActivityIndexChanged(int index);
|
||||
void onTrackerIndexChanged(int index);
|
||||
void onTextChanged(QString const&);
|
||||
void onTorrentsChanged(torrent_ids_t const&, Torrent::fields_t const& fields);
|
||||
enum
|
||||
{
|
||||
ACTIVITY,
|
||||
TRACKERS,
|
||||
|
||||
NUM_FLAGS
|
||||
};
|
||||
|
||||
using Pending = std::bitset<NUM_FLAGS>;
|
||||
|
||||
private:
|
||||
Prefs& prefs_;
|
||||
TorrentModel const& torrents_;
|
||||
TorrentFilter const& filter_;
|
||||
|
@ -61,5 +63,19 @@ private:
|
|||
QStandardItemModel* tracker_model_ = {};
|
||||
QTimer* recount_timer_ = {};
|
||||
QLineEdit* line_edit_ = {};
|
||||
Pending pending_ = {};
|
||||
bool is_bootstrapping_ = {};
|
||||
|
||||
private slots:
|
||||
void recount();
|
||||
void recountSoon(Pending const& fields);
|
||||
void recountActivitySoon() { recountSoon(Pending().set(ACTIVITY)); }
|
||||
void recountTrackersSoon() { recountSoon(Pending().set(TRACKERS)); }
|
||||
void recountAllSoon() { recountSoon(Pending().set(ACTIVITY).set(TRACKERS)); }
|
||||
|
||||
void refreshPref(int key);
|
||||
void onActivityIndexChanged(int index);
|
||||
void onTextChanged(QString const&);
|
||||
void onTorrentsChanged(torrent_ids_t const&, Torrent::fields_t const& fields);
|
||||
void onTrackerIndexChanged(int index);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <cstdint> // uint64_t
|
||||
|
||||
#include "Filters.h"
|
||||
|
||||
std::array<QString, FilterMode::NUM_MODES> const FilterMode::Names =
|
||||
|
@ -33,6 +35,41 @@ int FilterMode::modeFromName(QString const& name)
|
|||
return FilterMode().mode(); // use the default value
|
||||
}
|
||||
|
||||
// NB: if you change this function, update TorrentFields too
|
||||
bool FilterMode::test(Torrent const& tor, int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SHOW_ACTIVE:
|
||||
return tor.peersWeAreUploadingTo() > 0 || tor.peersWeAreDownloadingFrom() > 0 || tor.isVerifying();
|
||||
|
||||
case SHOW_DOWNLOADING:
|
||||
return tor.isDownloading() || tor.isWaitingToDownload();
|
||||
|
||||
case SHOW_ERROR:
|
||||
return tor.hasError();
|
||||
|
||||
case SHOW_FINISHED:
|
||||
return tor.isFinished();
|
||||
|
||||
case SHOW_PAUSED:
|
||||
return tor.isPaused();
|
||||
|
||||
case SHOW_SEEDING:
|
||||
return tor.isSeeding() || tor.isWaitingToSeed();
|
||||
|
||||
case SHOW_VERIFYING:
|
||||
return tor.isVerifying() || tor.isWaitingToVerify();
|
||||
|
||||
default: // SHOW_ALL
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
std::array<QString, SortMode::NUM_MODES> const SortMode::Names =
|
||||
{
|
||||
QStringLiteral("sort-by-activity"),
|
||||
|
|
15
qt/Filters.h
15
qt/Filters.h
|
@ -14,6 +14,8 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "Torrent.h"
|
||||
|
||||
class FilterMode
|
||||
{
|
||||
public:
|
||||
|
@ -58,6 +60,19 @@ public:
|
|||
return Names[mode];
|
||||
}
|
||||
|
||||
/* The Torrent properties that can affect this filter.
|
||||
When one of these changes, it's time to refilter. */
|
||||
static Torrent::fields_t constexpr TorrentFields = {
|
||||
(uint64_t(1) << Torrent::ERROR) |
|
||||
(uint64_t(1) << Torrent::IS_FINISHED) |
|
||||
(uint64_t(1) << Torrent::PEERS_GETTING_FROM_US) |
|
||||
(uint64_t(1) << Torrent::PEERS_SENDING_TO_US) |
|
||||
(uint64_t(1) << Torrent::STATUS)
|
||||
};
|
||||
|
||||
static bool test(Torrent const& tor, int mode);
|
||||
bool test(Torrent const& tor) const { return test(tor, mode()); }
|
||||
|
||||
private:
|
||||
int mode_;
|
||||
|
||||
|
|
|
@ -231,79 +231,28 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
|
|||
****
|
||||
***/
|
||||
|
||||
bool TorrentFilter::trackerFilterAcceptsTorrent(Torrent const* tor, QString const& tracker) const
|
||||
{
|
||||
return tracker.isEmpty() || tor->hasTrackerSubstring(tracker);
|
||||
}
|
||||
|
||||
bool TorrentFilter::activityFilterAcceptsTorrent(Torrent const* tor, FilterMode const& m) const
|
||||
{
|
||||
bool accepts;
|
||||
|
||||
switch (m.mode())
|
||||
{
|
||||
case FilterMode::SHOW_ACTIVE:
|
||||
accepts = tor->peersWeAreUploadingTo() > 0 || tor->peersWeAreDownloadingFrom() > 0 || tor->isVerifying();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_DOWNLOADING:
|
||||
accepts = tor->isDownloading() || tor->isWaitingToDownload();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_SEEDING:
|
||||
accepts = tor->isSeeding() || tor->isWaitingToSeed();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_PAUSED:
|
||||
accepts = tor->isPaused();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_FINISHED:
|
||||
accepts = tor->isFinished();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_VERIFYING:
|
||||
accepts = tor->isVerifying() || tor->isWaitingToVerify();
|
||||
break;
|
||||
|
||||
case FilterMode::SHOW_ERROR:
|
||||
accepts = tor->hasError();
|
||||
break;
|
||||
|
||||
default: // FilterMode::SHOW_ALL
|
||||
accepts = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return accepts;
|
||||
}
|
||||
|
||||
bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_parent) const
|
||||
{
|
||||
QModelIndex child_index = sourceModel()->index(source_row, 0, source_parent);
|
||||
auto const* tor = child_index.model()->data(child_index, TorrentModel::TorrentRole).value<Torrent const*>();
|
||||
auto const& tor = *child_index.model()->data(child_index, TorrentModel::TorrentRole).value<Torrent const*>();
|
||||
bool accepts = true;
|
||||
|
||||
if (accepts)
|
||||
{
|
||||
auto const m = prefs_.get<FilterMode>(Prefs::FILTER_MODE);
|
||||
accepts = activityFilterAcceptsTorrent(tor, m);
|
||||
accepts = m.test(tor);
|
||||
}
|
||||
|
||||
if (accepts)
|
||||
{
|
||||
QString const trackers = prefs_.getString(Prefs::FILTER_TRACKERS);
|
||||
accepts = trackerFilterAcceptsTorrent(tor, trackers);
|
||||
auto const name = prefs_.getString(Prefs::FILTER_TRACKERS);
|
||||
accepts = name.isEmpty() || tor.hasTrackerSubstring(name);
|
||||
}
|
||||
|
||||
if (accepts)
|
||||
{
|
||||
QString const text = prefs_.getString(Prefs::FILTER_TEXT);
|
||||
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
accepts = tor->name().contains(text, Qt::CaseInsensitive);
|
||||
}
|
||||
auto const text = prefs_.getString(Prefs::FILTER_TEXT);
|
||||
accepts = text.isEmpty() || tor.name().contains(text, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
return accepts;
|
||||
|
@ -317,7 +266,7 @@ std::array<int, FilterMode::NUM_MODES> TorrentFilter::countTorrentsPerMode() con
|
|||
{
|
||||
for (int mode = 0; mode < FilterMode::NUM_MODES; ++mode)
|
||||
{
|
||||
if (activityFilterAcceptsTorrent(tor, mode))
|
||||
if (FilterMode::test(*tor, mode))
|
||||
{
|
||||
++torrent_counts[mode];
|
||||
}
|
||||
|
|
|
@ -47,9 +47,6 @@ private slots:
|
|||
void refilter();
|
||||
|
||||
private:
|
||||
bool activityFilterAcceptsTorrent(Torrent const* tor, FilterMode const& mode) const;
|
||||
bool trackerFilterAcceptsTorrent(Torrent const* tor, QString const& tracker) const;
|
||||
|
||||
QTimer refilter_timer_;
|
||||
Prefs const& prefs_;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue