Allow to set custom suffix to window title

This is to allow users to differentiate qbt instances when there are multiple running.
PR #20429.
Closes #17905.
This commit is contained in:
Chocobo1 2024-02-27 12:41:12 +08:00 committed by GitHub
parent 364bcf73ee
commit 46e8ee50c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 98 additions and 27 deletions

View File

@ -226,6 +226,7 @@ namespace
Application::Application(int &argc, char **argv)
: BaseApplication(argc, argv)
, m_commandLineArgs(parseCommandLine(Application::arguments()))
, m_storeInstanceName(SETTINGS_KEY(u"InstanceName"_s))
, m_storeFileLoggerEnabled(FILELOGGER_SETTINGS_KEY(u"Enabled"_s))
, m_storeFileLoggerBackup(FILELOGGER_SETTINGS_KEY(u"Backup"_s))
, m_storeFileLoggerDeleteOld(FILELOGGER_SETTINGS_KEY(u"DeleteOld"_s))
@ -360,6 +361,23 @@ const QBtCommandLineParameters &Application::commandLineArgs() const
return m_commandLineArgs;
}
QString Application::instanceName() const
{
return m_storeInstanceName;
}
void Application::setInstanceName(const QString &name)
{
if (name == instanceName())
return;
m_storeInstanceName = name;
#ifndef DISABLE_GUI
if (MainWindow *mw = mainWindow())
mw->setTitleSuffix(name);
#endif
}
int Application::memoryWorkingSetLimit() const
{
return m_storeMemoryWorkingSetLimit.get(512);
@ -880,7 +898,8 @@ int Application::exec()
const WindowState windowState = (m_startupProgressDialog->windowState() & Qt::WindowMinimized)
? WindowState::Minimized : WindowState::Normal;
#endif
m_window = new MainWindow(this, windowState);
m_window = new MainWindow(this, windowState, instanceName());
delete m_startupProgressDialog;
#endif // DISABLE_GUI

View File

@ -105,6 +105,9 @@ public:
bool callMainInstance();
const QBtCommandLineParameters &commandLineArgs() const;
QString instanceName() const override;
void setInstanceName(const QString &name) override;
// FileLogger properties
bool isFileLoggerEnabled() const override;
void setFileLoggerEnabled(bool value) override;
@ -194,6 +197,7 @@ private:
QList<QBtCommandLineParameters> m_paramsQueue;
SettingValue<QString> m_storeInstanceName;
SettingValue<bool> m_storeFileLoggerEnabled;
SettingValue<bool> m_storeFileLoggerBackup;
SettingValue<bool> m_storeFileLoggerDeleteOld;

View File

@ -61,6 +61,9 @@ class IApplication
public:
virtual ~IApplication() = default;
virtual QString instanceName() const = 0;
virtual void setInstanceName(const QString &name) = 0;
// FileLogger properties
virtual bool isFileLoggerEnabled() const = 0;
virtual void setFileLoggerEnabled(bool value) = 0;

View File

@ -79,6 +79,7 @@ namespace
CONFIRM_RECHECK_TORRENT,
RECHECK_COMPLETED,
// UI related
APP_INSTANCE_NAME,
LIST_REFRESH,
RESOLVE_HOSTS,
RESOLVE_COUNTRIES,
@ -280,6 +281,8 @@ void AdvancedSettings::saveAdvancedSettings() const
session->setBlockPeersOnPrivilegedPorts(m_checkBoxBlockPeersOnPrivilegedPorts.isChecked());
// Recheck torrents on completion
pref->recheckTorrentsOnCompletion(m_checkBoxRecheckCompleted.isChecked());
// Customize application instance name
app()->setInstanceName(m_lineEditAppInstanceName.text());
// Transfer list refresh interval
session->setRefreshInterval(m_spinBoxListRefresh.value());
// Peer resolution
@ -723,6 +726,10 @@ void AdvancedSettings::loadAdvancedSettings()
// Recheck completed torrents
m_checkBoxRecheckCompleted.setChecked(pref->recheckTorrentsOnCompletion());
addRow(RECHECK_COMPLETED, tr("Recheck torrents on completion"), &m_checkBoxRecheckCompleted);
// Customize application instance name
m_lineEditAppInstanceName.setText(app()->instanceName());
m_lineEditAppInstanceName.setToolTip(tr("It appends the text to the window title to help distinguish qBittorent instances"));
addRow(APP_INSTANCE_NAME, tr("Customize application instance name"), &m_lineEditAppInstanceName);
// Refresh interval
m_spinBoxListRefresh.setMinimum(30);
m_spinBoxListRefresh.setMaximum(99999);

View File

@ -82,7 +82,7 @@ private:
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport, m_checkBoxConfirmRemoveTrackerFromAllTorrents;
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage;
QLineEdit m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
QLineEdit m_lineEditAppInstanceName, m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
#ifndef QBT_USES_LIBTORRENT2
QSpinBox m_spinBoxCache, m_spinBoxCacheTTL;

View File

@ -122,7 +122,7 @@ namespace
}
}
MainWindow::MainWindow(IGUIApplication *app, WindowState initialState)
MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, const QString &titleSuffix)
: GUIApplicationComponent(app)
, m_ui(new Ui::MainWindow)
, m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_s))
@ -134,9 +134,10 @@ MainWindow::MainWindow(IGUIApplication *app, WindowState initialState)
{
m_ui->setupUi(this);
setTitleSuffix(titleSuffix);
Preferences *const pref = Preferences::instance();
m_uiLocked = pref->isUILocked();
setWindowTitle(QStringLiteral("qBittorrent " QBT_VERSION));
m_displaySpeedInTitle = pref->speedInTitleBar();
// Setting icons
#ifndef Q_OS_MACOS
@ -524,6 +525,16 @@ void MainWindow::setDownloadTrackerFavicon(const bool value)
m_storeDownloadTrackerFavicon = value;
}
void MainWindow::setTitleSuffix(const QString &suffix)
{
const auto emDash = QChar(0x2014);
const QString separator = u' ' + emDash + u' ';
m_windowTitle = QStringLiteral("qBittorrent " QBT_VERSION)
+ (!suffix.isEmpty() ? (separator + suffix) : QString());
setWindowTitle(m_windowTitle);
}
void MainWindow::addToolbarContextMenu()
{
const Preferences *const pref = Preferences::instance();
@ -1485,23 +1496,24 @@ void MainWindow::loadPreferences()
void MainWindow::reloadSessionStats()
{
const BitTorrent::SessionStatus &status = BitTorrent::Session::instance()->status();
const QString downloadRate = Utils::Misc::friendlyUnit(status.payloadDownloadRate, true);
const QString uploadRate = Utils::Misc::friendlyUnit(status.payloadUploadRate, true);
// update global information
#ifdef Q_OS_MACOS
m_badger->updateSpeed(status.payloadDownloadRate, status.payloadUploadRate);
#else
const auto toolTip = u"%1\n%2"_s.arg(
tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true))
, tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true)));
tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(downloadRate)
, tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(uploadRate));
app()->desktopIntegration()->setToolTip(toolTip); // tray icon
#endif // Q_OS_MACOS
if (m_displaySpeedInTitle)
{
setWindowTitle(tr("[D: %1, U: %2] qBittorrent %3", "D = Download; U = Upload; %3 is qBittorrent version")
.arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true)
, Utils::Misc::friendlyUnit(status.payloadUploadRate, true)
, QStringLiteral(QBT_VERSION)));
const QString title = tr("[D: %1, U: %2] %3", "D = Download; U = Upload; %3 is the rest of the window title")
.arg(downloadRate, uploadRate, m_windowTitle);
setWindowTitle(title);
}
}
@ -1621,7 +1633,7 @@ void MainWindow::on_actionSpeedInTitleBar_triggered()
if (m_displaySpeedInTitle)
reloadSessionStats();
else
setWindowTitle(QStringLiteral("qBittorrent " QBT_VERSION));
setWindowTitle(m_windowTitle);
}
void MainWindow::on_actionRSSReader_triggered()

View File

@ -84,7 +84,7 @@ class MainWindow final : public GUIApplicationComponent<QMainWindow>
Q_DISABLE_COPY_MOVE(MainWindow)
public:
explicit MainWindow(IGUIApplication *app, WindowState initialState = WindowState::Normal);
explicit MainWindow(IGUIApplication *app, WindowState initialState = WindowState::Normal, const QString &titleSuffix = {});
~MainWindow() override;
QWidget *currentTabWidget() const;
@ -97,12 +97,12 @@ public:
Log::MsgTypes executionLogMsgTypes() const;
void setExecutionLogMsgTypes(Log::MsgTypes value);
// Notifications properties
// Misc properties
bool isDownloadTrackerFavicon() const;
void setDownloadTrackerFavicon(bool value);
void setTitleSuffix(const QString &suffix);
void activate();
void cleanup();
@ -207,6 +207,7 @@ private:
QFileSystemWatcher *m_executableWatcher = nullptr;
// GUI related
QString m_windowTitle;
bool m_posInitialized = false;
bool m_neverShown = true;
QPointer<QTabWidget> m_tabs;

View File

@ -361,6 +361,8 @@ void AppController::preferencesAction()
data[u"torrent_file_size_limit"_s] = pref->getTorrentFileSizeLimit();
// Recheck completed torrents
data[u"recheck_completed_torrents"_s] = pref->recheckTorrentsOnCompletion();
// Customize application instance name
data[u"app_instance_name"_s] = app()->instanceName();
// Refresh interval
data[u"refresh_interval"_s] = session->refreshInterval();
// Resolve peer countries
@ -943,6 +945,9 @@ void AppController::setPreferencesAction()
// Recheck completed torrents
if (hasKey(u"recheck_completed_torrents"_s))
pref->recheckTorrentsOnCompletion(it.value().toBool());
// Customize application instance name
if (hasKey(u"app_instance_name"_s))
app()->setInstanceName(it.value().toString());
// Refresh interval
if (hasKey(u"refresh_interval"_s))
session->setRefreshInterval(it.value().toInt());

View File

@ -53,7 +53,7 @@
#include "base/utils/version.h"
#include "api/isessionmanager.h"
inline const Utils::Version<3, 2> API_VERSION {2, 10, 3};
inline const Utils::Version<3, 2> API_VERSION {2, 10, 4};
class QTimer;

View File

@ -241,7 +241,7 @@ a.propButton img {
.contextMenu li a {
color: var(--color-text-default);
display: block;
font-family: tahoma, arial, sans-serif;
font-family: Tahoma, Arial, sans-serif;
font-size: 12px;
padding: 5px 20px 5px 5px;
text-decoration: none;

View File

@ -36,7 +36,8 @@ window.qBittorrent.Client = (() => {
genHash: genHash,
getSyncMainDataInterval: getSyncMainDataInterval,
isStopped: isStopped,
stop: stop
stop: stop,
mainTitle: mainTitle
};
};
@ -67,6 +68,15 @@ window.qBittorrent.Client = (() => {
stopped = true;
};
const mainTitle = () => {
const emDash = '\u2014';
const qbtVersion = window.qBittorrent.Cache.qbtVersion.get();
const suffix = window.qBittorrent.Cache.preferences.get()['app_instance_name'] || '';
const title = `qBittorrent ${qbtVersion} QBT_TR(WebUI)QBT_TR[CONTEXT=OptionsDialog]`
+ ((suffix.length > 0) ? ` ${emDash} ${suffix}` : '');
return title;
};
return exports();
})();
Object.freeze(window.qBittorrent.Client);
@ -275,7 +285,7 @@ window.addEventListener("DOMContentLoaded", function() {
initializeWindows();
// Show Top Toolbar is enabled by default
let showTopToolbar = LocalPreferences.get('show_top_toolbar', 'true') == "true";
let showTopToolbar = LocalPreferences.get('show_top_toolbar', 'true') === "true";
if (!showTopToolbar) {
$('showTopToolbarLink').firstChild.style.opacity = '0';
$('mochaToolbar').addClass('invisible');
@ -296,7 +306,7 @@ window.addEventListener("DOMContentLoaded", function() {
$('filtersColumn_handle').addClass('invisible');
}
let speedInTitle = LocalPreferences.get('speed_in_browser_title_bar') == "true";
let speedInTitle = LocalPreferences.get('speed_in_browser_title_bar') === "true";
if (!speedInTitle)
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
@ -873,14 +883,13 @@ window.addEventListener("DOMContentLoaded", function() {
transfer_info += " (" + window.qBittorrent.Misc.friendlyUnit(serverState.up_info_data, false) + ")";
$("UpInfos").set('html', transfer_info);
const qbtVersion = window.qBittorrent.Cache.qbtVersion.get();
document.title = (speedInTitle
? (`QBT_TR([D: %1, U: %2])QBT_TR[CONTEXT=MainWindow] `
.replace("%1", window.qBittorrent.Misc.friendlyUnit(serverState.dl_info_speed, true))
.replace("%2", window.qBittorrent.Misc.friendlyUnit(serverState.up_info_speed, true)))
: '')
+ window.qBittorrent.Client.mainTitle();
if (speedInTitle) {
document.title = "QBT_TR([D: %1, U: %2] qBittorrent %3)QBT_TR[CONTEXT=MainWindow]".replace("%1", window.qBittorrent.Misc.friendlyUnit(serverState.dl_info_speed, true)).replace("%2", window.qBittorrent.Misc.friendlyUnit(serverState.up_info_speed, true)).replace("%3", qbtVersion);
document.title += " QBT_TR(Web UI)QBT_TR[CONTEXT=OptionsDialog]";
}
else
document.title = ("qBittorrent " + qbtVersion + " QBT_TR(Web UI)QBT_TR[CONTEXT=OptionsDialog]");
$('freeSpaceOnDisk').set('html', 'QBT_TR(Free space: %1)QBT_TR[CONTEXT=HttpServer]'.replace("%1", window.qBittorrent.Misc.friendlyUnit(serverState.free_space_on_disk)));
$('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR[CONTEXT=StatusBar]'.replace("%1", serverState.dht_nodes));

View File

@ -1157,7 +1157,8 @@ const initializeWindows = function() {
url: 'api/v2/app/shutdown',
method: 'post',
onSuccess: function() {
document.write('<!doctype html><html lang="${LANG}"><head> <meta charset="UTF-8"> <meta name="color-scheme" content="light dark" /> <title>QBT_TR(qBittorrent has been shutdown)QBT_TR[CONTEXT=HttpServer]</title></head><body> <h1 style="text-align: center;">QBT_TR(qBittorrent has been shutdown)QBT_TR[CONTEXT=HttpServer]</h1></body></html>');
const shutdownMessage = 'QBT_TR(%1 has been shutdown)QBT_TR[CONTEXT=HttpServer]'.replace("%1", window.qBittorrent.Client.mainTitle());
document.write(`<!doctype html><html lang="${LANG}"><head> <meta charset="UTF-8"> <meta name="color-scheme" content="light dark"> <title>${shutdownMessage}</title> <style>* {font-family: Arial, Helvetica, sans-serif;}</style></head><body> <h1 style="text-align: center;">${shutdownMessage}</h1></body></html>`);
document.close();
window.stop();
window.qBittorrent.Client.stop();

View File

@ -1043,6 +1043,14 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
<input type="checkbox" id="recheckTorrentsOnCompletion">
</td>
</tr>
<tr>
<td>
<label for="appInstanceName">QBT_TR(Customize application instance name:)QBT_TR[CONTEXT=OptionsDialog]</label>
</td>
<td>
<input type="text" id="appInstanceName" style="width: 15em;" title="QBT_TR(It appends the text to the window title to help distinguish qBittorent instances)QBT_TR[CONTEXT=OptionsDialog]" />
</td>
</tr>
<tr>
<td>
<label for="refreshInterval">QBT_TR(Refresh interval:)QBT_TR[CONTEXT=OptionsDialog]</label>
@ -2301,6 +2309,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
$('saveResumeDataInterval').setProperty('value', pref.save_resume_data_interval);
$('torrentFileSizeLimit').setProperty('value', (pref.torrent_file_size_limit / 1024 / 1024));
$('recheckTorrentsOnCompletion').setProperty('checked', pref.recheck_completed_torrents);
$('appInstanceName').setProperty('value', pref.app_instance_name);
$('refreshInterval').setProperty('value', pref.refresh_interval);
$('resolvePeerCountries').setProperty('checked', pref.resolve_peer_countries);
$('reannounceWhenAddressChanged').setProperty('checked', pref.reannounce_when_address_changed);
@ -2746,6 +2755,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
settings['save_resume_data_interval'] = Number($('saveResumeDataInterval').getProperty('value'));
settings['torrent_file_size_limit'] = ($('torrentFileSizeLimit').getProperty('value') * 1024 * 1024);
settings['recheck_completed_torrents'] = $('recheckTorrentsOnCompletion').getProperty('checked');
settings['app_instance_name'] = $('appInstanceName').getProperty('value');
settings['refresh_interval'] = Number($('refreshInterval').getProperty('value'));
settings['resolve_peer_countries'] = $('resolvePeerCountries').getProperty('checked');
settings['reannounce_when_address_changed'] = $('reannounceWhenAddressChanged').getProperty('checked');