summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2022-01-13 22:42:59 +0100
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2022-01-15 19:21:57 +0100
commitcb27ed30f7bc58474eba991f837843eb76cbd339 (patch)
treef0b4c5b93301792e01ae42ec4fbfc9d582ddff31 /src
parentda6c8b2fc6bc1a29b8a88c4ecce53eaf811eb06c (diff)
QCSS: Support Qt 5-style integer property selectors
In Qt 5 style sheets, objects could be selected by an enum-type property using the integer value of the enum value, e.g QToolButton[popupMode="1"] { ... } In Qt 6, the the new meta type system and QVariant implementation enabled QVariant::toString to return the string representation of the enum value instead for a property containing an enum. Since QStyleSheetStyle's attribute matching is string based, this breaks the Qt 5 style selector, and QCSS code instead needs to use e.g. QToolButton[popupMode=MenuButtonPopup] { ... } While the new syntax is arguably preferable, this is an unintentional change that silently breaks style sheet code (no error or warning at compile- or run-time). To support Qt 5-style selectors, we have to change the StyleSelector interface of the QCssParser API so that we can pass through what type of value the attribute extractor should return; if an integer string "1" is provided, then we need to compare the enum integer value; if the string provided does not represent a number, then we need to compare the name of the enum value. Since the pure virtual attribute() method that needs to be implemented to extract the attribute value of the node is implemented in modules outside qtbase, add a second virtual method that takes the entire QCss::AttributeSelector, which includes the value to match. Extractor implementations can use it to evaluate which type of data to return for an exact match. The default implementation calls the old attribute() method so that existing StyleSelector implementations continue to work. Make the respective change in the QStyleSheetStyleSelector, and simplify the surrounding code. Adjust other StyleSelector implemnentations in qtbase. As a drive-by, remove the superfluous virtual declaration from those overrides. Once submodules are adjusted to override this virtual function instead of the (now no longer pure) virtual attribute() method, that method can be removed. Pick-to: 6.3 6.2 Fixes: QTBUG-99642 Change-Id: I9a2b3498f77bf7cab5e90980b7dab2f621d3d859 Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/text/qcssparser.cpp4
-rw-r--r--src/gui/text/qcssparser_p.h8
-rw-r--r--src/gui/text/qtexthtmlparser.cpp20
-rw-r--r--src/widgets/styles/qstylesheetstyle.cpp54
4 files changed, 51 insertions, 35 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index da3de4d651..283c3d346d 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -1988,7 +1988,7 @@ bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
QStringList StyleSelector::nodeIds(NodePtr node) const
{
- return QStringList(attribute(node, QLatin1String("id")));
+ return QStringList(attributeValue(node, QCss::AttributeSelector{QLatin1String("id"), {}, AttributeSelector::NoMatch}));
}
bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
@@ -2060,7 +2060,7 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
- const QString attrValue = attribute(node, a.name);
+ const QString attrValue = attributeValue(node, a);
if (attrValue.isNull())
return false;
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index 56af5c8bb2..dc982018b9 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -557,11 +557,10 @@ struct AttributeSelector
MatchEndsWith,
MatchContains
};
- inline AttributeSelector() : valueMatchCriterium(NoMatch) {}
QString name;
QString value;
- ValueMatchType valueMatchCriterium;
+ ValueMatchType valueMatchCriterium = NoMatch;
};
QT_CSS_DECLARE_TYPEINFO(AttributeSelector, Q_RELOCATABLE_TYPE)
@@ -666,7 +665,10 @@ public:
QList<Declaration> declarationsForNode(NodePtr node, const char *extraPseudo = nullptr);
virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const;
- virtual QString attribute(NodePtr node, const QString &name) const = 0;
+ // ### TODO keep until QtSvg is updated with new qtbase and has overridden attributeValue
+ virtual QString attribute(NodePtr, const QString &) const { return QString(); }
+ virtual QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const
+ { return attribute(node, aSelector.name); }
virtual bool hasAttributes(NodePtr node) const = 0;
virtual QStringList nodeIds(NodePtr node) const;
virtual QStringList nodeNames(NodePtr node) const = 0;
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index f97b8a672a..2494fa472c 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -1848,14 +1848,14 @@ public:
inline QTextHtmlStyleSelector(const QTextHtmlParser *parser)
: parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
- virtual QStringList nodeNames(NodePtr node) const override;
- virtual QString attribute(NodePtr node, const QString &name) const override;
- virtual bool hasAttributes(NodePtr node) const override;
- virtual bool isNullNode(NodePtr node) const override;
- virtual NodePtr parentNode(NodePtr node) const override;
- virtual NodePtr previousSiblingNode(NodePtr node) const override;
- virtual NodePtr duplicateNode(NodePtr node) const override;
- virtual void freeNode(NodePtr node) const override;
+ QStringList nodeNames(NodePtr node) const override;
+ QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const override;
+ bool hasAttributes(NodePtr node) const override;
+ bool isNullNode(NodePtr node) const override;
+ NodePtr parentNode(NodePtr node) const override;
+ NodePtr previousSiblingNode(NodePtr node) const override;
+ NodePtr duplicateNode(NodePtr node) const override;
+ void freeNode(NodePtr node) const override;
private:
const QTextHtmlParser *parser;
@@ -1879,10 +1879,10 @@ static inline int findAttribute(const QStringList &attributes, const QString &na
return idx;
}
-QString QTextHtmlStyleSelector::attribute(NodePtr node, const QString &name) const
+QString QTextHtmlStyleSelector::attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const
{
const QStringList &attributes = parser->at(node.id).attributes;
- const int idx = findAttribute(attributes, name);
+ const int idx = findAttribute(attributes, aSelector.name);
if (idx == -1)
return QString();
return attributes.at(idx + 1);
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp
index 1449def8c5..b799948933 100644
--- a/src/widgets/styles/qstylesheetstyle.cpp
+++ b/src/widgets/styles/qstylesheetstyle.cpp
@@ -1570,39 +1570,53 @@ public:
} while (metaObject != nullptr);
return result;
}
- QString attribute(NodePtr node, const QString& name) const override
+ QString attributeValue(NodePtr node, const QCss::AttributeSelector& aSelector) const override
{
if (isNullNode(node))
return QString();
+ const QString &name = aSelector.name;
QHash<QString, QString> &cache = m_attributeCache[OBJECT_PTR(node)];
QHash<QString, QString>::const_iterator cacheIt = cache.constFind(name);
if (cacheIt != cache.constEnd())
return cacheIt.value();
+ QVariant value;
+ QString valueStr;
QObject *obj = OBJECT_PTR(node);
- QVariant value = obj->property(name.toLatin1());
- if (!value.isValid()) {
- if (name == QLatin1String("class")) {
- QString className = QString::fromLatin1(obj->metaObject()->className());
- if (className.contains(QLatin1Char(':')))
- className.replace(QLatin1Char(':'), QLatin1Char('-'));
- cache[name] = className;
- return className;
- } else if (name == QLatin1String("style")) {
- QWidget *w = qobject_cast<QWidget *>(obj);
- QStyleSheetStyle *proxy = w ? qt_styleSheet(w->style()) : nullptr;
- if (proxy) {
- QString styleName = QString::fromLatin1(proxy->baseStyle()->metaObject()->className());
- cache[name] = styleName;
- return styleName;
+ const int propertyIndex = obj->metaObject()->indexOfProperty(name.toLatin1());
+ if (propertyIndex == -1) {
+ value = obj->property(name.toLatin1()); // might be a dynamic property
+ if (!value.isValid()) {
+ if (name == u"class"_qs) {
+ QString className = QString::fromLatin1(obj->metaObject()->className());
+ if (className.contains(QLatin1Char(':')))
+ className.replace(QLatin1Char(':'), QLatin1Char('-'));
+ valueStr = className;
+ } else if (name == u"style"_qs) {
+ QWidget *w = qobject_cast<QWidget *>(obj);
+ QStyleSheetStyle *proxy = w ? qt_styleSheet(w->style()) : nullptr;
+ if (proxy)
+ valueStr = QString::fromLatin1(proxy->baseStyle()->metaObject()->className());
}
}
+ } else {
+ const QMetaProperty property = obj->metaObject()->property(propertyIndex);
+ value = property.read(obj);
+ // support Qt 5 selector syntax, which required the integer enum value
+ if (property.isEnumType()) {
+ bool isNumber;
+ aSelector.value.toInt(&isNumber);
+ if (isNumber)
+ value.convert(QMetaType::fromType<int>());
+ }
+ }
+ if (value.isValid()) {
+ valueStr = (value.userType() == QMetaType::QStringList
+ || value.userType() == QMetaType::QVariantList)
+ ? value.toStringList().join(QLatin1Char(' '))
+ : value.toString();
}
- QString valueStr = (value.userType() == QMetaType::QStringList
- || value.userType() == QMetaType::QVariantList)
- ? value.toStringList().join(QLatin1Char(' '))
- : value.toString();
cache[name] = valueStr;
return valueStr;
}