6 #include <QAbstractScrollArea>
10 #include <QFileInfoList>
14 #include <QRegularExpression>
15 #include <QStringBuilder>
16 #include <QStyleOption>
17 #include <QStylePainter>
18 #include <QTextStream>
27 using namespace swift::config;
32 CStyleSheetUtility::CStyleSheetUtility(QObject *parent) : QObject(parent)
35 connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged,
this, &CStyleSheetUtility::qssDirectoryChanged);
36 connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged,
this, &CStyleSheetUtility::qssDirectoryChanged);
41 static const QStringList cats { CLogCategories::guiComponent() };
47 static const QString n(
"normal");
48 static const QString i(
"italic");
49 static const QString o(
"oblique");
50 static const QString e;
54 case QFont::StyleNormal:
return n;
55 case QFont::StyleItalic:
return i;
56 case QFont::StyleOblique:
return o;
63 if (font.weight() <
static_cast<int>(QFont::Normal))
65 static const QString l(
"light");
68 else if (font.weight() <
static_cast<int>(QFont::DemiBold))
70 static const QString n(
"normal");
73 else if (font.weight() <
static_cast<int>(QFont::Bold))
75 static const QString d(
"demibold");
78 else if (font.weight() <
static_cast<int>(QFont::Black))
80 static const QString b(
"bold");
85 static const QString b(
"black");
95 if (w.isEmpty() && s.isEmpty())
return "normal";
96 if (w.isEmpty())
return s;
97 if (s.isEmpty())
return w;
98 if (s ==
"normal")
return w;
99 return w.append(
" ").append(s);
103 const QString &fontStyle,
const QString &fontWeight,
104 const QString &fontColor)
106 static const QString indent(
" ");
107 static const QString lf(
"\n");
108 static const QString fontStyleSheet(
109 "%1font-family: \"%3\";%2%1font-size: %4;%2%1font-style: %5;%2%1font-weight: %6;%2%1color: %7;%2");
110 static const QString fontStyleSheetNoColor(
111 "%1font-family: \"%3\";%2%1font-size: %4;%2%1font-style: %5;%2%1font-weight: %6;%2");
113 return fontColor.isEmpty() ?
120 Q_ASSERT_X(widget, Q_FUNC_INFO,
"Missing widget");
121 const QFont f = widget->font();
123 f.family(), QStringLiteral(
"%1pt").arg(pointSize < 0 ? f.pointSize() : pointSize),
130 if (!s.contains(
"color:"))
return "";
131 thread_local
const QRegularExpression rx(
"color:\\s*(#*\\w+);");
132 const QString c = rx.match(s).captured(1);
133 return c.isEmpty() ?
"" : c;
138 QDir directory(CSwiftDirectories::stylesheetsDirectory());
139 if (!directory.exists()) {
return false; }
142 const bool needsWatcher = m_fileWatcher.files().isEmpty();
145 m_fileWatcher.addPath(CSwiftDirectories::stylesheetsDirectory());
147 directory.setNameFilters({
"*.qss",
"*.css" });
148 directory.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
151 const QFileInfoList fileInfoList = directory.entryInfoList();
154 for (
const QFileInfo &fileInfo : fileInfoList)
156 const QString absolutePath = fileInfo.absoluteFilePath();
157 QFile file(absolutePath);
158 if (file.open(QFile::QIODevice::ReadOnly | QIODevice::Text))
160 if (needsWatcher) { m_fileWatcher.addPath(absolutePath); }
161 QTextStream in(&file);
163 const QString f = fileInfo.fileName().toLower();
166 if (CBuildConfig::isLocalDeveloperDebugBuild())
168 const QString fn = CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), f);
169 CFileUtils::writeStringToFile(c, fn);
173 newStyleSheets.insert(f, c);
179 if (newStyleSheets != m_styleSheets)
181 m_styleSheets = newStyleSheets;
190 return m_styleSheets[fileName.toLower()].trimmed();
196 bool fontAdded =
false;
199 for (
const QString &fileName : fileNames)
201 const QString key = fileName.toLower().trimmed();
207 if (fontAdded) {
continue; }
211 else { s = m_styleSheets[key]; }
212 if (s.isEmpty()) {
continue; }
214 style += (
style.isEmpty() ? QString() :
"\n\n") % u
"/** file: " % fileName %
" **/\n" % s;
221 if (fileName.isEmpty())
return false;
222 return m_styleSheets.contains(fileName.toLower().trimmed());
228 if (font.pixelSize() >= 0) { fs.append(QString::number(font.pixelSize())).append(
"px"); }
229 else { fs.append(QString::number(font.pointSizeF())).append(
"pt"); }
234 const QString &fontWeight,
const QString &fontColor)
242 const QString qssWidget(u
"QWidget {\n" % qss % u
"}\n");
246 bool ok = fontFile.open(QFile::Text | QFile::WriteOnly);
249 QTextStream out(&fontFile);
263 QFile fontFile(CFileUtils::appendFilePaths(CSwiftDirectories::stylesheetsDirectory(),
fileNameFontsModified()));
264 return fontFile.remove();
269 static const QString n(
"normal");
270 const QString c = combinedStyleAndWeight.toLower();
273 if (c.contains(s)) {
return s; }
280 static const QString n(
"normal");
281 const QString c = combinedStyleAndWeight.toLower();
284 if (c.contains(w)) {
return w; }
291 static const QString f(getQssFileName(
"fonts"));
297 static const QString f(
"fonts.modified.qss");
306 if (!file.exists()) {
return false; }
307 bool r = file.remove();
308 if (!r) {
return false; }
315 static const QString f(getQssFileName(
"swiftstdgui"));
321 static const QString fn = CFileUtils::appendFilePaths(CSwiftDirectories::stylesheetsDirectory(),
328 static const QString f(getQssFileName(
"infobar"));
334 static const QString f(getQssFileName(
"navigator"));
340 static const QString f(getQssFileName(
"dockwidgettab"));
346 static const QString f(getQssFileName(
"stdwidget"));
352 static const QString fn = CFileUtils::appendFilePaths(CSwiftDirectories::stylesheetsDirectory(),
359 static const QString f(
"textmessage.css");
365 static const QString f(getQssFileName(
"filterdialog"));
371 static const QString f(getQssFileName(
"swiftcore"));
377 static const QString f(getQssFileName(
"swiftdata"));
383 static const QString f(getQssFileName(
"swiftlauncher"));
389 static const QStringList w({
"bold",
"semibold",
"light",
"black",
"normal" });
395 static const QStringList s({
"italic",
"oblique",
"normal" });
401 static const QString t =
"background-color: transparent;";
407 Q_ASSERT(usedWidget);
408 if (!usedWidget) {
return false; }
410 Q_ASSERT(usedWidget->style());
411 QStyle *
style = usedWidget->style();
412 if (!
style) {
return false; }
418 QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(usedWidget);
419 QStylePainter p(sa ? sa->viewport() : usedWidget);
420 if (!p.isActive()) {
return false; }
423 opt.initFrom(usedWidget);
424 p.drawPrimitive(element, opt);
429 const QString &width,
const QString &height)
431 Q_ASSERT(!checkedIcon.isEmpty());
432 Q_ASSERT(!uncheckedIcon.isEmpty());
434 static const QString st =
"QCheckBox::indicator { width: %1; height: %2; } QCheckBox::indicator:checked { "
435 "image: url(%3); } QCheckBox::indicator:unchecked { image: url(%4); }";
436 return st.arg(width, height, checkedIcon, uncheckedIcon);
441 QString s1(style1.trimmed());
442 QString s2(style2.trimmed());
443 if (s1.isEmpty()) {
return s2; }
444 if (s2.isEmpty()) {
return s1; }
445 if (!s1.endsWith(
";")) { s1 = s1.append(
"; "); }
447 if (!s1.endsWith(
";")) { s1 = s1.append(
";"); }
453 Q_ASSERT_X(widget, Q_FUNC_INFO,
"Missing widget");
454 if (!widget->property(
"qsysKernelType").isValid())
456 widget->setProperty(
"qsysKernelType", QSysInfo::kernelType());
457 widget->setProperty(
"qsysCurrentCpuArchitecture", QSysInfo::currentCpuArchitecture());
458 widget->setProperty(
"qsysBuildCpuArchitecture", QSysInfo::buildCpuArchitecture());
459 widget->setProperty(
"qsysProductType", QSysInfo::productType());
462 if (withChildWidgets)
464 for (QWidget *w : widget->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly))
471 void CStyleSheetUtility::qssDirectoryChanged(
const QString &file)
477 QString CStyleSheetUtility::getQssFileName(
const QString &fileName)
479 static const QString qss(
".qss");
480 QString fn(fileName);
481 if (fn.endsWith(qss)) { fn.chop(qss.length()); }
484 if (CBuildConfig::isRunningOnWindowsNtPlatform()) { specific = fn % u
".win" % qss; }
485 else if (CBuildConfig::isRunningOnMacOSPlatform()) { specific = fn % u
".mac" % qss; }
486 else if (CBuildConfig::isRunningOnLinuxPlatform()) { specific = fn % u
".linux" % qss; }
487 return qssFileExists(specific) ? specific : fn + qss;
490 bool CStyleSheetUtility::qssFileExists(
const QString &filename)
492 if (filename.isEmpty()) {
return false; }
493 const QFileInfo f(CFileUtils::appendFilePaths(CSwiftDirectories::stylesheetsDirectory(), filename));
494 return f.exists() && f.isReadable();
Reads and provides style sheets.
bool containsStyle(const QString &fileName) const
Contains style for name.
static const QString & fileNameDockWidgetTab()
File name dockwidgettab.qss.
static const QString & fontWeightAsString(const QFont &font)
Font weight as string.
static QString asStylesheet(const QString &fontFamily, const QString &fontSize, const QString &fontStyle, const QString &fontWeight, const QString &fontColorString={})
Parameters as stylesheet.
static bool useStyleSheetInDerivedWidget(QWidget *derivedWidget, QStyle::PrimitiveElement element=QStyle::PE_Widget)
Use style sheets in derived widgets.
static const QString & fileNameSwiftData()
File name swiftcore.qss.
static const QString & fileNameSwiftStandardGui()
File name swift standard GUI.
static const QStringList & fontStyles()
Font styles.
static const QString & fontStyleAsString(const QFont &font)
Font style as string.
static const QString & fileNameSwiftCore()
File name swiftcore.qss.
static const QString & fileNameInfoBar()
File name infobar.qss.
static const QString & fileNameStandardWidget()
File name for standard widgets.
static const QString & fileNameTextMessage()
File name textmessage.qss.
static const QString & transparentBackgroundColor()
Transparent background color.
static QString fontStyle(const QString &combinedStyleAndWeight)
Get the font style.
static const QStringList & fontWeights()
Font weights.
static const QString & fileNameSwiftLauncher()
File name swiftlauncher.qss.
static QString styleForIconCheckBox(const QString &checkedIcon, const QString &uncheckedIcon, const QString &width="16px", const QString &height="16px")
Stylesheet string for a checkbox displayed as 2 icons.
bool updateFont(const QFont &font)
Update the fonts.
static const QStringList & getLogCategories()
Log cats.
static QString fontAsCombinedWeightStyle(const QFont &font)
Font as combined weight and style.
static QString concatStyles(const QString &style1, const QString &style2)
Concatenate 2 styles.
static const QString & fileNameFilterDialog()
File name maininfoarea.qss.
static void setQSysInfoProperties(QWidget *widget, bool withChildWidgets)
Set QSysInfo properties for given widget (which can be used in stylesheet)
QString fontColorString() const
Current font color from style sheet.
bool read()
Read the *.qss files.
QString style(const QString &fileName) const
Style for given file name.
static const QString & fileNameAndPathStandardWidget()
Full file path and name for standard widgets.
void styleSheetsChanged()
Sheets have been changed.
static const QString & fileNameFontsModified()
Name for user modified file.
QString styles(const QStringList &fileNames) const
Multiple styles concatenated.
static const QString & fileNameNavigator()
File name navigator.qss.
static QString fontWeight(const QString &combinedStyleAndWeight)
Get the font weight.
bool resetFont()
Reset font.
static const QString & fileNameFonts()
File name fonts.qss.
static const QString & fileNameAndPathSwiftStandardGui()
Full file path and name for swift standard GUI.
bool deleteModifiedFontFile()
Delete the modified file for fonts.
Class for emitting a log message.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Free functions in swift::misc.
SWIFT_MISC_EXPORT QString removeComments(const QString &in, bool removeSlash, bool removeDoubleSlash)
Remove comments such as /** **/ or //.