-QBrush QssParser::parseBrushValue(const QString &str, bool *ok) {
- if(ok)
- *ok = false;
- QColor c = parseColorValue(str);
- if(c.isValid()) {
- if(ok)
- *ok = true;
- return QBrush(c);
- }
-
- if(str.startsWith("palette")) { // Palette color role
- QRegExp rx("palette\\s*\\(\\s*([a-z-]+)\\s*\\)");
- if(!rx.exactMatch(str)) {
- qWarning() << Q_FUNC_INFO << tr("Invalid palette color role specification: %1").arg(str);
- return QBrush();
- }
- if(!_paletteColorRoles.contains(rx.cap(1))) {
- qWarning() << Q_FUNC_INFO << tr("Unknown palette color role: %1").arg(rx.cap(1));
- return QBrush();
- }
- return QBrush(_palette.brush(_paletteColorRoles.value(rx.cap(1))));
-
- } else if(str.startsWith("qlineargradient")) {
- static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
- QRegExp rx(QString("qlineargradient\\s*\\(\\s*x1:%1,\\s*y1:%1,\\s*x2:%1,\\s*y2:%1,(.+)\\)").arg(rxFloat));
- if(!rx.exactMatch(str)) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
- return QBrush();
- }
- qreal x1 = rx.cap(1).toDouble();
- qreal y1 = rx.cap(2).toDouble();
- qreal x2 = rx.cap(3).toDouble();
- qreal y2 = rx.cap(4).toDouble();
- QGradientStops stops = parseGradientStops(rx.cap(5).trimmed());
- if(!stops.count()) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
- return QBrush();
- }
- QLinearGradient gradient(x1, y1, x2, y2);
- gradient.setStops(stops);
- if(ok)
- *ok = true;
- return QBrush(gradient);
-
- } else if(str.startsWith("qconicalgradient")) {
- static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
- QRegExp rx(QString("qconicalgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*angle:%1,(.+)\\)").arg(rxFloat));
- if(!rx.exactMatch(str)) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
- return QBrush();
- }
- qreal cx = rx.cap(1).toDouble();
- qreal cy = rx.cap(2).toDouble();
- qreal angle = rx.cap(3).toDouble();
- QGradientStops stops = parseGradientStops(rx.cap(4).trimmed());
- if(!stops.count()) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
- return QBrush();
- }
- QConicalGradient gradient(cx, cy, angle);
- gradient.setStops(stops);
- if(ok)
- *ok = true;
- return QBrush(gradient);
-
- } else if(str.startsWith("qradialgradient")) {
- static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
- QRegExp rx(QString("qradialgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*radius:%1,\\s*fx:%1,\\s*fy:%1,(.+)\\)").arg(rxFloat));
- if(!rx.exactMatch(str)) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
- return QBrush();
- }
- qreal cx = rx.cap(1).toDouble();
- qreal cy = rx.cap(2).toDouble();
- qreal radius = rx.cap(3).toDouble();
- qreal fx = rx.cap(4).toDouble();
- qreal fy = rx.cap(5).toDouble();
- QGradientStops stops = parseGradientStops(rx.cap(6).trimmed());
- if(!stops.count()) {
- qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
- return QBrush();
- }
- QRadialGradient gradient(cx, cy, radius, fx, fy);
- gradient.setStops(stops);
- if(ok)
- *ok = true;
- return QBrush(gradient);
- }
-
- return QBrush();
+
+/******** Parse a whole format attribute block ********/
+
+QTextCharFormat QssParser::parseFormat(const QString &qss)
+{
+ QTextCharFormat format;
+
+ foreach(QString line, qss.split(';', QString::SkipEmptyParts)) {
+ int idx = line.indexOf(':');
+ if (idx <= 0) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid property declaration: %1").arg(line.trimmed());
+ continue;
+ }
+ QString property = line.left(idx).trimmed();
+ QString value = line.mid(idx + 1).simplified();
+
+ if (property == "background" || property == "background-color")
+ format.setBackground(parseBrush(value));
+ else if (property == "foreground" || property == "color")
+ format.setForeground(parseBrush(value));
+
+ // Color code overrides
+ else if (property == "allow-foreground-override") {
+ bool ok;
+ bool v = parseBoolean(value, &ok);
+ if (ok)
+ format.setProperty(static_cast<int>(UiStyle::FormatProperty::AllowForegroundOverride), v);
+ }
+ else if (property == "allow-background-override") {
+ bool ok;
+ bool v = parseBoolean(value, &ok);
+ if (ok)
+ format.setProperty(static_cast<int>(UiStyle::FormatProperty::AllowBackgroundOverride), v);
+ }
+
+ // font-related properties
+ else if (property.startsWith("font")) {
+ if (property == "font")
+ parseFont(value, &format);
+ else if (property == "font-style")
+ parseFontStyle(value, &format);
+ else if (property == "font-weight")
+ parseFontWeight(value, &format);
+ else if (property == "font-size")
+ parseFontSize(value, &format);
+ else if (property == "font-family")
+ parseFontFamily(value, &format);
+ else {
+ qWarning() << Q_FUNC_INFO << tr("Invalid font property: %1").arg(line);
+ continue;
+ }
+ }
+
+ else {
+ qWarning() << Q_FUNC_INFO << tr("Unknown ChatLine property: %1").arg(property);
+ }
+ }
+
+ return format;
+}
+
+/******** Boolean value ********/
+
+bool QssParser::parseBoolean(const QString &str, bool *ok) const
+{
+ if (ok)
+ *ok = true;
+
+ if (str == "true")
+ return true;
+ if (str == "false")
+ return false;
+
+ qWarning() << Q_FUNC_INFO << tr("Invalid boolean value: %1").arg(str);
+ if (ok)
+ *ok = false;
+ return false;
+}
+
+/******** Brush ********/
+
+QBrush QssParser::parseBrush(const QString &str, bool *ok)
+{
+ if (ok)
+ *ok = false;
+ QColor c = parseColor(str);
+ if (c.isValid()) {
+ if (ok)
+ *ok = true;
+ return QBrush(c);
+ }
+
+ if (str.startsWith("palette")) { // Palette color role
+ // Does the palette follow the expected format? For example:
+ // palette(marker-line)
+ // palette ( system-color-0f )
+ //
+ // Match the palette marker, grabbing the name inside in case-sensitive manner
+ // palette\s*\(\s*([a-z-0-9]+)\s*\)
+ // palette Match the string 'palette'
+ // \s* Match any amount of whitespace
+ // \(, \) Match literal '(' or ')' marks
+ // (...+) Match contents between 1 and unlimited number of times
+ // [a-z-] Match any character from a-z, case sensitive
+ // [0-9] Match any digit from 0-9
+ // Note that '\' must be escaped as '\\'
+ // Helpful interactive website for debugging and explaining: https://regex101.com/
+ static const QRegExp rx("palette\\s*\\(\\s*([a-z-0-9]+)\\s*\\)");
+ if (!rx.exactMatch(str)) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid palette color role specification: %1").arg(str);
+ return QBrush();
+ }
+ if (_paletteColorRoles.contains(rx.cap(1)))
+ return QBrush(_palette.brush(_paletteColorRoles.value(rx.cap(1))));
+ if (_uiStyleColorRoles.contains(rx.cap(1)))
+ return QBrush(_uiStylePalette.at(static_cast<int>(_uiStyleColorRoles.value(rx.cap(1)))));
+ qWarning() << Q_FUNC_INFO << tr("Unknown palette color role: %1").arg(rx.cap(1));
+ return QBrush();
+ }
+ else if (str.startsWith("qlineargradient")) {
+ static const QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
+ static const QRegExp rx(QString("qlineargradient\\s*\\(\\s*x1:%1,\\s*y1:%1,\\s*x2:%1,\\s*y2:%1,(.+)\\)").arg(rxFloat));
+ if (!rx.exactMatch(str)) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
+ return QBrush();
+ }
+ qreal x1 = rx.cap(1).toDouble();
+ qreal y1 = rx.cap(2).toDouble();
+ qreal x2 = rx.cap(3).toDouble();
+ qreal y2 = rx.cap(4).toDouble();
+ QGradientStops stops = parseGradientStops(rx.cap(5).trimmed());
+ if (!stops.count()) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
+ return QBrush();
+ }
+ QLinearGradient gradient(x1, y1, x2, y2);
+ gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ gradient.setStops(stops);
+ if (ok)
+ *ok = true;
+ return QBrush(gradient);
+ }
+ else if (str.startsWith("qconicalgradient")) {
+ static const QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
+ static const QRegExp rx(QString("qconicalgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*angle:%1,(.+)\\)").arg(rxFloat));
+ if (!rx.exactMatch(str)) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
+ return QBrush();
+ }
+ qreal cx = rx.cap(1).toDouble();
+ qreal cy = rx.cap(2).toDouble();
+ qreal angle = rx.cap(3).toDouble();
+ QGradientStops stops = parseGradientStops(rx.cap(4).trimmed());
+ if (!stops.count()) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
+ return QBrush();
+ }
+ QConicalGradient gradient(cx, cy, angle);
+ gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ gradient.setStops(stops);
+ if (ok)
+ *ok = true;
+ return QBrush(gradient);
+ }
+ else if (str.startsWith("qradialgradient")) {
+ static const QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
+ static const QRegExp rx(QString("qradialgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*radius:%1,\\s*fx:%1,\\s*fy:%1,(.+)\\)").arg(rxFloat));
+ if (!rx.exactMatch(str)) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
+ return QBrush();
+ }
+ qreal cx = rx.cap(1).toDouble();
+ qreal cy = rx.cap(2).toDouble();
+ qreal radius = rx.cap(3).toDouble();
+ qreal fx = rx.cap(4).toDouble();
+ qreal fy = rx.cap(5).toDouble();
+ QGradientStops stops = parseGradientStops(rx.cap(6).trimmed());
+ if (!stops.count()) {
+ qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
+ return QBrush();
+ }
+ QRadialGradient gradient(cx, cy, radius, fx, fy);
+ gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ gradient.setStops(stops);
+ if (ok)
+ *ok = true;
+ return QBrush(gradient);
+ }
+
+ return QBrush();