From c32ee8e5b54b219ee8afa716cb4cc1b58b2aba72 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 27 Jan 2024 11:53:19 +0100 Subject: [PATCH] feat: add system theme on Qt 6.5 and up (#5118) * feat: add system theme on Qt 6.5 * chroe: add changelog entry * refactor: add separate settings * fix: qt 5 * Update changelog entry --------- Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/singletons/Theme.cpp | 47 +++++++++++++++- src/singletons/Theme.hpp | 6 +++ src/widgets/settingspages/GeneralPage.cpp | 53 +++++++++++++++---- src/widgets/settingspages/GeneralPageView.hpp | 9 ++++ 5 files changed, 105 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b91e9e3d7..c4773ba81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - Minor: Normalized the input padding between light & dark themes. (#5095) - Minor: Add `--activate ` (or `-a`) command line option to activate or add a Twitch channel. (#5111) - Minor: Chatters from recent-messages are now added to autocompletion. (#5116) +- Minor: Added a _System_ theme that updates according to the system's color scheme (requires Qt 6.5). (#5118) - Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130) - Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840) - Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848) diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp index ba7cbf63e..e89c31f96 100644 --- a/src/singletons/Theme.cpp +++ b/src/singletons/Theme.cpp @@ -6,6 +6,7 @@ #include "common/QLogging.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" +#include "singletons/WindowManager.hpp" #include #include @@ -13,6 +14,9 @@ #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +# include +#endif #include @@ -219,6 +223,11 @@ bool Theme::isLightTheme() const return this->isLight_; } +bool Theme::isSystemTheme() const +{ + return this->themeName == u"System"_s; +} + void Theme::initialize(Settings &settings, const Paths &paths) { this->themeName.connect( @@ -227,15 +236,51 @@ void Theme::initialize(Settings &settings, const Paths &paths) this->update(); }, false); + auto updateIfSystem = [this](const auto &) { + if (this->isSystemTheme()) + { + this->update(); + } + }; + this->darkSystemThemeName.connect(updateIfSystem, false); + this->lightSystemThemeName.connect(updateIfSystem, false); this->loadAvailableThemes(paths); +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + QObject::connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, + &this->lifetime_, [this] { + if (this->isSystemTheme()) + { + this->update(); + getIApp()->getWindows()->forceLayoutChannelViews(); + } + }); +#endif + this->update(); } void Theme::update() { - auto oTheme = this->findThemeByKey(this->themeName); + auto currentTheme = [&]() -> QString { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + if (this->isSystemTheme()) + { + switch (qApp->styleHints()->colorScheme()) + { + case Qt::ColorScheme::Light: + return this->lightSystemThemeName; + case Qt::ColorScheme::Unknown: + case Qt::ColorScheme::Dark: + return this->darkSystemThemeName; + } + } +#endif + return this->themeName; + }; + + auto oTheme = this->findThemeByKey(currentTheme()); constexpr const double nsToMs = 1.0 / 1000000.0; QElapsedTimer timer; diff --git a/src/singletons/Theme.hpp b/src/singletons/Theme.hpp index 034a01b64..d67ff8a9e 100644 --- a/src/singletons/Theme.hpp +++ b/src/singletons/Theme.hpp @@ -46,6 +46,7 @@ public: void initialize(Settings &settings, const Paths &paths) final; bool isLightTheme() const; + bool isSystemTheme() const; struct TabColors { QColor text; @@ -153,6 +154,9 @@ public: pajlada::Signals::NoArgSignal updated; QStringSetting themeName{"/appearance/theme/name", "Dark"}; + QStringSetting lightSystemThemeName{"/appearance/theme/lightSystem", + "Light"}; + QStringSetting darkSystemThemeName{"/appearance/theme/darkSystem", "Dark"}; private: bool isLight_ = false; @@ -164,6 +168,8 @@ private: // This will only be populated when auto-reloading themes QJsonObject currentThemeJson_; + QObject lifetime_; + /** * Figure out which themes are available in the Themes directory * diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 7f414e81b..3c7938f46 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -122,16 +122,49 @@ void GeneralPage::initLayout(GeneralPageView &layout) layout.addTitle("Interface"); - layout.addDropdown( - "Theme", getIApp()->getThemes()->availableThemes(), - getIApp()->getThemes()->themeName, - [](const auto *combo, const auto &themeKey) { - return combo->findData(themeKey, Qt::UserRole); - }, - [](const auto &args) { - return args.combobox->itemData(args.index, Qt::UserRole).toString(); - }, - {}, Theme::fallbackTheme.name); + { + auto *themes = getIApp()->getThemes(); + auto available = themes->availableThemes(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + available.emplace_back("System", "System"); +#endif + + auto addThemeDropdown = [&](auto name, auto &setting, + const auto &options, + const QString &tooltip = {}) { + return layout.addDropdown( + name, options, setting, + [](const auto *combo, const auto &themeKey) { + return combo->findData(themeKey, Qt::UserRole); + }, + [](const auto &args) { + return args.combobox->itemData(args.index, Qt::UserRole) + .toString(); + }, + tooltip, Theme::fallbackTheme.name); + }; + + addThemeDropdown("Theme", themes->themeName, available); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + auto *darkDropdown = addThemeDropdown( + "Dark system theme", themes->darkSystemThemeName, + themes->availableThemes(), + "This theme is selected if your system is in a dark theme and you " + "enabled the adaptive 'System' theme."); + auto *lightDropdown = addThemeDropdown( + "Light system theme", themes->lightSystemThemeName, + themes->availableThemes(), + "This theme is selected if your system is in a light theme and you " + "enabled the adaptive 'System' theme."); + + auto isSystem = [](const auto &s) { + return s == "System"; + }; + layout.enableIf(darkDropdown, themes->themeName, isSystem); + layout.enableIf(lightDropdown, themes->themeName, isSystem); +#endif + } layout.addDropdown( "Font", {"Segoe UI", "Arial", "Choose..."}, diff --git a/src/widgets/settingspages/GeneralPageView.hpp b/src/widgets/settingspages/GeneralPageView.hpp index fce52077b..9e10e5cf3 100644 --- a/src/widgets/settingspages/GeneralPageView.hpp +++ b/src/widgets/settingspages/GeneralPageView.hpp @@ -306,6 +306,15 @@ public: return combo; } + void enableIf(QComboBox *widget, auto &setting, auto cb) + { + auto updateVisibility = [cb = std::move(cb), &setting, widget]() { + auto enabled = cb(setting.getValue()); + widget->setEnabled(enabled); + }; + setting.connect(updateVisibility, this->managedConnections_); + } + DescriptionLabel *addDescription(const QString &text); void addSeperator();