RabbitCommon v2.3.4
Loading...
Searching...
No Matches
Style.cpp
1// Copyright Copyright (c) Kang Lin studio, All Rights Reserved
2// Author Kang Lin <kl222@126.com>
3
4#include "Style.h"
5#include "RabbitCommonDir.h"
6
7#include <QStyle>
8#include <QStyleFactory>
9#include <QSettings>
10#include <QDebug>
11#include <QApplication>
12#include <QRegularExpression>
13#include <QLoggingCategory>
14#include <QDir>
15#include <QColor>
16#include <QPalette>
17#include <QIcon>
18#include <QStyleHints>
19#include <QMessageBox>
20
21namespace RabbitCommon {
22
23static Q_LOGGING_CATEGORY(log, "RabbitCommon.Style")
24
25CStyle::CStyle(QObject *parent) : QObject(parent)
26 , m_bModifyStyleSheetFile(false)
27 , m_bModifyStyleName(false)
28{
29 qDebug(log) << Q_FUNC_INFO;
30
31 m_szDefaultIconTheme = QIcon::themeName();
32 if(m_szDefaultIconTheme.isEmpty()) {
33 m_szDefaultIconTheme = "rabbit-black";
34 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
35 const auto scheme = QGuiApplication::styleHints()->colorScheme();
36 if(Qt::ColorScheme::Dark == scheme)
37 m_szDefaultIconTheme = "rabbit-white";
38 #endif
39 QIcon::setThemeName(m_szDefaultIconTheme);
40 }
41#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && !defined(Q_OS_WINDOWS)
42 if(!QIcon::fallbackThemeName().isEmpty())
43 qDebug(log) << "Old icon fallback theme:" << QIcon::fallbackThemeName();
44 //Note: the fallback icon theme must set "rabbit-*"
45 m_szDefaultFallbackIconTheme = "rabbit-black";
46 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
47 const auto scheme = QGuiApplication::styleHints()->colorScheme();
48 if(Qt::ColorScheme::Dark == scheme)
49 m_szDefaultFallbackIconTheme = "rabbit-white";
50 #endif
51 QIcon::setFallbackThemeName(m_szDefaultFallbackIconTheme);
52#endif
53 qDebug(log)
54 << "Icon theme name:" << QIcon::themeName() << "\n"
55 << "Icon theme search paths:" << QIcon::themeSearchPaths() << "\n"
56 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
57 << "Fallback theme name:" << QIcon::fallbackThemeName() << "\n"
58 #endif //QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
59 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
60 << "Fallback search paths:" << QIcon::fallbackSearchPaths() << "\n"
61 #endif // QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
62 ;
63
64 bool check = false;
65#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
66 if(QApplication::instance()) {
67 check = connect(QApplication::styleHints(),
68 SIGNAL(colorSchemeChanged(Qt::ColorScheme)),
69 this, SLOT(slotColorSchemeChanged(Qt::ColorScheme)));
70 Q_ASSERT(check);
71 }
72#endif
73
74#if defined(Q_OS_WIN)
75 if(QStyleFactory::keys().contains("Fusion"))
76 m_szStyleName = "Fusion";
77#else
78 #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
79 if(QApplication::style())
80 m_szStyleName = QApplication::style()->name();
81 #else
82 if(!QStyleFactory::keys().isEmpty())
83 m_szStyleName = QStyleFactory::keys().at(0);
84 #endif
85#endif
86 if(m_szStyleName.isEmpty()) {
87 if(!QStyleFactory::keys().isEmpty())
88 m_szStyleName = QStyleFactory::keys().at(0);
89 }
90 qDebug(log) << "Init style name:" << m_szStyleName;
91}
92
94{
95 // Load icons theme
96 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
97 QSettings::IniFormat);
98 QIcon::setThemeSearchPaths(
99 QIcon::themeSearchPaths()
100 << RabbitCommon::CDir::Instance()->GetDirIcons(true));
101 bool bIconTheme = set.value("Style/Icon/Theme/Enable", true).toBool();
102 if(bIconTheme) {
103 QString szThemeName = QIcon::themeName();
104 if(szThemeName.isEmpty())
105 szThemeName = m_szDefaultIconTheme;
106 szThemeName = set.value("Style/Icon/Theme", szThemeName).toString();
107
108#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
109 Qt::ColorScheme scheme = Qt::ColorScheme::Unknown;
110 if(QGuiApplication::styleHints())
111 scheme = QGuiApplication::styleHints()->colorScheme();
112 //qDebug(log) << "Scheme:" << scheme;
113 static QRegularExpression reBlack("black|[Dd]ark");
114 QRegularExpressionMatch matchBlack = reBlack.match(szThemeName);
115 if (Qt::ColorScheme::Dark == scheme && matchBlack.hasMatch()) {
116 QString szInfo = tr("Current system theme is dark, current theme is ") + QString("\"") + szThemeName + "\". ";
117 szInfo += tr("it's almost impossible to find the icon because its color matches the current system theme.");
118 szThemeName = "rabbit-white";
119 szInfo += " " + tr("change to ") + szThemeName;
120 qInfo(log) << szInfo;
121 }
122 static QRegularExpression reWhite("[Ww]hite|[L|l]ight");
123 QRegularExpressionMatch matchWhite = reWhite.match(szThemeName);
124 if (Qt::ColorScheme::Light == scheme && matchWhite.hasMatch()) {
125 QString szInfo = tr("Current system theme is light, current theme is ") + QString("\"") + szThemeName + "\". ";
126 szInfo += tr("it's almost impossible to find the icon because its color matches the current system theme.");
127 szThemeName = "rabbit-black";
128 szInfo += " " + tr("change to ") + szThemeName;
129 qInfo(log) << szInfo;
130 }
131#endif // QT_VERSION
132
133 QIcon::setThemeName(szThemeName);
134
135#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && !defined(Q_OS_WINDOWS)
136 QIcon::setFallbackSearchPaths(
137 QIcon::fallbackSearchPaths()
138 << RabbitCommon::CDir::Instance()->GetDirIcons(true));
139 QString szFallbackThemeName = QIcon::fallbackThemeName();
140 if(szFallbackThemeName.isEmpty())
141 szFallbackThemeName = m_szDefaultFallbackIconTheme;
142 QIcon::setFallbackThemeName(set.value("Style/Icon/Theme/Fallback",
143 szFallbackThemeName).toString());
144
145 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
146 if (Qt::ColorScheme::Dark == scheme && matchBlack.hasMatch()) {
147 QString szInfo = tr("Current system theme is dark, current theme is ") + QString("\"") + szFallbackThemeName + "\". ";
148 szInfo += tr("it's almost impossible to find the icon because its color matches the current system theme.");
149 szFallbackThemeName = "rabbit-white";
150 szInfo += " " + tr("change to ") + szFallbackThemeName;
151 qInfo(log) << szInfo;
152 }
153 if (Qt::ColorScheme::Light == scheme && matchWhite.hasMatch()) {
154 QString szInfo = tr("Current system theme is light, current theme is ") + QString("\"") + szFallbackThemeName + "\". ";
155 szInfo += tr("it's almost impossible to find the icon because its color matches the current system theme.");
156 szFallbackThemeName = "rabbit-black";
157 szInfo += " " + tr("change to ") + szFallbackThemeName;
158 qInfo(log) << szInfo;
159 }
160 #endif
161#endif //QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
162
163 qDebug(log) << Q_FUNC_INFO << "\n"
164 << "Icon theme name:" << QIcon::themeName() << "\n"
165 << "Icon theme search paths:" << QIcon::themeSearchPaths() << "\n"
166 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
167 << "Fallback theme name:" << QIcon::fallbackThemeName() << "\n"
168 #endif //QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
169 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
170 << "Fallback search paths:" << QIcon::fallbackSearchPaths() << "\n"
171 #endif // QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
172 ;
173 }
174
175 //qDebug(log) << "Current style:" << QApplication::style() << "QStyleFactory:" << QStyleFactory::keys();
176
177 QString szName = set.value("Style/Name", GetStyleName()).toString();
178 SetStyleName(szName);
179 LoadStyle(szName);
180
181 // Get style sheet file
182 QString szFile = set.value("Style/Sheet/File", GetStyleSheetFile()).toString();
183 SetStyleSheetFile(szFile);
184 return LoadStyleSheet(szFile);
185}
186
187CStyle* CStyle::Instance()
188{
189 static CStyle* p = new CStyle();
190 if(!p) p = new CStyle();
191 return p;
192}
193
194int CStyle::ReLoadStyle()
195{
196 if(m_bModifyStyleName) {
197 LoadStyle(m_szStyleName);
198 m_bModifyStyleName = false;
199 }
200 if(m_bModifyStyleSheetFile) {
201 LoadStyleSheet(m_szStyleSheetFile);
202 m_bModifyStyleSheetFile = false;
203 }
204 return 0;
205}
206
207int CStyle::LoadStyle(const QString& szName)
208{
209 // qDebug(log) << "Current style:" << QApplication::style()
210 // << "QStyleFactory:" << QStyleFactory::keys();
211
212 if(szName.isEmpty())
213 return 0;
214#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
215 if(QApplication::style() && QApplication::style()->name() == szName)
216 return 0;
217#endif
218 qInfo(log) << "Apply style:" << szName;
219 QApplication::setStyle(QStyleFactory::create(szName));
220 return 0;
221
222#if defined(Q_OS_WIN)
223 // 显式设置 Windows 样式,随着系统颜色改变(测试环境:Qt6.9.2; Windows10)。
224 //QApplication::setStyle(QStyleFactory::create("windows"));
225
226 // 显式设置 Windows11 样式,可随着系统颜色改变(测试环境:Qt6.9.2; Windows10)。
227 //QApplication::setStyle(QStyleFactory::create("windows11"));
228
229 // 或者使用 WindowsVista 样式(更现代),不能随着系统颜色改变(测试环境:Qt6.9.2; Windows10)。
230 //QApplication::setStyle(QStyleFactory::create("WindowsVista"));
231
232 if(QStyleFactory::keys().contains("Fusion")) {
233 // 使用 Fusion 样式(跨平台一致)
234 QApplication::setStyle(QStyleFactory::create("Fusion"));
235 }
236 // 但使用 Windows 的默认调色板
237 //QApplication::setPalette(QApplication::style()->standardPalette());
238#endif
239 return 0;
240}
241
242int CStyle::LoadStyleSheet(const QString &szFile)
243{
244 //m_szStyleSheetFile = szFile;
245 if(szFile.isEmpty())
246 {
247 qApp->setStyleSheet("");
248 //qApp->setPalette(QPalette(QColor(Qt::gray)));
249 } else {
250 QFile file(szFile);
251 if(file.open(QFile::ReadOnly))
252 {
253 qInfo(log) << "Load style file:" << szFile;
254 QString stylesheet= file.readAll();
255 QString pattern("QPalette\\{background:#[0-9a-fA-F]+;\\}");
256#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
257 QRegularExpression re(pattern);
258 QRegularExpressionMatch match = re.match(stylesheet);
259 if (match.hasMatch()) {
260 QString matched = match.captured(0);
261 if(!matched.isEmpty())
262 {
263 QRegularExpression rePalette("#[0-9a-fA-F]+");
264 match = rePalette.match(matched);
265 if(match.hasMatch())
266 {
267 QString paletteColor = match.captured(0);
268 qApp->setPalette(QPalette(QColor(paletteColor)));
269 }
270 }
271 }
272#else
273 QRegExp rx(pattern);
274 int pos = rx.indexIn(stylesheet);
275 if(pos > -1)
276 {
277 QString szPalette = rx.cap();
278 QRegExp rxPalette("#[0-9a-fA-F]+");
279 int pos = rxPalette.indexIn(stylesheet);
280 if(pos > -1)
281 {
282 QString paletteColor = rxPalette.cap();
283 qApp->setPalette(QPalette(QColor(paletteColor)));
284 }
285 }
286#endif
287 qApp->setStyleSheet(stylesheet);
288 file.close();
289 }
290 else
291 {
292 qCritical(log) << "file open file fail:" << szFile;
293 }
294 }
295 return 0;
296}
297
299{
300 QString szFile = m_szStyleSheetFile;
301 QString szPath = RabbitCommon::CDir::Instance()->GetDirData(true)
302 + QDir::separator()
303 + "style";
304#ifdef Q_OS_LINUX
305 QDir d(szPath);
306 if(!d.exists())
307 szPath = "/usr/share/style";
308#endif
309 if(!szFile.isEmpty()) {
310 QFileInfo fi(szFile);
311 szPath = fi.absoluteFilePath();
312 }
313 qDebug(log) << "Path:" << szPath;
314 QWidget* pParent = dynamic_cast<QWidget*>(this->parent());
315 szFile = QFileDialog::getOpenFileName(pParent, tr("Open style"),
316 szPath,
317 tr("Style files(*.qss *.css);; All files(*.*)"));
318 return szFile;
319}
320
321QString CStyle::GetStyleSheetFile()
322{
323 if(!m_szStyleSheetFile.isEmpty())
324 if(!QFile::exists(m_szStyleSheetFile))
325 return "";
326 return m_szStyleSheetFile;
327}
328
329void CStyle::SetStyleSheetFile(const QString &file)
330{
331 if(m_szStyleSheetFile == file)
332 return;
333 m_szStyleSheetFile = file;
334 m_bModifyStyleSheetFile = true;
335 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
336 QSettings::IniFormat);
337 set.setValue("Style/Sheet/File", m_szStyleSheetFile);
338}
339
340QString CStyle::GetStyleName()
341{
342 return m_szStyleName;
343}
344
345void CStyle::SetStyleName(const QString &szName)
346{
347 if(szName == m_szStyleName)
348 return;
349 m_szStyleName = szName;
350 m_bModifyStyleName = true;
351 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
352 QSettings::IniFormat);
353 set.setValue("Style/Name", m_szStyleName);
354}
355
356#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
357void CStyle::slotColorSchemeChanged(Qt::ColorScheme colorScheme)
358{
359 QString szThemeName;
360 QString szColorScheme;
361 QRegularExpression reBlack("[Bb]lack|[Dd]ark");
362 QRegularExpressionMatch match = reBlack.match(QIcon::themeName());
363 if (Qt::ColorScheme::Dark == colorScheme && match.hasMatch()) {
364
365 szThemeName = "rabbit-white";
366 szColorScheme = tr("Dark");
367 }
368 QRegularExpression reWhite("[Ww]hite|[L|l]ight");
369 match = reWhite.match(QIcon::themeName());
370 if (Qt::ColorScheme::Light == colorScheme && match.hasMatch()) {
371
372 szThemeName = "rabbit-black";
373 szColorScheme = tr("Light");
374 }
375 if(szThemeName.isEmpty()) return;
376 QString szInfo = tr("Current system theme is") + " \"" + szColorScheme + "\", " + tr("current theme is") + QString(" \"") + QIcon::themeName() + "\". ";
377 szInfo += tr("it's almost impossible to find the icon because its color matches the current system theme.");
378 szInfo += " " + tr("change to ") + "\"" + szThemeName + "\"?";
379 auto ret = QMessageBox::information(nullptr, tr("Change theme"), szInfo,
380 QMessageBox::Ok | QMessageBox::No,
381 QMessageBox::Ok);
382 if(QMessageBox::Ok == ret) {
383 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
384 QSettings::IniFormat);
385 QIcon::setThemeName(szThemeName);
386 szThemeName = set.value("Style/Icon/Theme", szThemeName).toString();
387 }
388}
389#endif // QT_VERSION
390
391} //namespace RabbitCommon
QString GetDirData(bool bReadOnly=false)
Get data directory.
The CStyle class.
Definition Style.h:23
int LoadStyle()
Load style from configure file.
Definition Style.cpp:93
int LoadStyleSheet(const QString &szFile)
Load style sheet from the file.
Definition Style.cpp:242
QString GetStyleSheet()
Open get style sheet dialog.
Definition Style.cpp:298