Skip to content

Commit

Permalink
FEAT(client): Automatically sync theme with OS color scheme- Fix
Browse files Browse the repository at this point in the history
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 mumble-voip#6515
  • Loading branch information
jakub2682-tuke committed Oct 31, 2024
1 parent 9999da2 commit 885065d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 79 deletions.
73 changes: 1 addition & 72 deletions src/mumble/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -839,7 +768,7 @@ void MainWindow::changeEvent(QEvent *e) {
}
#endif
if (e->type() == QEvent::ThemeChange) {
applyTheme();
Themes::apply();
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/mumble/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
74 changes: 70 additions & 4 deletions src/mumble/Themes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
#include "MumbleApplication.h"
#include "Global.h"

#include <QFile>
#include <QGuiApplication>
#include <QPalette>
#include <QSettings>
#include <QStyleHints>
#include <QTextStream>


boost::optional< ThemeInfo::StyleInfo > Themes::getConfiguredStyle(const Settings &settings) {
if (settings.themeName.isEmpty() && settings.themeStyleName.isEmpty()) {
return boost::none;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
}
Expand Down
3 changes: 3 additions & 0 deletions src/mumble/Themes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down

0 comments on commit 885065d

Please sign in to comment.