QtCanpool 3.3.0
载入中...
搜索中...
未找到
qxthememanager.cpp
浏览该文件的文档.
1
5#include "qxthememanager.h"
6#include "qxthemepalette.h"
7
8#include <QtCore/QCoreApplication>
9#include <QtCore/QFile>
10#include <QtCore/QHash>
11#include <QtCore/QMetaType>
12#include <QtGui/QGuiApplication>
13#include <QtGui/QPalette>
14#include <QtGui/QStyleHints>
15#include <QtWidgets/QApplication>
16
18
20#ifndef QX_THEME_FALLBACK_ORGANIZATION
21#define QX_THEME_FALLBACK_ORGANIZATION "qtcanpool"
22#endif
23
24namespace
25{
26
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";
32
41bool detectSystemDark()
42{
43#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
44 const Qt::ColorScheme scheme = QGuiApplication::styleHints()->colorScheme();
45 if (scheme == Qt::ColorScheme::Dark) {
46 return true;
47 }
48 if (scheme == Qt::ColorScheme::Light) {
49 return false;
50 }
51#endif
52 return QGuiApplication::palette().color(QPalette::Window).lightness() < 128;
53}
54
55} // namespace
56
57class QxThemeManagerPrivate
58{
59 QX_DECLARE_PUBLIC(QxThemeManager)
60public:
61 QxThemeManagerPrivate()
62 : m_settings(nullptr)
63 {
64 }
65
67 void start();
69 void setupSystemHook();
71 QX_CORE_PREPEND_NAMESPACE(QxSettings) * ensureSettings() const;
73 void reload();
75 void persistTheme();
76public:
77 mutable QX_CORE_PREPEND_NAMESPACE(QxSettings) * m_settings;
78 Theme m_theme = LightYellow;
79 bool m_followSystem = false;
80 Theme m_systemLight = LightOffice2013;
81 Theme m_systemDark = DarkOfficePlus;
82 bool m_warnedMissingStyleSheet = false;
83 QHash<int, QString> m_styleSheetFiles;
84 QHash<int, QString> m_styleSheetContent;
85};
86
87void QxThemeManagerPrivate::start()
88{
89 ensureSettings();
90 reload();
91 setupSystemHook();
92
93 if (m_followSystem) {
94 m_theme = detectSystemDark() ? m_systemDark : m_systemLight;
95 }
96 q_ptr->apply();
97}
98
99void QxThemeManagerPrivate::setupSystemHook()
100{
101 // Only Qt 6.5+ reports the colour scheme as a signal. On older versions the
102 // platforms do not offer an equivalent, so refreshSystemTheme() has to be
103 // called by the application (for example from a settings dialog).
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();
109 });
110 }
111#endif
112}
113
114QX_CORE_PREPEND_NAMESPACE(QxSettings) * QxThemeManagerPrivate::ensureSettings() const
115{
116 if (!m_settings) {
117 QString organization = QCoreApplication::organizationName();
118 if (organization.isEmpty()) {
119 organization = QString::fromLatin1(QX_THEME_FALLBACK_ORGANIZATION);
120 }
121 QString application = QCoreApplication::applicationName();
122 if (application.isEmpty()) {
123 application = organization;
124 }
125 m_settings = new QX_CORE_PREPEND_NAMESPACE(QxSettings)(organization, application, q_ptr);
126 }
127 return m_settings;
128}
129
130void QxThemeManagerPrivate::reload()
131{
132 QX_CORE_PREPEND_NAMESPACE(QxSettings) *settings = ensureSettings();
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);
136 m_systemLight =
137 themeFromId(settings->stringValue(QString::fromLatin1(kSystemLightKey), themeId(m_systemLight)), m_systemLight);
138 m_systemDark =
139 themeFromId(settings->stringValue(QString::fromLatin1(kSystemDarkKey), themeId(m_systemDark)), m_systemDark);
140 settings->endGroup();
141}
142
143void QxThemeManagerPrivate::persistTheme()
144{
145 QX_CORE_PREPEND_NAMESPACE(QxSettings) *settings = ensureSettings();
146 settings->beginGroup(QString::fromLatin1(kGroup));
147 settings->setValue(QString::fromLatin1(kThemeKey), themeId(m_theme));
148 settings->endGroup();
149}
150
151QxThemeManager::QxThemeManager(QObject *parent)
152 : QObject(parent)
153 , d_ptr(new QxThemeManagerPrivate())
154{
155 d_ptr->setPublic(this);
156 qRegisterMetaType<QX_THEME_PREPEND_NAMESPACE(Theme)>("QxTheme::Theme");
157}
158
163
165{
166 static QxThemeManager *s_instance = Q_NULLPTR;
167 if (!s_instance) {
168 s_instance = new QxThemeManager();
169 s_instance->d_ptr->start();
170 }
171 return s_instance;
172}
173
175{
176 Q_D(const QxThemeManager);
177 return d->m_theme;
178}
179
181{
182 return isDarkTheme(theme());
183}
184
186{
187 Q_D(const QxThemeManager);
188 return d->m_followSystem;
189}
190
192{
193 Q_D(QxThemeManager);
194 if (d->m_followSystem == on) {
195 return;
196 }
197 d->m_followSystem = on;
198
199 QX_CORE_PREPEND_NAMESPACE(QxSettings) *settings = d->ensureSettings();
200 settings->beginGroup(QString::fromLatin1(kGroup));
201 settings->setValue(QString::fromLatin1(kFollowKey), on);
203
204 if (!on) {
205 // Keep whatever the system selected and record it as the explicit
206 // choice, so the next launch does not silently jump to an older value.
207 d->persistTheme();
208 }
210 Q_EMIT followSystemThemeChanged(on);
211}
212
214{
215 Q_D(const QxThemeManager);
216 return d->m_systemLight;
217}
218
220{
221 Q_D(const QxThemeManager);
222 return d->m_systemDark;
223}
224
226{
227 Q_D(QxThemeManager);
228 d->m_systemLight = lightTheme;
229 d->m_systemDark = darkTheme;
230
231 QX_CORE_PREPEND_NAMESPACE(QxSettings) *settings = d->ensureSettings();
232 settings->beginGroup(QString::fromLatin1(kGroup));
233 settings->setValue(QString::fromLatin1(kSystemLightKey), themeId(lightTheme));
234 settings->setValue(QString::fromLatin1(kSystemDarkKey), themeId(darkTheme));
236
238}
239
241{
242 Q_D(const QxThemeManager);
243 return d->ensureSettings();
244}
245
247{
248 Q_D(QxThemeManager);
249 if (d->m_settings == settings) {
250 return;
251 }
252 delete d->m_settings;
253 d->m_settings = settings;
254 if (d->m_settings) {
255 d->m_settings->setParent(this);
256 }
257 d->reload();
258 apply();
259}
260
262{
263 Q_D(const QxThemeManager);
264 const int key = int(theme);
265
266 const QHash<int, QString>::const_iterator content = d->m_styleSheetContent.constFind(key);
267 if (content != d->m_styleSheetContent.constEnd()) {
268 return content.value();
269 }
270
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()));
276 return QString();
277 }
278 return QString::fromUtf8(reader.readAll());
279 }
280
282}
283
285{
286 Q_D(QxThemeManager);
287 d->m_styleSheetContent.remove(int(theme));
288 if (fileName.isEmpty()) {
289 d->m_styleSheetFiles.remove(int(theme));
290 } else {
291 d->m_styleSheetFiles.insert(int(theme), fileName);
292 }
293}
294
295void QxThemeManager::setStyleSheet(Theme theme, const QString &css)
296{
297 Q_D(QxThemeManager);
298 d->m_styleSheetFiles.remove(int(theme));
299 if (css.isEmpty()) {
300 d->m_styleSheetContent.remove(int(theme));
301 } else {
302 d->m_styleSheetContent.insert(int(theme), css);
303 }
304}
305
307{
308 Q_D(QxThemeManager);
309 d->m_theme = theme;
310 d->persistTheme();
311 apply();
312 Q_EMIT themeChanged(theme);
313}
314
316{
317 Q_D(QxThemeManager);
318 QApplication *app = qobject_cast<QApplication *>(QCoreApplication::instance());
319 if (!app) {
320 // The engine is documented to be used after QApplication exists.
321 return;
322 }
323
324 app->setPalette(QX_THEME_PREPEND_NAMESPACE(palette)(d->m_theme));
325
326 const QString css = styleSheet(d->m_theme);
327 if (css.isEmpty() && d->m_theme != Custom) {
328 // The built-in style sheet could not be resolved - qxribbon is not part
329 // of the process, or the resource prefix was relocated. Installing an
330 // empty sheet would silently wipe whatever the application set up, so
331 // leave it alone and say so once.
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)));
336 }
337 return;
338 }
339 app->setStyleSheet(css);
340}
341
343{
344 Q_D(QxThemeManager);
345 if (!d->m_followSystem) {
346 return;
347 }
348 const Theme target = detectSystemDark() ? d->m_systemDark : d->m_systemLight;
349 if (target == d->m_theme) {
350 return;
351 }
352 d->m_theme = target;
353 apply();
354 Q_EMIT themeChanged(target);
355}
356
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.
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.
Definition qxtheme.cpp:78
Theme themeFromId(const QString &id, Theme fallback)
Resolves an id produced by themeId(); fallback is returned for unknown ids.
Definition qxtheme.cpp:68
QString themeStyleSheet(Theme theme)
Content of the built-in style sheet, empty when the resource is missing.
Definition qxtheme.cpp:93
QString themeId(Theme theme)
Stable identifier written to the configuration.
Definition qxtheme.cpp:56
Theme
The themes understood by QxThemeManager.
Definition qxtheme.h:22
@ Custom
Definition qxtheme.h:29
@ LightYellow
Definition qxtheme.h:23
@ DarkOfficePlus
Definition qxtheme.h:28
@ LightOffice2013
Definition qxtheme.h:24
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)
QFile file
Definition qxlogger.cpp:25
const char * fileName
Definition qxtheme.cpp:26
#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.