feat: Add functionality to Desktop/App too.

* Add preference flag that is turned **OFF** by default
* Exposes the `Type` in `TorrentFilter` so that we can know on which status we are.
* Maintains a `QHash` object with the `Status` as `key` and the pair of `column` + `sort order` as `value`.
     * This sorting is maintained for the session being, we do not save between shutdowns of QBT
This commit is contained in:
Stiliyan Tonev (Bark) 2025-05-27 09:31:45 +03:00
commit e13139a264
12 changed files with 69 additions and 2 deletions

View file

@ -425,6 +425,19 @@ void Preferences::setPreventFromSuspendWhenSeeding(const bool b)
setValue(u"Preferences/General/PreventFromSuspendWhenSeeding"_s, b); setValue(u"Preferences/General/PreventFromSuspendWhenSeeding"_s, b);
} }
bool Preferences::usePerStatusSortOrder() const
{
return value(u"Preferences/General/UsePerStatusSortOrder"_s, false);
}
void Preferences::setUsePerStatusSortOrder(const bool use)
{
if (use == usePerStatusSortOrder())
return;
setValue(u"Preferences/General/UsePerStatusSortOrder"_s, use);
}
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
bool Preferences::WinStartup() const bool Preferences::WinStartup() const
{ {

View file

@ -162,6 +162,9 @@ public:
int getActionOnDblClOnTorrentFn() const; int getActionOnDblClOnTorrentFn() const;
void setActionOnDblClOnTorrentFn(int act); void setActionOnDblClOnTorrentFn(int act);
bool usePerStatusSortOrder() const;
void setUsePerStatusSortOrder(bool use);
// Connection options // Connection options
QTime getSchedulerStartTime() const; QTime getSchedulerStartTime() const;
void setSchedulerStartTime(const QTime &time); void setSchedulerStartTime(const QTime &time);

View file

@ -100,6 +100,7 @@ public:
bool setType(Type type); bool setType(Type type);
Type getType() const { return m_type; }
bool setTypeByName(const QString &filter); bool setTypeByName(const QString &filter);
bool setTorrentIDSet(const std::optional<TorrentIDSet> &idSet); bool setTorrentIDSet(const std::optional<TorrentIDSet> &idSet);
bool setCategory(const std::optional<QString> &category); bool setCategory(const std::optional<QString> &category);

View file

@ -358,6 +358,7 @@ void OptionsDialog::loadBehaviorTabOptions()
m_ui->checkBoxFreeDiskSpaceStatusBar->setChecked(pref->isStatusbarFreeDiskSpaceDisplayed()); m_ui->checkBoxFreeDiskSpaceStatusBar->setChecked(pref->isStatusbarFreeDiskSpaceDisplayed());
m_ui->checkBoxExternalIPStatusBar->setChecked(pref->isStatusbarExternalIPDisplayed()); m_ui->checkBoxExternalIPStatusBar->setChecked(pref->isStatusbarExternalIPDisplayed());
m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled()); m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled());
m_ui->checkBoxPerFilterSorting->setChecked(pref->usePerStatusSortOrder());
connect(m_ui->comboLanguage, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboLanguage, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
@ -447,6 +448,7 @@ void OptionsDialog::loadBehaviorTabOptions()
connect(m_ui->checkBoxFreeDiskSpaceStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBoxFreeDiskSpaceStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBoxExternalIPStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBoxExternalIPStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBoxPerFilterSorting, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
} }
void OptionsDialog::saveBehaviorTabOptions() const void OptionsDialog::saveBehaviorTabOptions() const
@ -540,6 +542,7 @@ void OptionsDialog::saveBehaviorTabOptions() const
pref->setStatusbarFreeDiskSpaceDisplayed(m_ui->checkBoxFreeDiskSpaceStatusBar->isChecked()); pref->setStatusbarFreeDiskSpaceDisplayed(m_ui->checkBoxFreeDiskSpaceStatusBar->isChecked());
pref->setStatusbarExternalIPDisplayed(m_ui->checkBoxExternalIPStatusBar->isChecked()); pref->setStatusbarExternalIPDisplayed(m_ui->checkBoxExternalIPStatusBar->isChecked());
pref->setUsePerStatusSortOrder(m_ui->checkBoxPerFilterSorting->isChecked());
session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked()); session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked());
} }

View file

@ -840,6 +840,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="checkBoxPerFilterSorting">
<property name="text">
<string>Use Per-Status Sorting</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer_4"> <spacer name="verticalSpacer_4">
<property name="orientation"> <property name="orientation">

View file

@ -123,6 +123,11 @@ void TransferListSortModel::sort(const int column, const Qt::SortOrder order)
QSortFilterProxyModel::sort(column, order); QSortFilterProxyModel::sort(column, order);
} }
TorrentFilter::Type TransferListSortModel::getType() const
{
return m_filter.getType();
}
void TransferListSortModel::setStatusFilter(const TorrentFilter::Type filter) void TransferListSortModel::setStatusFilter(const TorrentFilter::Type filter)
{ {
if (m_filter.setType(filter)) if (m_filter.setType(filter))

View file

@ -56,6 +56,7 @@ public:
void disableTagFilter(); void disableTagFilter();
void setTrackerFilter(const QSet<BitTorrent::TorrentID> &torrentIDs); void setTrackerFilter(const QSet<BitTorrent::TorrentID> &torrentIDs);
void disableTrackerFilter(); void disableTrackerFilter();
TorrentFilter::Type getType() const;
private: private:
int compare(const QModelIndex &left, const QModelIndex &right) const; int compare(const QModelIndex &left, const QModelIndex &right) const;

View file

@ -1336,7 +1336,26 @@ void TransferListWidget::applyFilter(const QString &name, const TransferListMode
void TransferListWidget::applyStatusFilter(const int filterIndex) void TransferListWidget::applyStatusFilter(const int filterIndex)
{ {
const auto filterType = static_cast<TorrentFilter::Type>(filterIndex); const auto filterType = static_cast<TorrentFilter::Type>(filterIndex);
//Save sort order for old filter
const bool usePerFilterSort = Preferences::instance()->usePerStatusSortOrder();
const TorrentFilter::Type filter = m_sortFilterModel->getType();
const int column = m_sortFilterModel->sortColumn();
if (column != -1 && usePerFilterSort)
{
m_statusSortPairs.insert_or_assign(filter, qMakePair(column, m_sortFilterModel->sortOrder()));
}
m_sortFilterModel->setStatusFilter(((filterType >= TorrentFilter::All) && (filterType < TorrentFilter::_Count)) ? filterType : TorrentFilter::All); m_sortFilterModel->setStatusFilter(((filterType >= TorrentFilter::All) && (filterType < TorrentFilter::_Count)) ? filterType : TorrentFilter::All);
// Load sort for new status filter
if (m_statusSortPairs.contains(filterType) && usePerFilterSort)
{
const QPair<int, Qt::SortOrder> sortPair = m_statusSortPairs.value(filterType);
m_sortFilterModel->sort(sortPair.first, sortPair.second);
// Rows are sorted correctly, but header state does not reflect actual sort setting
header()->setSortIndicator(sortPair.first, sortPair.second);
}
// Select first item if nothing is selected // Select first item if nothing is selected
if (selectionModel()->selectedRows(0).empty() && (m_sortFilterModel->rowCount() > 0)) if (selectionModel()->selectedRows(0).empty() && (m_sortFilterModel->rowCount() > 0))
{ {

View file

@ -35,6 +35,7 @@
#include <QTreeView> #include <QTreeView>
#include "base/bittorrent/infohash.h" #include "base/bittorrent/infohash.h"
#include "base/torrentfilter.h"
#include "guiapplicationcomponent.h" #include "guiapplicationcomponent.h"
#include "transferlistmodel.h" #include "transferlistmodel.h"
@ -137,6 +138,7 @@ private:
void applyToSelectedTorrents(const std::function<void (BitTorrent::Torrent *const)> &fn); void applyToSelectedTorrents(const std::function<void (BitTorrent::Torrent *const)> &fn);
QList<BitTorrent::Torrent *> getVisibleTorrents() const; QList<BitTorrent::Torrent *> getVisibleTorrents() const;
int visibleColumnsCount() const; int visibleColumnsCount() const;
QHash<TorrentFilter::Type, QPair<int, Qt::SortOrder>> m_statusSortPairs;
TransferListModel *m_listModel = nullptr; TransferListModel *m_listModel = nullptr;
TransferListSortModel *m_sortFilterModel = nullptr; TransferListSortModel *m_sortFilterModel = nullptr;

View file

@ -137,6 +137,7 @@ void AppController::preferencesAction()
data[u"locale"_s] = pref->getLocale(); data[u"locale"_s] = pref->getLocale();
data[u"performance_warning"_s] = session->isPerformanceWarningEnabled(); data[u"performance_warning"_s] = session->isPerformanceWarningEnabled();
data[u"status_bar_external_ip"_s] = pref->isStatusbarExternalIPDisplayed(); data[u"status_bar_external_ip"_s] = pref->isStatusbarExternalIPDisplayed();
data[u"per_status_sorting"_s] = pref->usePerStatusSortOrder();
// Transfer List // Transfer List
data[u"confirm_torrent_deletion"_s] = pref->confirmTorrentDeletion(); data[u"confirm_torrent_deletion"_s] = pref->confirmTorrentDeletion();
// Log file // Log file
@ -530,6 +531,8 @@ void AppController::setPreferencesAction()
pref->setStatusbarExternalIPDisplayed(it.value().toBool()); pref->setStatusbarExternalIPDisplayed(it.value().toBool());
if (hasKey(u"performance_warning"_s)) if (hasKey(u"performance_warning"_s))
session->setPerformanceWarningEnabled(it.value().toBool()); session->setPerformanceWarningEnabled(it.value().toBool());
if (hasKey(u"per_status_sorting"_s))
pref->setUsePerStatusSortOrder(it.value().toBool());
// Transfer List // Transfer List
if (hasKey(u"confirm_torrent_deletion"_s)) if (hasKey(u"confirm_torrent_deletion"_s))
pref->setConfirmTorrentDeletion(it.value().toBool()); pref->setConfirmTorrentDeletion(it.value().toBool());

View file

@ -267,21 +267,24 @@ window.addEventListener("DOMContentLoaded", (event) => {
setStatusFilter = (name) => { setStatusFilter = (name) => {
const currentHash = torrentsTable.getCurrentTorrentID(); const currentHash = torrentsTable.getCurrentTorrentID();
const usePerFilterSorting = window.qBittorrent.Cache.preferences.get().per_status_sorting;
// Save current sorting for this filter. // Save current sorting for this filter.
if (torrentsTable.getSortedColumn()) { if ((torrentsTable.getSortedColumn()) && usePerFilterSorting) {
LocalPreferences.set(`selected_filter_sort_${selectedStatus}`, torrentsTable.getSortedColumn()); LocalPreferences.set(`selected_filter_sort_${selectedStatus}`, torrentsTable.getSortedColumn());
LocalPreferences.set(`selected_filter_sort_reverse_${selectedStatus}`, (torrentsTable.reverseSort ?? "0")); LocalPreferences.set(`selected_filter_sort_reverse_${selectedStatus}`, (torrentsTable.reverseSort ?? "0"));
} }
LocalPreferences.set("selected_filter", name); LocalPreferences.set("selected_filter", name);
// If there is a saved sorting column, load it. // If there is a saved sorting column, load it.
const sortColumn = LocalPreferences.get(`selected_filter_sort_${name}`); const sortColumn = LocalPreferences.get(`selected_filter_sort_${name}`);
if (sortColumn !== null) { if ((sortColumn !== null) && usePerFilterSorting) {
torrentsTable.setSortedColumn( torrentsTable.setSortedColumn(
sortColumn, sortColumn,
LocalPreferences.get(`selected_filter_sort_reverse_${name}`, "0") LocalPreferences.get(`selected_filter_sort_reverse_${name}`, "0")
); );
} }
selectedStatus = name; selectedStatus = name;
highlightSelectedStatus(); highlightSelectedStatus();
updateMainData(); updateMainData();

View file

@ -100,6 +100,11 @@
<label for="performanceWarning">QBT_TR(Log performance warnings)QBT_TR[CONTEXT=OptionsDialog]</label> <label for="performanceWarning">QBT_TR(Log performance warnings)QBT_TR[CONTEXT=OptionsDialog]</label>
</div> </div>
<div class="formRow" style="margin-bottom: 5px;">
<input type="checkbox" id="perStatusSorting">
<label for="perStatusSorting">QBT_TR(Use Per-Status Sorting)QBT_TR[CONTEXT=OptionsDialog]</label>
</div>
<fieldset class="settings"> <fieldset class="settings">
<legend title="QBT_TR(Following settings are WebUI only)QBT_TR[CONTEXT=OptionsDialog]">QBT_TR(Custom WebUI settings)QBT_TR[CONTEXT=OptionsDialog]</legend> <legend title="QBT_TR(Following settings are WebUI only)QBT_TR[CONTEXT=OptionsDialog]">QBT_TR(Custom WebUI settings)QBT_TR[CONTEXT=OptionsDialog]</legend>
<div class="formRow" style="margin-bottom: 3px;"> <div class="formRow" style="margin-bottom: 3px;">
@ -2224,6 +2229,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
updateColoSchemeSelect(); updateColoSchemeSelect();
document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip; document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip;
document.getElementById("performanceWarning").checked = pref.performance_warning; document.getElementById("performanceWarning").checked = pref.performance_warning;
document.getElementById("perStatusSorting").checked = pref.per_status_sorting;
document.getElementById("displayFullURLTrackerColumn").checked = (LocalPreferences.get("full_url_tracker_column", "false") === "true"); document.getElementById("displayFullURLTrackerColumn").checked = (LocalPreferences.get("full_url_tracker_column", "false") === "true");
document.getElementById("useVirtualList").checked = (LocalPreferences.get("use_virtual_list", "false") === "true"); document.getElementById("useVirtualList").checked = (LocalPreferences.get("use_virtual_list", "false") === "true");
document.getElementById("hideZeroFiltersCheckbox").checked = (LocalPreferences.get("hide_zero_status_filters", "false") === "true"); document.getElementById("hideZeroFiltersCheckbox").checked = (LocalPreferences.get("hide_zero_status_filters", "false") === "true");
@ -2657,6 +2663,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
LocalPreferences.set("color_scheme", "dark"); LocalPreferences.set("color_scheme", "dark");
settings["status_bar_external_ip"] = document.getElementById("statusBarExternalIP").checked; settings["status_bar_external_ip"] = document.getElementById("statusBarExternalIP").checked;
settings["performance_warning"] = document.getElementById("performanceWarning").checked; settings["performance_warning"] = document.getElementById("performanceWarning").checked;
settings["per_status_sorting"] = document.getElementById("perStatusSorting").checked;
LocalPreferences.set("full_url_tracker_column", document.getElementById("displayFullURLTrackerColumn").checked.toString()); LocalPreferences.set("full_url_tracker_column", document.getElementById("displayFullURLTrackerColumn").checked.toString());
LocalPreferences.set("use_virtual_list", document.getElementById("useVirtualList").checked.toString()); LocalPreferences.set("use_virtual_list", document.getElementById("useVirtualList").checked.toString());
LocalPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString()); LocalPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString());