diff --git a/.github/workflows/ci_windows.yaml b/.github/workflows/ci_windows.yaml index 3cb3b4a0c..f891e264b 100644 --- a/.github/workflows/ci_windows.yaml +++ b/.github/workflows/ci_windows.yaml @@ -95,7 +95,7 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v4 with: - version: "6.7.0" + version: "6.7.3" archives: qtbase qtsvg qttools cache: true diff --git a/src/app/main.cpp b/src/app/main.cpp index 53455f99b..d80629f44 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014-2023 Vladimir Golovnev + * Copyright (C) 2014-2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -58,10 +58,6 @@ #include #include -#ifdef Q_OS_WIN -#include -#endif - #ifdef QBT_STATIC_QT #include Q_IMPORT_PLUGIN(QICOPlugin) @@ -189,11 +185,6 @@ int main(int argc, char *argv[]) // We must save it here because QApplication constructor may change it const bool isOneArg = (argc == 2); -#if !defined(DISABLE_GUI) && defined(Q_OS_WIN) - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) - QApplication::setStyle(u"Fusion"_s); -#endif - // `app` must be declared out of try block to allow display message box in case of exception std::unique_ptr app; try diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index ec2fe2493..2f5ba7939 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -429,6 +429,19 @@ void Preferences::setWinStartup(const bool b) settings.remove(profileID); } } + +QString Preferences::getStyle() const +{ + return value(u"Appearance/Style"_s); +} + +void Preferences::setStyle(const QString &styleName) +{ + if (styleName == getStyle()) + return; + + setValue(u"Appearance/Style"_s, styleName); +} #endif // Q_OS_WIN // Downloads diff --git a/src/base/preferences.h b/src/base/preferences.h index a0ee4d0af..9bdb8bf13 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -130,6 +130,8 @@ public: #ifdef Q_OS_WIN bool WinStartup() const; void setWinStartup(bool b); + QString getStyle() const; + void setStyle(const QString &styleName); #endif // Downloads diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 264018117..d642fab1b 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -44,6 +44,10 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif + #include "base/bittorrent/session.h" #include "base/bittorrent/sharelimitaction.h" #include "base/exceptions.h" @@ -236,6 +240,8 @@ void OptionsDialog::loadBehaviorTabOptions() initializeLanguageCombo(); setLocale(pref->getLocale()); + initializeStyleCombo(); + m_ui->checkUseCustomTheme->setChecked(Preferences::instance()->useCustomUITheme()); m_ui->customThemeFilePath->setSelectedPath(Preferences::instance()->customUIThemePath()); m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen); @@ -345,7 +351,11 @@ void OptionsDialog::loadBehaviorTabOptions() m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled()); - connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + connect(m_ui->comboLanguage, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + +#ifdef Q_OS_WIN + connect(m_ui->comboStyle, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); +#endif #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -443,6 +453,10 @@ void OptionsDialog::saveBehaviorTabOptions() const } pref->setLocale(locale); +#ifdef Q_OS_WIN + pref->setStyle(m_ui->comboStyle->currentText()); +#endif + #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) pref->useSystemIcons(m_ui->checkUseSystemIcon->isChecked()); #endif @@ -1385,7 +1399,7 @@ void OptionsDialog::initializeLanguageCombo() for (const QString &langFile : langFiles) { const QString langCode = QStringView(langFile).sliced(12).chopped(3).toString(); // remove "qbittorrent_" and ".qm" - m_ui->comboI18n->addItem(Utils::Misc::languageToLocalizedString(langCode), langCode); + m_ui->comboLanguage->addItem(Utils::Misc::languageToLocalizedString(langCode), langCode); } } @@ -1670,6 +1684,30 @@ bool OptionsDialog::isSplashScreenDisabled() const return !m_ui->checkShowSplash->isChecked(); } +void OptionsDialog::initializeStyleCombo() +{ +#ifdef Q_OS_WIN + const QString prefStyleName = Preferences::instance()->getStyle(); + const QString selectedStyleName = prefStyleName.isEmpty() ? QApplication::style()->name() : prefStyleName; + QStringList styleNames = QStyleFactory::keys(); + for (qsizetype i = 1, stylesCount = styleNames.size(); i < stylesCount; ++i) + { + if (selectedStyleName.compare(styleNames.at(i), Qt::CaseInsensitive) == 0) + { + styleNames.swapItemsAt(0, i); + break; + } + } + m_ui->comboStyle->addItems(styleNames); +#else + m_ui->labelStyle->hide(); + m_ui->comboStyle->hide(); + m_ui->UISettingsBoxLayout->removeWidget(m_ui->labelStyle); + m_ui->UISettingsBoxLayout->removeWidget(m_ui->comboStyle); + m_ui->UISettingsBoxLayout->removeItem(m_ui->spacerStyle); +#endif +} + #ifdef Q_OS_WIN bool OptionsDialog::WinStartup() const { @@ -1719,7 +1757,7 @@ QString OptionsDialog::getProxyPassword() const // Locale Settings QString OptionsDialog::getLocale() const { - return m_ui->comboI18n->itemData(m_ui->comboI18n->currentIndex(), Qt::UserRole).toString(); + return m_ui->comboLanguage->itemData(m_ui->comboLanguage->currentIndex(), Qt::UserRole).toString(); } void OptionsDialog::setLocale(const QString &localeStr) @@ -1744,7 +1782,7 @@ void OptionsDialog::setLocale(const QString &localeStr) name = locale.name(); } // Attempt to find exact match - int index = m_ui->comboI18n->findData(name, Qt::UserRole); + int index = m_ui->comboLanguage->findData(name, Qt::UserRole); if (index < 0) { //Attempt to find a language match without a country @@ -1752,16 +1790,16 @@ void OptionsDialog::setLocale(const QString &localeStr) if (pos > -1) { QString lang = name.left(pos); - index = m_ui->comboI18n->findData(lang, Qt::UserRole); + index = m_ui->comboLanguage->findData(lang, Qt::UserRole); } } if (index < 0) { // Unrecognized, use US English - index = m_ui->comboI18n->findData(u"en"_s, Qt::UserRole); + index = m_ui->comboLanguage->findData(u"en"_s, Qt::UserRole); Q_ASSERT(index >= 0); } - m_ui->comboI18n->setCurrentIndex(index); + m_ui->comboLanguage->setCurrentIndex(index); } Path OptionsDialog::getTorrentExportDir() const diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h index 6cb66ea05..b5a6c51fc 100644 --- a/src/gui/optionsdialog.h +++ b/src/gui/optionsdialog.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2023 Vladimir Golovnev + * Copyright (C) 2023-2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -143,6 +143,7 @@ private: // General options void initializeLanguageCombo(); + void initializeStyleCombo(); QString getLocale() const; bool isSplashScreenDisabled() const; #ifdef Q_OS_WIN diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index ef4315773..f102622df 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -132,9 +132,9 @@ Interface - + - + true @@ -146,17 +146,17 @@ - + Language: - + - + Qt::Orientation::Horizontal @@ -168,7 +168,30 @@ - + + + + Style: + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + Use custom UI Theme @@ -190,14 +213,14 @@ - + Use icons from system theme - + Customize UI Theme... @@ -3921,7 +3944,7 @@ Use ';' to split multiple entries. Can use wildcard '*'. tabOption tabSelection - comboI18n + comboLanguage checkUseCustomTheme customThemeFilePath checkUseSystemIcon diff --git a/src/gui/uithememanager.cpp b/src/gui/uithememanager.cpp index d27a91c78..5157dcd9e 100644 --- a/src/gui/uithememanager.cpp +++ b/src/gui/uithememanager.cpp @@ -37,6 +37,10 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif + #include "base/global.h" #include "base/logger.h" #include "base/path.h" @@ -80,6 +84,12 @@ UIThemeManager::UIThemeManager() , m_useSystemIcons {Preferences::instance()->useSystemIcons()} #endif { +#ifdef Q_OS_WIN + const QString defaultStyle = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) ? u"Fusion"_s : QString(); + if (const QString styleName = Preferences::instance()->getStyle(); !QApplication::setStyle(styleName.isEmpty() ? defaultStyle : styleName)) + LogMsg(tr("Set app style failed. Unknown style: \"%1\"").arg(styleName), Log::WARNING); +#endif + // NOTE: Qt::QueuedConnection can be omitted as soon as support for Qt 6.5 is dropped connect(QApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &UIThemeManager::onColorSchemeChanged, Qt::QueuedConnection);