From b12b02f1bd5ee8a68bda20622b7ec3eac84f000c Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Wed, 7 Mar 2018 20:59:37 +0100 Subject: [PATCH] uistyle: Define via stylesheet if color codes have an effect 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 | 7 ++++ data/stylesheets/m4yer.qss | 34 ----------------- src/uisupport/qssparser.cpp | 31 ++++++++++++++++ src/uisupport/qssparser.h | 5 ++- src/uisupport/uistyle.cpp | 71 +++++++++++++++++++++--------------- src/uisupport/uistyle.h | 8 +++- 6 files changed, 90 insertions(+), 66 deletions(-) diff --git a/data/stylesheets/default.qss b/data/stylesheets/default.qss index 8d5ae323..506dda97 100644 --- a/data/stylesheets/default.qss +++ b/data/stylesheets/default.qss @@ -6,16 +6,22 @@ // 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; + allow-background-override: false; } ChatLine[label="selected"] { foreground: palette(highlighted-text); background: palette(highlight); + allow-foreground-override: false; + allow-background-override: false; } // ChatLine::sender[sender="self"] { @@ -28,6 +34,7 @@ ChatLine::timestamp { ChatLine::url { foreground: palette(link); + allow-foreground-override: false; } // Markerline gets a nice (and hardly visible) gradient diff --git a/data/stylesheets/m4yer.qss b/data/stylesheets/m4yer.qss index 04204328..163eff5d 100644 --- a/data/stylesheets/m4yer.qss +++ b/data/stylesheets/m4yer.qss @@ -80,40 +80,6 @@ ChatLine[bg-color="0e"] { background: #808080; } 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; } diff --git a/src/uisupport/qssparser.cpp b/src/uisupport/qssparser.cpp index 24032fbf..d1a13e8f 100644 --- a/src/uisupport/qssparser.cpp +++ b/src/uisupport/qssparser.cpp @@ -451,6 +451,20 @@ QTextCharFormat QssParser::parseFormat(const QString &qss) 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(UiStyle::FormatProperty::AllowForegroundOverride), v); + } + else if (property == "allow-background-override") { + bool ok; + bool v = parseBoolean(value, &ok); + if (ok) + format.setProperty(static_cast(UiStyle::FormatProperty::AllowBackgroundOverride), v); + } + // font-related properties else if (property.startsWith("font")) { if (property == "font") @@ -477,6 +491,23 @@ QTextCharFormat QssParser::parseFormat(const QString &qss) 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 ********/ diff --git a/src/uisupport/qssparser.h b/src/uisupport/qssparser.h index 1370c105..f727cee7 100644 --- a/src/uisupport/qssparser.h +++ b/src/uisupport/qssparser.h @@ -50,8 +50,11 @@ protected: QTextCharFormat parseFormat(const QString &qss); + // Parse boolean properties + bool parseBoolean(const QString &str, bool *ok = nullptr) const; + // 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); diff --git a/src/uisupport/uistyle.cpp b/src/uisupport/uistyle.cpp index 0e9a93a0..d913fc9b 100644 --- a/src/uisupport/uistyle.cpp +++ b/src/uisupport/uistyle.cpp @@ -417,7 +417,7 @@ QVariant UiStyle::itemData(int role, const QTextCharFormat &format) const /******** Caching *******/ -QTextCharFormat UiStyle::format(quint64 key) const +QTextCharFormat UiStyle::parsedFormat(quint64 key) const { return _formats.value(key, QTextCharFormat()); } @@ -472,11 +472,22 @@ QTextCharFormat UiStyle::format(const Format &format, MessageLabel label) const if (charFormat.properties().count()) return charFormat; + // Merge all formats except mIRC and extended colors mergeFormat(charFormat, format, label & 0xffff0000); // keep nickhash in label - for (quint32 mask = 0x00000001; mask <= static_cast(MessageLabel::Selected); mask <<= 1) { if (static_cast(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(MessageLabel::Selected); mask <<= 1) { + if (static_cast(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 - 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); } } } - - // 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(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(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; - 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(FormatProperty::AllowForegroundOverride)).toBool(); + bool allowBg = charFormat.property(static_cast(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(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); } diff --git a/src/uisupport/uistyle.h b/src/uisupport/uistyle.h index 28ba4054..f0618bbb 100644 --- a/src/uisupport/uistyle.h +++ b/src/uisupport/uistyle.h @@ -128,6 +128,11 @@ public: Invalid = 0xffffffff }; + enum class FormatProperty { + AllowForegroundOverride = QTextFormat::UserProperty, + AllowBackgroundOverride + }; + enum class ColorRole { MarkerLine, // Sender colors (16 + self) @@ -266,11 +271,12 @@ protected: 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; + void mergeColors(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const; static FormatType formatType(const QString &code); static QString formatCode(FormatType); -- 2.20.1