From be5a1046ac89bdbf522efea45354e49193b98132 Mon Sep 17 00:00:00 2001 From: jk405cz Date: Mon, 28 Oct 2024 19:22:30 +0100 Subject: [PATCH 1/5] FEAT(client): Automatically sync theme with OS color scheme 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 | 91 ++++++++++++++++++++++++++++++++++++++- src/mumble/MainWindow.h | 4 ++ themes/Default/theme.ini | 6 ++- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index 4f511bb10ab..fdeea97d189 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -86,6 +86,16 @@ # include #endif + +#include +#include +#include +#include +#include +#include + + + #include MessageBoxEvent::MessageBoxEvent(QString m) : QEvent(static_cast< QEvent::Type >(MB_QEVENT)) { @@ -117,7 +127,9 @@ MainWindow::MainWindow(QWidget *p) else SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); #else - { SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); } + { + SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); + } // Set application icon except on MacOSX, where the window-icon // shown in the title-bar usually serves as a draggable version of the @@ -198,8 +210,81 @@ MainWindow::MainWindow(QWidget *p) &PluginManager::on_serverSynchronized); QAccessible::installFactory(AccessibleSlider::semanticSliderFactory); + + // Theme application + applyTheme(); // Apply the light or dark theme during initialization } +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() { gsPushTalk = new GlobalShortcut(this, GlobalShortcutType::PushToTalk, tr("Push-to-Talk", "Global Shortcut")); gsPushTalk->setObjectName(QLatin1String("PushToTalk")); @@ -756,6 +841,10 @@ void MainWindow::changeEvent(QEvent *e) { QTimer::singleShot(0, this, SLOT(hide())); } #endif + + if (e->type() == QEvent::ThemeChange) { + applyTheme(); + } } void MainWindow::keyPressEvent(QKeyEvent *e) { diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 30b9094da9b..e296f4cca8a 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -221,6 +221,10 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { void autocompleteUsername(); + void applyTheme(); + bool detectSystemTheme(); + QString loadStyleSheet(const QString &path); + public slots: void on_qmServer_aboutToShow(); void on_qaServerConnect_triggered(bool autoconnect = false); diff --git a/themes/Default/theme.ini b/themes/Default/theme.ini index 89b91ff52af..89660bdedbc 100644 --- a/themes/Default/theme.ini +++ b/themes/Default/theme.ini @@ -1,6 +1,6 @@ [theme] name=Mumble -styles=dark,lite +styles=dark,lite,auto [dark] name=Dark qss=Dark.qss @@ -9,3 +9,7 @@ qss_MAC=OSX Dark.qss name=Lite qss=Lite.qss qss_MAC=OSX Lite.qss +[auto] +name=Auto +qss=Lite.qss +qss_MAC=OSX Lite.qss From 9999da228531215c10d786b41f2913609a6cf890 Mon Sep 17 00:00:00 2001 From: jk405cz Date: Tue, 29 Oct 2024 12:22:10 +0100 Subject: [PATCH 2/5] FEAT(client): Automatically sync theme with OS color scheme 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. -fix Implements #6515 --- src/mumble/MainWindow.cpp | 10 +++------- src/mumble/MainWindow.h | 1 - 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index fdeea97d189..b53c1adce95 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -127,9 +127,7 @@ MainWindow::MainWindow(QWidget *p) else SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); #else - { - SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); - } + { SvgIcon::addSvgPixmapsToIcon(qiIcon, QLatin1String("skin:mumble.svg")); } // Set application icon except on MacOSX, where the window-icon // shown in the title-bar usually serves as a draggable version of the @@ -211,8 +209,7 @@ MainWindow::MainWindow(QWidget *p) QAccessible::installFactory(AccessibleSlider::semanticSliderFactory); - // Theme application - applyTheme(); // Apply the light or dark theme during initialization + applyTheme(); } void MainWindow::applyTheme() { @@ -225,6 +222,7 @@ void MainWindow::applyTheme() { 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 { @@ -284,7 +282,6 @@ QString MainWindow::loadStyleSheet(const QString &path) { return QString(); // Return empty if the file cannot be loaded } - void MainWindow::createActions() { gsPushTalk = new GlobalShortcut(this, GlobalShortcutType::PushToTalk, tr("Push-to-Talk", "Global Shortcut")); gsPushTalk->setObjectName(QLatin1String("PushToTalk")); @@ -841,7 +838,6 @@ void MainWindow::changeEvent(QEvent *e) { QTimer::singleShot(0, this, SLOT(hide())); } #endif - if (e->type() == QEvent::ThemeChange) { applyTheme(); } diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index e296f4cca8a..0e3bed2b1a5 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -220,7 +220,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { ContextMenuTarget getContextMenuTargets(); void autocompleteUsername(); - void applyTheme(); bool detectSystemTheme(); QString loadStyleSheet(const QString &path); From 885065df46c346dc3831aa1b9a460825bc5bbe24 Mon Sep 17 00:00:00 2001 From: jk405cz Date: Thu, 31 Oct 2024 17:49:53 +0100 Subject: [PATCH 3/5] 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(); From ab72ac2e422a73b8d9a8cb71b3fe370936016e19 Mon Sep 17 00:00:00 2001 From: jk405cz Date: Fri, 1 Nov 2024 12:16:02 +0100 Subject: [PATCH 4/5] FEAT(client): Automatically sync theme with OS color scheme- v4 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 | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index f66196dd3ac..0cf75b1e294 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -86,16 +86,6 @@ # include #endif - -#include -#include -#include -#include -#include -#include - - - #include MessageBoxEvent::MessageBoxEvent(QString m) : QEvent(static_cast< QEvent::Type >(MB_QEVENT)) { @@ -208,7 +198,6 @@ MainWindow::MainWindow(QWidget *p) &PluginManager::on_serverSynchronized); QAccessible::installFactory(AccessibleSlider::semanticSliderFactory); - } void MainWindow::createActions() { From fcc5edd25533f2ddeb866f6d0a07d95daade20ef Mon Sep 17 00:00:00 2001 From: jk405cz Date: Fri, 1 Nov 2024 19:24:42 +0100 Subject: [PATCH 5/5] FEAT(client): Automatically sync theme with OS color scheme- v5 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/Themes.cpp | 27 ++++++--------------------- src/mumble/Themes.h | 3 +++ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/mumble/Themes.cpp b/src/mumble/Themes.cpp index 9512b1fe83b..5a4d08a27ef 100644 --- a/src/mumble/Themes.cpp +++ b/src/mumble/Themes.cpp @@ -13,8 +13,8 @@ #include #include #include -#include +QString Themes::currentThemePath; boost::optional< ThemeInfo::StyleInfo > Themes::getConfiguredStyle(const Settings &settings) { if (settings.themeName.isEmpty() && settings.themeStyleName.isEmpty()) { @@ -63,37 +63,23 @@ void Themes::applyFallback() { } bool Themes::applyConfigured() { - static QString currentThemePath; - + boost::optional< ThemeInfo::StyleInfo > style = Themes::getConfiguredStyle(Global::get().s); if (!style) { return false; } - QString themePath; + QString themePath = style->getPlatformQss().absoluteFilePath(); 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 @@ -149,9 +135,8 @@ bool Themes::apply() { 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 + return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; #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); diff --git a/src/mumble/Themes.h b/src/mumble/Themes.h index 6aa0dffceae..93eae62739a 100644 --- a/src/mumble/Themes.h +++ b/src/mumble/Themes.h @@ -66,6 +66,9 @@ class Themes { /// If a the file is is available, the function returns true. /// If no file is available, it returns false. static bool readStylesheet(const QString &stylesheetFn, QString &stylesheetContent); + + /// Member variable to store the current theme path + static QString currentThemePath; }; #endif // MUMBLE_MUMBLE_THEMES_H_