diff --git a/.github/workflows/ci_windows.yaml b/.github/workflows/ci_windows.yaml index f891e264b..fa43a38cb 100644 --- a/.github/workflows/ci_windows.yaml +++ b/.github/workflows/ci_windows.yaml @@ -95,7 +95,8 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v4 with: - version: "6.7.3" + version: "6.8.0" + arch: win64_msvc2022_64 archives: qtbase qtsvg qttools cache: true diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9db94e77e..69c9ae1a2 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -46,6 +46,7 @@ add_library(qbt_gui STATIC autoexpandabledialog.h banlistoptionsdialog.h color.h + colorscheme.h cookiesdialog.h cookiesmodel.h deletionconfirmationdialog.h diff --git a/src/gui/colorscheme.h b/src/gui/colorscheme.h new file mode 100644 index 000000000..62dcb0d6f --- /dev/null +++ b/src/gui/colorscheme.h @@ -0,0 +1,45 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include + +inline namespace ColorSchemeNS +{ + Q_NAMESPACE + + enum class ColorScheme + { + System , + Light, + Dark + }; + + Q_ENUM_NS(ColorScheme) +} diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 7c2e7cf12..ffebfff33 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -243,6 +243,7 @@ void OptionsDialog::loadBehaviorTabOptions() setLocale(pref->getLocale()); initializeStyleCombo(); + initializeColorSchemeOptions(); m_ui->checkUseCustomTheme->setChecked(Preferences::instance()->useCustomUITheme()); m_ui->customThemeFilePath->setSelectedPath(Preferences::instance()->customUIThemePath()); @@ -359,6 +360,10 @@ void OptionsDialog::loadBehaviorTabOptions() connect(m_ui->comboStyle, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); #endif +#ifdef QBT_HAS_COLORSCHEME_OPTION + connect(m_ui->comboColorScheme, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); +#endif + #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); #endif @@ -459,6 +464,10 @@ void OptionsDialog::saveBehaviorTabOptions() const pref->setStyle(m_ui->comboStyle->currentText()); #endif +#ifdef QBT_HAS_COLORSCHEME_OPTION + UIThemeManager::instance()->setColorScheme(m_ui->comboColorScheme->currentData().value()); +#endif + #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) pref->useSystemIcons(m_ui->checkUseSystemIcon->isChecked()); #endif @@ -1704,6 +1713,22 @@ void OptionsDialog::initializeStyleCombo() #endif } +void OptionsDialog::initializeColorSchemeOptions() +{ +#ifdef QBT_HAS_COLORSCHEME_OPTION + m_ui->comboColorScheme->addItem(tr("Dark", "Dark color scheme"), QVariant::fromValue(ColorScheme::Dark)); + m_ui->comboColorScheme->addItem(tr("Light", "Light color scheme"), QVariant::fromValue(ColorScheme::Light)); + m_ui->comboColorScheme->addItem(tr("System", "System color scheme"), QVariant::fromValue(ColorScheme::System)); + m_ui->comboColorScheme->setCurrentIndex(m_ui->comboColorScheme->findData(QVariant::fromValue(UIThemeManager::instance()->colorScheme()))); +#else + m_ui->labelColorScheme->hide(); + m_ui->comboColorScheme->hide(); + m_ui->UISettingsBoxLayout->removeWidget(m_ui->labelColorScheme); + m_ui->UISettingsBoxLayout->removeWidget(m_ui->comboColorScheme); + m_ui->UISettingsBoxLayout->removeItem(m_ui->spacerColorScheme); +#endif +} + #ifdef Q_OS_WIN bool OptionsDialog::WinStartup() const { diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h index b5a6c51fc..534ca4078 100644 --- a/src/gui/optionsdialog.h +++ b/src/gui/optionsdialog.h @@ -144,6 +144,7 @@ private: // General options void initializeLanguageCombo(); void initializeStyleCombo(); + void initializeColorSchemeOptions(); QString getLocale() const; bool isSplashScreenDisabled() const; #ifdef Q_OS_WIN diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index f102622df..95429236c 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -191,7 +191,30 @@ - + + + + Color scheme: + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + Use custom UI Theme @@ -213,14 +236,14 @@ - + Use icons from system theme - + Customize UI Theme... @@ -3945,6 +3968,8 @@ Use ';' to split multiple entries. Can use wildcard '*'. tabOption tabSelection comboLanguage + comboStyle + comboColorScheme checkUseCustomTheme customThemeFilePath checkUseSystemIcon diff --git a/src/gui/uithememanager.cpp b/src/gui/uithememanager.cpp index 5157dcd9e..12b4ac68a 100644 --- a/src/gui/uithememanager.cpp +++ b/src/gui/uithememanager.cpp @@ -80,6 +80,9 @@ void UIThemeManager::initInstance() UIThemeManager::UIThemeManager() : m_useCustomTheme {Preferences::instance()->useCustomUITheme()} +#ifdef QBT_HAS_COLORSCHEME_OPTION + , m_colorSchemeSetting {u"Appearance/ColorScheme"_s} +#endif #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) , m_useSystemIcons {Preferences::instance()->useSystemIcons()} #endif @@ -90,6 +93,10 @@ UIThemeManager::UIThemeManager() LogMsg(tr("Set app style failed. Unknown style: \"%1\"").arg(styleName), Log::WARNING); #endif +#ifdef QBT_HAS_COLORSCHEME_OPTION + applyColorScheme(); +#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); @@ -125,6 +132,38 @@ UIThemeManager *UIThemeManager::instance() return m_instance; } +#ifdef QBT_HAS_COLORSCHEME_OPTION +ColorScheme UIThemeManager::colorScheme() const +{ + return m_colorSchemeSetting.get(ColorScheme::System); +} + +void UIThemeManager::setColorScheme(const ColorScheme value) +{ + if (value == colorScheme()) + return; + + m_colorSchemeSetting = value; +} + +void UIThemeManager::applyColorScheme() const +{ + switch (colorScheme()) + { + case ColorScheme::System: + default: + qApp->styleHints()->unsetColorScheme(); + break; + case ColorScheme::Light: + qApp->styleHints()->setColorScheme(Qt::ColorScheme::Light); + break; + case ColorScheme::Dark: + qApp->styleHints()->setColorScheme(Qt::ColorScheme::Dark); + break; + } +} +#endif + void UIThemeManager::applyStyleSheet() const { qApp->setStyleSheet(QString::fromUtf8(m_themeSource->readStyleSheet())); diff --git a/src/gui/uithememanager.h b/src/gui/uithememanager.h index 8c82424a4..3a2f9ad95 100644 --- a/src/gui/uithememanager.h +++ b/src/gui/uithememanager.h @@ -31,6 +31,7 @@ #pragma once #include +#include #include #include #include @@ -40,6 +41,15 @@ #include "uithemesource.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) && defined(Q_OS_WIN) +#define QBT_HAS_COLORSCHEME_OPTION +#endif + +#ifdef QBT_HAS_COLORSCHEME_OPTION +#include "base/settingvalue.h" +#include "colorscheme.h" +#endif + class UIThemeManager final : public QObject { Q_OBJECT @@ -50,6 +60,11 @@ public: static void freeInstance(); static UIThemeManager *instance(); +#ifdef QBT_HAS_COLORSCHEME_OPTION + ColorScheme colorScheme() const; + void setColorScheme(ColorScheme value); +#endif + QIcon getIcon(const QString &iconId, const QString &fallback = {}) const; QIcon getFlagIcon(const QString &countryIsoCode) const; QPixmap getScaledPixmap(const QString &iconId, int height) const; @@ -66,8 +81,15 @@ private: void applyStyleSheet() const; void onColorSchemeChanged(); +#ifdef QBT_HAS_COLORSCHEME_OPTION + void applyColorScheme() const; +#endif + static UIThemeManager *m_instance; const bool m_useCustomTheme; +#ifdef QBT_HAS_COLORSCHEME_OPTION + SettingValue m_colorSchemeSetting; +#endif #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) const bool m_useSystemIcons; #endif