From 885065df46c346dc3831aa1b9a460825bc5bbe24 Mon Sep 17 00:00:00 2001 From: jk405cz Date: Thu, 31 Oct 2024 17:49:53 +0100 Subject: [PATCH] FEAT(client): Automatically sync theme with OS color scheme- Fix This update introduces an automatic theme switch that changes based on the OS color scheme at startup and during runtime. This feature is implemented for Qt 6.2 and includes compatibility for Qt 6.5 once Mumble upgrades. Implements #6515 --- src/mumble/MainWindow.cpp | 73 +------------------------------------- src/mumble/MainWindow.h | 3 -- src/mumble/Themes.cpp | 74 ++++++++++++++++++++++++++++++++++++--- src/mumble/Themes.h | 3 ++ 4 files changed, 74 insertions(+), 79 deletions(-) diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index b53c1adce95..f66196dd3ac 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -209,77 +209,6 @@ MainWindow::MainWindow(QWidget *p) QAccessible::installFactory(AccessibleSlider::semanticSliderFactory); - applyTheme(); -} - -void MainWindow::applyTheme() { - boost::optional< ThemeInfo::StyleInfo > configuredStyle = Themes::getConfiguredStyle(Global::get().s); - - QString lightThemePath = ":/themes/Default/Lite.qss"; // Default light theme path - QString darkThemePath = ":/themes/Default/Dark.qss"; // Default dark theme path - - if (configuredStyle) { - if (configuredStyle->name == "Auto") { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - auto colorScheme = QGuiApplication::styleHints()->colorScheme(); - - if (colorScheme == Qt::ColorScheme::Dark) { - setStyleSheet(loadStyleSheet(darkThemePath)); // Apply dark theme - } else { - setStyleSheet(loadStyleSheet(lightThemePath)); // Apply light theme - } -#else - bool isDarkTheme = detectSystemTheme(); - if (isDarkTheme) { - setStyleSheet(loadStyleSheet(darkThemePath)); // Apply dark theme - } else { - setStyleSheet(loadStyleSheet(lightThemePath)); // Apply light theme - } -#endif - } else if (configuredStyle->themeName == "none") { - setStyleSheet(""); // Clear the stylesheet if "None" is selected - } else { - QString themePath = - QString(":/themes/%1/%2.qss").arg(configuredStyle->themeName).arg(configuredStyle->name); - setStyleSheet(loadStyleSheet(themePath)); // Apply the selected theme and style - } - } else { - // Handle the case where no theme is configured (fallback to default behavior) - setStyleSheet(loadStyleSheet(lightThemePath)); // Default to light theme - } -} - -bool MainWindow::detectSystemTheme() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - return false; // This should not be called for Qt 6.5 and above -#else -// Custom method to detect dark theme for Qt 6.2 and below -# ifdef Q_OS_WIN - QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", - QSettings::NativeFormat); - return settings.value("AppsUseLightTheme", 1).toInt() == 0; // 0 means dark mode -# else - // Fallback for other OSes - QByteArray platform = qgetenv("QT_QPA_PLATFORM"); - if (platform.contains("darkmode=2")) { - return true; - } else if (platform.contains("darkmode=1")) { - QPalette defaultPalette; - return defaultPalette.color(QPalette::WindowText).lightness() - > defaultPalette.color(QPalette::Window).lightness(); - } - return false; -# endif -#endif -} - -QString MainWindow::loadStyleSheet(const QString &path) { - QFile file(path); - if (file.open(QFile::ReadOnly | QFile::Text)) { - QTextStream stream(&file); - return stream.readAll(); // Return the stylesheet content - } - return QString(); // Return empty if the file cannot be loaded } void MainWindow::createActions() { @@ -839,7 +768,7 @@ void MainWindow::changeEvent(QEvent *e) { } #endif if (e->type() == QEvent::ThemeChange) { - applyTheme(); + Themes::apply(); } } diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 0e3bed2b1a5..30b9094da9b 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -220,9 +220,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { ContextMenuTarget getContextMenuTargets(); void autocompleteUsername(); - void applyTheme(); - bool detectSystemTheme(); - QString loadStyleSheet(const QString &path); public slots: void on_qmServer_aboutToShow(); diff --git a/src/mumble/Themes.cpp b/src/mumble/Themes.cpp index 4b1a121c44d..9512b1fe83b 100644 --- a/src/mumble/Themes.cpp +++ b/src/mumble/Themes.cpp @@ -8,6 +8,14 @@ #include "MumbleApplication.h" #include "Global.h" +#include +#include +#include +#include +#include +#include + + boost::optional< ThemeInfo::StyleInfo > Themes::getConfiguredStyle(const Settings &settings) { if (settings.themeName.isEmpty() && settings.themeStyleName.isEmpty()) { return boost::none; @@ -55,25 +63,59 @@ void Themes::applyFallback() { } bool Themes::applyConfigured() { + static QString currentThemePath; + + boost::optional< ThemeInfo::StyleInfo > style = Themes::getConfiguredStyle(Global::get().s); if (!style) { return false; } - const QFileInfo qssFile(style->getPlatformQss()); + QString themePath; + if (style->themeName == "Auto" || style->name == "Auto") { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + auto colorScheme = QGuiApplication::styleHints()->colorScheme(); + if (colorScheme == Qt::ColorScheme::Dark) { + themePath = ":/themes/Default/Dark.qss"; + } else { + themePath = ":/themes/Default/Lite.qss"; + } +#else + bool isDarkTheme = detectSystemTheme(); + if (isDarkTheme) { + themePath = ":/themes/Default/Dark.qss"; + } else { + themePath = ":/themes/Default/Lite.qss"; + } +#endif + } else { + if (style->name == "Dark") { + themePath = ":/themes/Default/Dark.qss"; + } else { + themePath = ":/themes/Default/Lite.qss"; + } + } + + // Early exit if the theme path is the same as the current one + if (themePath == currentThemePath) { + qWarning() << "Themes::applyConfigured(): Skipping redundant theme application for path:" << themePath; + return true; + } + + currentThemePath = themePath; // Update the current theme path qWarning() << "Theme:" << style->themeName; qWarning() << "Style:" << style->name; - qWarning() << "--> qss:" << qssFile.absoluteFilePath(); + qWarning() << "--> qss:" << themePath; - QFile file(qssFile.absoluteFilePath()); + QFile file(themePath); if (!file.open(QFile::ReadOnly)) { qWarning() << "Failed to open theme stylesheet:" << file.errorString(); return false; } QStringList skinPaths; - skinPaths << qssFile.path(); + skinPaths << QFileInfo(themePath).path(); skinPaths << QLatin1String(":/themes/Default"); // Some skins might want to fall-back on our built-in resources QString themeQss = QString::fromUtf8(file.readAll()); @@ -105,6 +147,30 @@ bool Themes::apply() { return result; } +bool Themes::detectSystemTheme() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + return false; // This should not be called for Qt 6.5 and above +#else +// Custom method to detect dark theme for Qt 6.2 and below +# ifdef Q_OS_WIN + QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + QSettings::NativeFormat); + return settings.value("AppsUseLightTheme", 1).toInt() == 0; // 0 means dark mode +# else + // Fallback for other OSes + QByteArray platform = qgetenv("QT_QPA_PLATFORM"); + if (platform.contains("darkmode=2")) { + return true; + } else if (platform.contains("darkmode=1")) { + QPalette defaultPalette; + return defaultPalette.color(QPalette::WindowText).lightness() + > defaultPalette.color(QPalette::Window).lightness(); + } + return false; +# endif +#endif +} + ThemeMap Themes::getThemes() { return ThemeInfo::scanDirectories(getSearchDirectories()); } diff --git a/src/mumble/Themes.h b/src/mumble/Themes.h index 000fd09b57c..6aa0dffceae 100644 --- a/src/mumble/Themes.h +++ b/src/mumble/Themes.h @@ -31,6 +31,9 @@ class Themes { /// @note Can only apply a theme before MainWindow etc. is opened static bool apply(); + /// Detects current OS theme + static bool detectSystemTheme(); + /// Return a theme name to theme map static ThemeMap getThemes();