8#include <QtCore/QCoreApplication>
10#include <QtCore/QHash>
11#include <QtCore/QMetaType>
12#include <QtGui/QGuiApplication>
13#include <QtGui/QPalette>
14#include <QtGui/QStyleHints>
15#include <QtWidgets/QApplication>
20#ifndef QX_THEME_FALLBACK_ORGANIZATION
21#define QX_THEME_FALLBACK_ORGANIZATION "qtcanpool"
27const char *
const kGroup =
"ui";
28const char *
const kThemeKey =
"theme";
29const char *
const kFollowKey =
"followSystemTheme";
30const char *
const kSystemLightKey =
"systemLightTheme";
31const char *
const kSystemDarkKey =
"systemDarkTheme";
41bool detectSystemDark()
43#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
44 const Qt::ColorScheme scheme = QGuiApplication::styleHints()->colorScheme();
45 if (scheme == Qt::ColorScheme::Dark) {
48 if (scheme == Qt::ColorScheme::Light) {
52 return QGuiApplication::palette().color(QPalette::Window).lightness() < 128;
57class QxThemeManagerPrivate
61 QxThemeManagerPrivate()
69 void setupSystemHook();
79 bool m_followSystem =
false;
82 bool m_warnedMissingStyleSheet =
false;
83 QHash<int, QString> m_styleSheetFiles;
84 QHash<int, QString> m_styleSheetContent;
87void QxThemeManagerPrivate::start()
94 m_theme = detectSystemDark() ? m_systemDark : m_systemLight;
99void QxThemeManagerPrivate::setupSystemHook()
104#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
105 if (QGuiApplication::instance()) {
106 QObject::connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, q_ptr,
107 [
this](Qt::ColorScheme) {
108 q_ptr->refreshSystemTheme();
117 QString organization = QCoreApplication::organizationName();
118 if (organization.isEmpty()) {
121 QString application = QCoreApplication::applicationName();
122 if (application.isEmpty()) {
123 application = organization;
130void QxThemeManagerPrivate::reload()
133 settings->beginGroup(QString::fromLatin1(kGroup));
134 m_theme =
themeFromId(settings->stringValue(QString::fromLatin1(kThemeKey),
themeId(m_theme)), m_theme);
135 m_followSystem = settings->boolValue(QString::fromLatin1(kFollowKey), m_followSystem);
137 themeFromId(settings->stringValue(QString::fromLatin1(kSystemLightKey),
themeId(m_systemLight)), m_systemLight);
139 themeFromId(settings->stringValue(QString::fromLatin1(kSystemDarkKey),
themeId(m_systemDark)), m_systemDark);
140 settings->endGroup();
143void QxThemeManagerPrivate::persistTheme()
146 settings->beginGroup(QString::fromLatin1(kGroup));
147 settings->setValue(QString::fromLatin1(kThemeKey),
themeId(m_theme));
148 settings->endGroup();
151QxThemeManager::QxThemeManager(QObject *parent)
153 , d_ptr(new QxThemeManagerPrivate())
155 d_ptr->setPublic(
this);
156 qRegisterMetaType<QX_THEME_PREPEND_NAMESPACE(Theme)>(
"QxTheme::Theme");
169 s_instance->d_ptr->start();
188 return d->m_followSystem;
194 if (d->m_followSystem == on) {
197 d->m_followSystem = on;
216 return d->m_systemLight;
222 return d->m_systemDark;
228 d->m_systemLight = lightTheme;
229 d->m_systemDark = darkTheme;
243 return d->ensureSettings();
252 delete d->m_settings;
255 d->m_settings->setParent(
this);
264 const int key = int(
theme);
266 const QHash<int, QString>::const_iterator content = d->m_styleSheetContent.constFind(key);
267 if (content != d->m_styleSheetContent.constEnd()) {
268 return content.value();
271 const QHash<int, QString>::const_iterator
file = d->m_styleSheetFiles.constFind(key);
272 if (
file != d->m_styleSheetFiles.constEnd()) {
273 QFile reader(
file.value());
274 if (!reader.open(QIODevice::ReadOnly | QIODevice::Text)) {
275 qWarning(
"QxThemeManager: cannot read style sheet '%s'", qPrintable(
file.value()));
278 return QString::fromUtf8(reader.readAll());
287 d->m_styleSheetContent.remove(
int(
theme));
289 d->m_styleSheetFiles.remove(
int(
theme));
298 d->m_styleSheetFiles.remove(
int(
theme));
300 d->m_styleSheetContent.remove(
int(
theme));
302 d->m_styleSheetContent.insert(
int(
theme), css);
318 QApplication *app = qobject_cast<QApplication *>(QCoreApplication::instance());
327 if (css.isEmpty() && d->m_theme !=
Custom) {
332 if (!d->m_warnedMissingStyleSheet) {
333 d->m_warnedMissingStyleSheet =
true;
334 qWarning(
"QxThemeManager: no style sheet available for theme '%s'; the palette was applied on its own",
335 qPrintable(
themeId(d->m_theme)));
339 app->setStyleSheet(css);
345 if (!d->m_followSystem) {
348 const Theme target = detectSystemDark() ? d->m_systemDark : d->m_systemLight;
349 if (target == d->m_theme) {
void beginGroup(const QString &prefix)
void setValue(const QString &key, const QVariant &value)
Application-wide theme engine.
QxCore::QxSettings * settings() const
The settings object backing persistence; created on first use from the application name,...
static QxThemeManager * instance()
The process-wide instance, created on first use.
void setFollowSystemTheme(bool on)
Enables or disables following the system colour scheme.
Theme theme() const
The theme that is currently applied.
void setStyleSheet(Theme theme, const QString &css)
Supplies the style sheet of theme inline, which takes precedence over any file.
~QxThemeManager() override
void setSettings(QxCore ::QxSettings *settings)
Replaces the settings object.
bool isDark() const
Convenience wrapper around isDarkTheme(theme()).
void apply()
Re-installs the palette and style sheet of the current theme.
void refreshSystemTheme()
Re-evaluates the system colour scheme.
Theme systemLightTheme() const
Theme used while following the system and the system is light.
void themeChanged(Theme theme)
Emitted after a theme has been applied.
QString styleSheet(Theme theme) const
Style sheet that apply() would install for theme.
void setSystemThemePair(Theme lightTheme, Theme darkTheme)
Overrides the pair used while following the system.
void setStyleSheetFile(Theme theme, const QString &fileName)
Points theme at an external style sheet.
Theme systemDarkTheme() const
Theme used while following the system and the system is dark.
bool followSystemTheme() const
True when the theme follows the operating system colour scheme.
void followSystemThemeChanged(bool on)
Emitted when following the system colour scheme is toggled.
void setTheme(Theme theme)
Applies theme, persists it as the explicit choice and emits themeChanged().
bool isDarkTheme(Theme theme)
True for the themes that draw on a dark background.
Theme themeFromId(const QString &id, Theme fallback)
Resolves an id produced by themeId(); fallback is returned for unknown ids.
QString themeStyleSheet(Theme theme)
Content of the built-in style sheet, empty when the resource is missing.
QString themeId(Theme theme)
Stable identifier written to the configuration.
Theme
The themes understood by QxThemeManager.
QPalette palette(Theme theme)
Palette matching theme: the dark palette for DarkWps / DarkOfficePlus, the light one otherwise.
#define QX_DECLARE_PUBLIC(Class)
#define Q_NULLPTR
Copyleft (C) 2025 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QX_FINI_PRIVATE()
#define QX_CORE_PREPEND_NAMESPACE(name)
#define QX_THEME_BEGIN_NAMESPACE
#define QX_THEME_PREPEND_NAMESPACE(name)
#define QX_THEME_END_NAMESPACE
#define QX_THEME_FALLBACK_ORGANIZATION
Where settings land when the application never named itself.