uistyle: Define via stylesheet if color codes have an effect
authorManuel Nickschas <sputnick@quassel-irc.org>
Wed, 7 Mar 2018 19:59:37 +0000 (20:59 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Thu, 8 Mar 2018 01:10:28 +0000 (02:10 +0100)
In certain situations, color codes should have no effect, e.g. when
selecting text. This required hacks in stylesheets; for example
when setting a foreground color for self or highlighted messages,
the mIRC colors had to be redefined in the stylesheet for that label.
Also the code had some workaround, e.g. for URLs.

With the introduction of the non-styleable extended mIRC and
hex colors, these workarounds are no longer possible.

Solve these issues by introducing two new properties that can be set
in the stylesheet:

allow-foreground-override: true|false
allow-background-override: true|false

The default stylesheet sets them to true by default, and to false
e.g. for selections.

Remove the now unneeded workaround from m4yer's stylesheet, too.

data/stylesheets/default.qss
data/stylesheets/m4yer.qss
src/uisupport/qssparser.cpp
src/uisupport/qssparser.h
src/uisupport/uistyle.cpp
src/uisupport/uistyle.h

index 8d5ae32..506dda9 100644 (file)
@@ -6,16 +6,22 @@
 // Basics
 ChatLine {
   foreground: palette(text);
 // Basics
 ChatLine {
   foreground: palette(text);
+  // By default, allow color codes everywhere
+  allow-foreground-override: true;
+  allow-background-override: true;
 }
 
 ChatLine[label="highlight"] {
   foreground: black;
   background: #ff8000;
 }
 
 ChatLine[label="highlight"] {
   foreground: black;
   background: #ff8000;
+  allow-background-override: false;
 }
 
 ChatLine[label="selected"] {
   foreground: palette(highlighted-text);
   background: palette(highlight);
 }
 
 ChatLine[label="selected"] {
   foreground: palette(highlighted-text);
   background: palette(highlight);
+  allow-foreground-override: false;
+  allow-background-override: false;
 }
 
 // ChatLine::sender[sender="self"] {
 }
 
 // ChatLine::sender[sender="self"] {
@@ -28,6 +34,7 @@ ChatLine::timestamp {
 
 ChatLine::url {
   foreground: palette(link);
 
 ChatLine::url {
   foreground: palette(link);
+  allow-foreground-override: false;
 }
 
 // Markerline gets a nice (and hardly visible) gradient
 }
 
 // Markerline gets a nice (and hardly visible) gradient
index 0420432..163eff5 100644 (file)
@@ -80,40 +80,6 @@ ChatLine[bg-color="0e"] { background: #808080; }
 ChatLine[fg-color="0f"] { foreground: #c0c0c0; }
 ChatLine[bg-color="0f"] { background: #c0c0c0; }
 
 ChatLine[fg-color="0f"] { foreground: #c0c0c0; }
 ChatLine[bg-color="0f"] { background: #c0c0c0; }
 
-    // for sender
-    ChatLine[fg-color="00", sender="self"] { foreground: #ffffff; }
-    ChatLine[bg-color="00", sender="self"] { background: #ffffff; }
-    ChatLine[fg-color="01", sender="self"] { foreground: #000000; }
-    ChatLine[bg-color="01", sender="self"] { background: #000000; }
-    ChatLine[fg-color="02", sender="self"] { foreground: #000080; }
-    ChatLine[bg-color="02", sender="self"] { background: #000080; }
-    ChatLine[fg-color="03", sender="self"] { foreground: #008000; }
-    ChatLine[bg-color="03", sender="self"] { background: #008000; }
-    ChatLine[fg-color="04", sender="self"] { foreground: #ff0000; }
-    ChatLine[bg-color="04", sender="self"] { background: #ff0000; }
-    ChatLine[fg-color="05", sender="self"] { foreground: #800000; }
-    ChatLine[bg-color="05", sender="self"] { background: #800000; }
-    ChatLine[fg-color="06", sender="self"] { foreground: #800080; }
-    ChatLine[bg-color="06", sender="self"] { background: #800080; }
-    ChatLine[fg-color="07", sender="self"] { foreground: #ffa500; }
-    ChatLine[bg-color="07", sender="self"] { background: #ffa500; }
-    ChatLine[fg-color="08", sender="self"] { foreground: #ffff00; }
-    ChatLine[bg-color="08", sender="self"] { background: #ffff00; }
-    ChatLine[fg-color="09", sender="self"] { foreground: #00ff00; }
-    ChatLine[bg-color="09", sender="self"] { background: #00ff00; }
-    ChatLine[fg-color="0a", sender="self"] { foreground: #008080; }
-    ChatLine[bg-color="0a", sender="self"] { background: #008080; }
-    ChatLine[fg-color="0b", sender="self"] { foreground: #00ffff; }
-    ChatLine[bg-color="0b", sender="self"] { background: #00ffff; }
-    ChatLine[fg-color="0c", sender="self"] { foreground: #4169e1; }
-    ChatLine[bg-color="0c", sender="self"] { background: #4169e1; }
-    ChatLine[fg-color="0d", sender="self"] { foreground: #ff00ff; }
-    ChatLine[bg-color="0d", sender="self"] { background: #ff00ff; }
-    ChatLine[fg-color="0e", sender="self"] { foreground: #808080; }
-    ChatLine[bg-color="0e", sender="self"] { background: #808080; }
-    ChatLine[fg-color="0f", sender="self"] { foreground: #c0c0c0; }
-    ChatLine[bg-color="0f", sender="self"] { background: #c0c0c0; }
-
 // mIRC formats
 ChatLine[format="bold"]      { font-weight: bold;}
 ChatLine[format="italic"]    { font-style: italic; }
 // mIRC formats
 ChatLine[format="bold"]      { font-weight: bold;}
 ChatLine[format="italic"]    { font-style: italic; }
index 24032fb..d1a13e8 100644 (file)
@@ -451,6 +451,20 @@ QTextCharFormat QssParser::parseFormat(const QString &qss)
         else if (property == "foreground" || property == "color")
             format.setForeground(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")
         // font-related properties
         else if (property.startsWith("font")) {
             if (property == "font")
@@ -477,6 +491,23 @@ QTextCharFormat QssParser::parseFormat(const QString &qss)
     return format;
 }
 
     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 ********/
 
 
 /******** Brush ********/
 
index 1370c10..f727cee 100644 (file)
@@ -50,8 +50,11 @@ protected:
 
     QTextCharFormat parseFormat(const QString &qss);
 
 
     QTextCharFormat parseFormat(const QString &qss);
 
+    // Parse boolean properties
+    bool parseBoolean(const QString &str, bool *ok = nullptr) const;
+
     // Parse color/brush-related properties
     // Parse color/brush-related properties
-    QBrush parseBrush(const QString &str, bool *ok = 0);
+    QBrush parseBrush(const QString &str, bool *ok = nullptr);
     QColor parseColor(const QString &str);
     ColorTuple parseColorTuple(const QString &str);
     QGradientStops parseGradientStops(const QString &str);
     QColor parseColor(const QString &str);
     ColorTuple parseColorTuple(const QString &str);
     QGradientStops parseGradientStops(const QString &str);
index 0e9a93a..d913fc9 100644 (file)
@@ -417,7 +417,7 @@ QVariant UiStyle::itemData(int role, const QTextCharFormat &format) const
 
 /******** Caching *******/
 
 
 /******** Caching *******/
 
-QTextCharFormat UiStyle::format(quint64 key) const
+QTextCharFormat UiStyle::parsedFormat(quint64 key) const
 {
     return _formats.value(key, QTextCharFormat());
 }
 {
     return _formats.value(key, QTextCharFormat());
 }
@@ -472,11 +472,22 @@ QTextCharFormat UiStyle::format(const Format &format, MessageLabel label) const
     if (charFormat.properties().count())
         return charFormat;
 
     if (charFormat.properties().count())
         return charFormat;
 
+    // Merge all formats except mIRC and extended colors
     mergeFormat(charFormat, format, label & 0xffff0000);  // keep nickhash in label
     mergeFormat(charFormat, format, label & 0xffff0000);  // keep nickhash in label
-
     for (quint32 mask = 0x00000001; mask <= static_cast<quint32>(MessageLabel::Selected); mask <<= 1) {
         if (static_cast<quint32>(label) & mask) {
     for (quint32 mask = 0x00000001; mask <= static_cast<quint32>(MessageLabel::Selected); mask <<= 1) {
         if (static_cast<quint32>(label) & mask) {
-            mergeFormat(charFormat, {format.type, {}, {}}, label & (mask | 0xffff0000));  // Don't re-apply extended colors
+            mergeFormat(charFormat, format, label & (mask | 0xffff0000));
+        }
+    }
+
+    // Merge mIRC and extended colors, if appropriate. These override any color set previously in the format,
+    // unless the AllowForegroundOverride or AllowBackgroundOverride properties are set (via stylesheet).
+    if (_allowMircColors) {
+        mergeColors(charFormat, format, MessageLabel::None);
+        for (quint32 mask = 0x00000001; mask <= static_cast<quint32>(MessageLabel::Selected); mask <<= 1) {
+            if (static_cast<quint32>(label) & mask) {
+                mergeColors(charFormat, format, label & mask);
+            }
         }
     }
 
         }
     }
 
@@ -492,34 +503,12 @@ void UiStyle::mergeFormat(QTextCharFormat &charFormat, const Format &format, Mes
     // TODO: allow combinations for mirc formats and colors (each), e.g. setting a special format for "bold and italic"
     //       or "foreground 01 and background 03"
     if ((format.type & 0xfff00) != FormatType::Base) { // element format
     // TODO: allow combinations for mirc formats and colors (each), e.g. setting a special format for "bold and italic"
     //       or "foreground 01 and background 03"
     if ((format.type & 0xfff00) != FormatType::Base) { // element format
-        for (quint32 mask = 0x00100; mask <= 0x40000; mask <<= 1) {
+        for (quint32 mask = 0x00100; mask <= 0x80000; mask <<= 1) {
             if ((format.type & mask) != FormatType::Base) {
                 mergeSubElementFormat(charFormat, format.type & (mask | 0xff), label);
             }
         }
     }
             if ((format.type & mask) != FormatType::Base) {
                 mergeSubElementFormat(charFormat, format.type & (mask | 0xff), label);
             }
         }
     }
-
-    // Now we handle color codes
-    // We assume that those can't be combined with subelement and message types.
-    if (_allowMircColors) {
-        // Classic mIRC colors (styleable)
-        if ((format.type & 0x00400000) != FormatType::Base)
-            mergeSubElementFormat(charFormat, format.type & 0x0f400000, label);  // foreground
-        if ((format.type & 0x00800000) != FormatType::Base)
-            mergeSubElementFormat(charFormat, format.type & 0xf0800000, label);  // background
-        if ((format.type & 0x00c00000) == static_cast<FormatType>(0x00c00000))
-            mergeSubElementFormat(charFormat, format.type & 0xffc00000, label);  // combination
-
-        // Extended mIRC colors (hardcoded)
-        if (format.foreground.isValid())
-            charFormat.setForeground(format.foreground);
-        if (format.background.isValid())
-            charFormat.setBackground(format.background);
-    }
-
-    // URL
-    if ((format.type & FormatType::Url) != FormatType::Base)
-        mergeSubElementFormat(charFormat, format.type & (FormatType::Url | static_cast<FormatType>(0x000000ff)), label);
 }
 
 
 }
 
 
@@ -527,10 +516,32 @@ void UiStyle::mergeFormat(QTextCharFormat &charFormat, const Format &format, Mes
 void UiStyle::mergeSubElementFormat(QTextCharFormat &fmt, FormatType ftype, MessageLabel label) const
 {
     quint64 key = ftype | label;
 void UiStyle::mergeSubElementFormat(QTextCharFormat &fmt, FormatType ftype, MessageLabel label) const
 {
     quint64 key = ftype | label;
-    fmt.merge(format(key & 0x0000ffffffffff00ull)); // label + subelement
-    fmt.merge(format(key & 0x0000ffffffffffffull)); // label + subelement + msgtype
-    fmt.merge(format(key & 0xffffffffffffff00ull)); // label + subelement + nickhash
-    fmt.merge(format(key & 0xffffffffffffffffull)); // label + subelement + nickhash + msgtype
+    fmt.merge(parsedFormat(key & 0x0000ffffffffff00ull)); // label + subelement
+    fmt.merge(parsedFormat(key & 0x0000ffffffffffffull)); // label + subelement + msgtype
+    fmt.merge(parsedFormat(key & 0xffffffffffffff00ull)); // label + subelement + nickhash
+    fmt.merge(parsedFormat(key & 0xffffffffffffffffull)); // label + subelement + nickhash + msgtype
+}
+
+
+void UiStyle::mergeColors(QTextCharFormat &charFormat, const Format &format, MessageLabel label) const
+{
+    bool allowFg = charFormat.property(static_cast<int>(FormatProperty::AllowForegroundOverride)).toBool();
+    bool allowBg = charFormat.property(static_cast<int>(FormatProperty::AllowBackgroundOverride)).toBool();
+
+    // Classic mIRC colors (styleable)
+    // We assume that those can't be combined with subelement and message types.
+    if (allowFg && (format.type & 0x00400000) != FormatType::Base)
+        charFormat.merge(parsedFormat((format.type & 0x0f400000) | label));  // foreground
+    if (allowBg && (format.type & 0x00800000) != FormatType::Base)
+        charFormat.merge(parsedFormat((format.type & 0xf0800000) | label));  // background
+    if (allowFg && allowBg && (format.type & 0x00c00000) == static_cast<FormatType>(0x00c00000))
+        charFormat.merge(parsedFormat((format.type & 0xffc00000) | label));  // combination
+
+    // Extended mIRC colors (hardcoded)
+    if (allowFg && format.foreground.isValid())
+        charFormat.setForeground(format.foreground);
+    if (allowBg && format.background.isValid())
+        charFormat.setBackground(format.background);
 }
 
 
 }
 
 
index 28ba405..f0618bb 100644 (file)
@@ -128,6 +128,11 @@ public:
         Invalid           = 0xffffffff
     };
 
         Invalid           = 0xffffffff
     };
 
+    enum class FormatProperty {
+        AllowForegroundOverride = QTextFormat::UserProperty,
+        AllowBackgroundOverride
+    };
+
     enum class ColorRole {
         MarkerLine,
         // Sender colors (16 + self)
     enum class ColorRole {
         MarkerLine,
         // Sender colors (16 + self)
@@ -266,11 +271,12 @@ protected:
     void loadStyleSheet();
     QString loadStyleSheet(const QString &name, bool shouldExist = false);
 
     void loadStyleSheet();
     QString loadStyleSheet(const QString &name, bool shouldExist = false);
 
-    QTextCharFormat format(quint64 key) const;
+    QTextCharFormat parsedFormat(quint64 key) const;
     QTextCharFormat cachedFormat(const Format &format, MessageLabel messageLabel) const;
     void setCachedFormat(const QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
     void mergeFormat(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
     void mergeSubElementFormat(QTextCharFormat &charFormat, FormatType formatType, MessageLabel messageLabel) const;
     QTextCharFormat cachedFormat(const Format &format, MessageLabel messageLabel) const;
     void setCachedFormat(const QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
     void mergeFormat(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
     void mergeSubElementFormat(QTextCharFormat &charFormat, FormatType formatType, MessageLabel messageLabel) const;
+    void mergeColors(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
 
     static FormatType formatType(const QString &code);
     static QString formatCode(FormatType);
 
     static FormatType formatType(const QString &code);
     static QString formatCode(FormatType);