diff options
Diffstat (limited to 'src/widgets/styles/qstylesheetstyle.cpp')
-rw-r--r-- | src/widgets/styles/qstylesheetstyle.cpp | 843 |
1 files changed, 481 insertions, 362 deletions
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index a49826b62a..655b224617 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qglobal.h> #include "qstylesheetstyle_p.h" @@ -95,7 +59,7 @@ #include <private/qstyleanimation_p.h> #endif #if QT_CONFIG(tabbar) -#include <qtabbar.h> +#include <private/qtabbar_p.h> #endif #include <QMetaProperty> #if QT_CONFIG(mainwindow) @@ -134,6 +98,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + using namespace QCss; @@ -151,7 +117,7 @@ static QStyleSheetStyleCaches *styleSheetCaches = nullptr; * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like: * QStyleSheetStyle -> ProxyStyle -> QStyleSheetStyle -> OriginalStyle * Recursion may happen if the style call the widget()->style() again. - * Not to mention the performence penalty of having two lookup of rules. + * Not to mention the performance penalty of having two lookup of rules. * * The first instance of QStyleSheetStyle will set globalStyleSheetStyle to itself. The second one * will notice the globalStyleSheetStyle is not istelf and call its base style directly. @@ -172,8 +138,6 @@ class QStyleSheetStyleRecursionGuard if (globalStyleSheetStyle != 0 && globalStyleSheetStyle != this) { RETURN; } \ QStyleSheetStyleRecursionGuard recursion_guard(this); -#define ceil(x) ((int)(x) + ((x) > 0 && (x) != (int)(x))) - enum PseudoElement { PseudoElement_None, PseudoElement_DownArrow, @@ -470,15 +434,26 @@ struct QStyleSheetBoxData : public QSharedData struct QStyleSheetPaletteData : public QSharedData { - QStyleSheetPaletteData(const QBrush &fg, const QBrush &sfg, const QBrush &sbg, - const QBrush &abg) - : foreground(fg), selectionForeground(sfg), selectionBackground(sbg), - alternateBackground(abg) { } + QStyleSheetPaletteData(const QBrush &foreground, + const QBrush &selectedForeground, + const QBrush &selectedBackground, + const QBrush &alternateBackground, + const QBrush &placeHolderTextForeground, + const QBrush &accent) + : foreground(foreground) + , selectionForeground(selectedForeground) + , selectionBackground(selectedBackground) + , alternateBackground(alternateBackground) + , placeholderForeground(placeHolderTextForeground) + , accent(accent) + { } QBrush foreground; QBrush selectionForeground; QBrush selectionBackground; QBrush alternateBackground; + QBrush placeholderForeground; + QBrush accent; }; struct QStyleSheetGeometryData : public QSharedData @@ -650,7 +625,7 @@ public: Q_DECLARE_TYPEINFO(QRenderRule, Q_RELOCATABLE_TYPE); /////////////////////////////////////////////////////////////////////////////////////////// -static const char knownStyleHints[][45] = { +static constexpr std::array<const char*, 90> knownStyleHints = { "activate-on-singleclick", "alignment", "arrow-keys-navigate-into-children", @@ -743,13 +718,10 @@ static const char knownStyleHints[][45] = { "widget-animation-duration" }; -static const int numKnownStyleHints = sizeof(knownStyleHints)/sizeof(knownStyleHints[0]); - -static QList<QVariant> subControlLayout(const QString& layout) +static QList<QVariant> subControlLayout(QByteArrayView layout) { QList<QVariant> buttons; - for (int i = 0; i < layout.count(); i++) { - int button = layout[i].toLatin1(); + for (int button : layout) { switch (button) { case 'm': buttons.append(PseudoElement_MdiMinButton); @@ -804,17 +776,16 @@ QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget const bool isMaximized = tb->titleBarState & Qt::WindowMaximized; QRenderRule subRule = renderRule(w, tb); QRect cr = subRule.contentsRect(tb->rect); - QList<QVariant> layout = subRule.styleHint(QLatin1String("button-layout")).toList(); + QList<QVariant> layout = subRule.styleHint("button-layout"_L1).toList(); if (layout.isEmpty()) - layout = subControlLayout(QLatin1String("I(T)HSmMX")); + layout = subControlLayout("I(T)HSmMX"_L1); int offsets[3] = { 0, 0, 0 }; enum Where { Left, Right, Center, NoWhere } where = Left; QList<ButtonInfo> infos; - const int numLayouts = layout.size(); - infos.reserve(numLayouts); - for (int i = 0; i < numLayouts; i++) { - const int element = layout[i].toInt(); + infos.reserve(layout.size()); + for (const QVariant &val : std::as_const(layout)) { + const int element = val.toInt(); if (element == '(') { where = Center; } else if (element == ')') { @@ -873,8 +844,7 @@ QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget } } - for (int i = 0; i < infos.size(); i++) { - const ButtonInfo &info = infos[i]; + for (const ButtonInfo &info : std::as_const(infos)) { QRect lr = cr; switch (info.where) { case Center: { @@ -990,10 +960,17 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * bg = new QStyleSheetBackgroundData(brush, pixmap, repeat, alignment, origin, attachment, clip); } - QBrush sfg, fg; - QBrush sbg, abg; - if (v.extractPalette(&fg, &sfg, &sbg, &abg)) - pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg); + QBrush foreground; + QBrush selectedForeground; + QBrush selectedBackground; + QBrush alternateBackground; + QBrush placeHolderTextForeground; + QBrush accent; + if (v.extractPalette(&foreground, &selectedForeground, &selectedBackground, + &alternateBackground, &placeHolderTextForeground, &accent)) { + pal = new QStyleSheetPaletteData(foreground, selectedForeground, selectedBackground, + alternateBackground, placeHolderTextForeground, accent); + } QIcon imgIcon; alignment = Qt::AlignCenter; @@ -1014,7 +991,7 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * palette = QToolTip::palette(); #endif - for (int i = 0; i < declarations.count(); i++) { + for (int i = 0; i < declarations.size(); i++) { const Declaration& decl = declarations.at(i); if (decl.d->propertyId == BorderImage) { QString uri; @@ -1022,7 +999,7 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * int cuts[4]; decl.borderImageValue(&uri, cuts, &horizStretch, &vertStretch); - if (uri.isEmpty() || uri == QLatin1String("none")) { + if (uri.isEmpty() || uri == "none"_L1) { if (bd && bd->bi) bd->bi->pixmap = QPixmap(); } else { @@ -1044,20 +1021,20 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * int role = decl.d->values.at(0).variant.toInt(); if (role >= Value_FirstColorRole && role <= Value_LastColorRole) defaultBackground = palette.color((QPalette::ColorRole)(role-Value_FirstColorRole)); - } else if (decl.d->property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) { + } else if (decl.d->property.startsWith("qproperty-"_L1, Qt::CaseInsensitive)) { // intentionally left blank... } else if (decl.d->propertyId == UnknownProperty) { bool knownStyleHint = false; - for (int i = 0; i < numKnownStyleHints; i++) { - QLatin1String styleHint(knownStyleHints[i]); + for (const auto sh : knownStyleHints) { + QLatin1StringView styleHint(sh); if (decl.d->property.compare(styleHint) == 0) { QString hintName = QString(styleHint); QVariant hintValue; - if (hintName.endsWith(QLatin1String("alignment"))) { + if (hintName.endsWith("alignment"_L1)) { hintValue = (int) decl.alignmentValue(); - } else if (hintName.endsWith(QLatin1String("color"))) { + } else if (hintName.endsWith("color"_L1)) { hintValue = (int) decl.colorValue().rgba(); - } else if (hintName.endsWith(QLatin1String("size"))) { + } else if (hintName.endsWith("size"_L1)) { // Check only for the 'em' case const QString valueString = decl.d->values.at(0).variant.toString(); const bool isEmSize = valueString.endsWith(u"em", Qt::CaseInsensitive); @@ -1087,11 +1064,11 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * // Normal case where we receive a 'px' or 'pt' unit hintValue = decl.sizeValue(); } - } else if (hintName.endsWith(QLatin1String("icon"))) { + } else if (hintName.endsWith("icon"_L1)) { hintValue = decl.iconValue(); - } else if (hintName == QLatin1String("button-layout") - && decl.d->values.count() != 0 && decl.d->values.at(0).type == Value::String) { - hintValue = subControlLayout(decl.d->values.at(0).variant.toString()); + } else if (hintName == "button-layout"_L1 && decl.d->values.size() != 0 + && decl.d->values.at(0).type == QCss::Value::String) { + hintValue = subControlLayout(decl.d->values.at(0).variant.toString().toLatin1()); } else { int integer; decl.intValue(&integer); @@ -1418,7 +1395,7 @@ void QRenderRule::drawBackground(QPainter *p, const QRect& rect, const QPoint& o // ### fix for gradients const QPainterPath &borderPath = borderClip(originRect(rect, origin)); if (!borderPath.isEmpty()) { - // Drawn intead of being used as clipping path for better visual quality + // Drawn instead of being used as clipping path for better visual quality bool wasAntialiased = p->renderHints() & QPainter::Antialiasing; p->setRenderHint(QPainter::Antialiasing); p->fillPath(borderPath, brush); @@ -1483,6 +1460,16 @@ void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette p->setBrush(QPalette::AlternateBase, pal->alternateBackground); } +void setDefault(QPalette *palette, QPalette::ColorGroup group, QPalette::ColorRole role, + const QBrush &defaultBrush, const QWidget *widget) +{ + const QPalette &widgetPalette = widget->palette(); + if (widgetPalette.isBrushSet(group, role)) + palette->setBrush(group, role, widgetPalette.brush(group, role)); + else + palette->setBrush(group, role, defaultBrush); +} + void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const QWidget *w, bool embedded) { if (bg && bg->brush.style() != Qt::NoBrush) { @@ -1504,11 +1491,15 @@ void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const Q return; if (pal->foreground.style() != Qt::NoBrush) { - p->setBrush(cg, QPalette::ButtonText, pal->foreground); - p->setBrush(cg, w->foregroundRole(), pal->foreground); - p->setBrush(cg, QPalette::WindowText, pal->foreground); - p->setBrush(cg, QPalette::Text, pal->foreground); - p->setBrush(cg, QPalette::PlaceholderText, pal->foreground); + setDefault(p, cg, QPalette::ButtonText, pal->foreground, w); + setDefault(p, cg, w->foregroundRole(), pal->foreground, w); + setDefault(p, cg, QPalette::WindowText, pal->foreground, w); + setDefault(p, cg, QPalette::Text, pal->foreground, w); + QColor phColor(pal->foreground.color()); + phColor.setAlpha((phColor.alpha() + 1) / 2); + QBrush placeholder = pal->foreground; + placeholder.setColor(phColor); + setDefault(p, cg, QPalette::PlaceholderText, placeholder, w); } if (pal->selectionBackground.style() != Qt::NoBrush) p->setBrush(cg, QPalette::Highlight, pal->selectionBackground); @@ -1516,6 +1507,10 @@ void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const Q p->setBrush(cg, QPalette::HighlightedText, pal->selectionForeground); if (pal->alternateBackground.style() != Qt::NoBrush) p->setBrush(cg, QPalette::AlternateBase, pal->alternateBackground); + if (pal->placeholderForeground.style() != Qt::NoBrush) + p->setBrush(cg, QPalette::PlaceholderText, pal->placeholderForeground); + if (pal->accent.style() != Qt::NoBrush) + p->setBrush(cg, QPalette::Accent, pal->accent); } bool QRenderRule::hasModification() const @@ -1561,48 +1556,62 @@ public: const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) if (qstrcmp(metaObject->className(), "QTipLabel") == 0) - return QStringList(QLatin1String("QToolTip")); + return QStringList("QToolTip"_L1); #endif QStringList result; do { - result += QString::fromLatin1(metaObject->className()).replace(QLatin1Char(':'), QLatin1Char('-')); + result += QString::fromLatin1(metaObject->className()).replace(u':', u'-'); metaObject = metaObject->superClass(); } 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 == "class"_L1) { + QString className = QString::fromLatin1(obj->metaObject()->className()); + if (className.contains(u':')) + className.replace(u':', u'-'); + valueStr = className; + } else if (name == "style"_L1) { + 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(u' ') + : value.toString(); } - QString valueStr = (value.userType() == QMetaType::QStringList - || value.userType() == QMetaType::QVariantList) - ? value.toStringList().join(QLatin1Char(' ')) - : value.toString(); cache[name] = valueStr; return valueStr; } @@ -1613,11 +1622,11 @@ public: const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) if (qstrcmp(metaObject->className(), "QTipLabel") == 0) - return nodeName == QLatin1String("QToolTip"); + return nodeName == "QToolTip"_L1; #endif do { - const ushort *uc = (const ushort *)nodeName.constData(); - const ushort *e = uc + nodeName.length(); + const auto *uc = reinterpret_cast<const char16_t *>(nodeName.constData()); + const auto *e = uc + nodeName.size(); const uchar *c = (const uchar *)metaObject->className(); while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) { ++uc; @@ -1667,7 +1676,8 @@ QList<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const defaultSs = getDefaultStyleSheet(); QStyle *bs = baseStyle(); styleSheetCaches->styleSheetCache.insert(bs, defaultSs); - QObject::connect(bs, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection); + QObject::connect(bs, &QStyle::destroyed, styleSheetCaches, + &QStyleSheetStyleCaches::styleDestroyed); } else { defaultSs = defaultCacheIt.value(); } @@ -1678,7 +1688,7 @@ QList<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp); if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) { QString ss = qApp->styleSheet(); - if (ss.startsWith(QLatin1String("file:///"))) + if (ss.startsWith("file:///"_L1)) ss.remove(0, 8); parser.init(ss, qApp->styleSheet() != ss); if (Q_UNLIKELY(!parser.parse(&appSs))) @@ -1702,7 +1712,7 @@ QList<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const if (objCacheIt == styleSheetCaches->styleSheetCache.constEnd()) { parser.init(styleSheet); if (!parser.parse(&ss)) { - parser.init(QLatin1String("* {") + styleSheet + QLatin1Char('}')); + parser.init("* {"_L1 + styleSheet + u'}'); if (Q_UNLIKELY(!parser.parse(&ss))) qWarning() << "Could not parse stylesheet of object" << o; } @@ -1714,8 +1724,8 @@ QList<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const objectSs.append(ss); } - for (int i = 0; i < objectSs.count(); i++) - objectSs[i].depth = objectSs.count() - i + 2; + for (int i = 0; i < objectSs.size(); i++) + objectSs[i].depth = objectSs.size() - i + 2; styleSelector.styleSheets += objectSs; @@ -1732,7 +1742,7 @@ static QList<Declaration> declarations(const QList<StyleRule> &styleRules, const quint64 pseudoClass = PseudoClass_Unspecified) { QList<Declaration> decls; - for (int i = 0; i < styleRules.count(); i++) { + for (int i = 0; i < styleRules.size(); i++) { const Selector& selector = styleRules.at(i).selectors.at(0); // Rules with pseudo elements don't cascade. This is an intentional // diversion for CSS @@ -1839,9 +1849,9 @@ static void qt_check_if_internal_object(const QObject **obj, int *element) Q_UNUSED(element); #else if (*obj && qstrcmp((*obj)->metaObject()->className(), "QDockWidgetTitleButton") == 0) { - if ((*obj)->objectName() == QLatin1String("qt_dockwidget_closebutton")) { + if ((*obj)->objectName() == "qt_dockwidget_closebutton"_L1) { *element = PseudoElement_DockWidgetCloseButton; - } else if ((*obj)->objectName() == QLatin1String("qt_dockwidget_floatbutton")) { + } else if ((*obj)->objectName() == "qt_dockwidget_floatbutton"_L1) { *element = PseudoElement_DockWidgetFloatButton; } *obj = (*obj)->parent(); @@ -1862,7 +1872,7 @@ QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, int element, quint6 quint64 stateMask = 0; const QList<StyleRule> rules = styleRules(obj); - for (int i = 0; i < rules.count(); i++) { + for (int i = 0; i < rules.size(); i++) { const Selector& selector = rules.at(i).selectors.at(0); quint64 negated = 0; stateMask |= selector.pseudoClass(&negated); @@ -1877,7 +1887,7 @@ QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, int element, quint6 } - const QString part = QLatin1String(knownPseudoElements[element].name); + const QString part = QLatin1StringView(knownPseudoElements[element].name); QList<Declaration> decls = declarations(rules, part, state); QRenderRule newRule(decls, obj); cache[state] = newRule; @@ -2183,8 +2193,8 @@ bool QStyleSheetStyle::hasStyleRule(const QObject *obj, int part) const return result; } - QString pseudoElement = QLatin1String(knownPseudoElements[part].name); - for (int i = 0; i < rules.count(); i++) { + auto pseudoElement = QLatin1StringView(knownPseudoElements[part].name); + for (int i = 0; i < rules.size(); i++) { const Selector& selector = rules.at(i).selectors.at(0); if (pseudoElement.compare(selector.pseudoElement(), Qt::CaseInsensitive) == 0) { cache[part] = true; @@ -2491,11 +2501,12 @@ static QWidget *embeddedWidget(QWidget *w) } /** \internal - in case w is an embedded widget, return the container widget - (i.e, the widget for which the rules actualy apply) - (exemple, if w is a lineedit embedded in a combobox, return the combobox) + Returns the widget whose style rules apply to \a w. + + When \a w is an embedded widget, this is the container widget. + For example, if w is a line edit embedded in a combobox, this returns the combobox. + When \a w is not embedded, this function return \a w itself. - if w is not embedded, return w itself */ static QWidget *containerWidget(const QWidget *w) { @@ -2583,8 +2594,9 @@ static quint64 extendedPseudoClass(const QWidget *w) } else if (const QPlainTextEdit *edit = qobject_cast<const QPlainTextEdit *>(w)) { pc |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable); - } + } else #endif + {} return pc; } @@ -2654,16 +2666,16 @@ void QStyleSheetStyle::setProperties(QWidget *w) { // scan decls for final occurrence of each "qproperty" QDuplicateTracker<QString> propertySet(decls.size()); - for (int i = decls.count() - 1; i >= 0; --i) { + for (int i = decls.size() - 1; i >= 0; --i) { const QString property = decls.at(i).d->property; - if (!property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) + if (!property.startsWith("qproperty-"_L1, Qt::CaseInsensitive)) continue; if (!propertySet.hasSeen(property)) finals.append(i); } } - for (int i = finals.count() - 1; i >= 0; --i) { + for (int i = finals.size() - 1; i >= 0; --i) { const Declaration &decl = decls.at(finals[i]); QStringView property = decl.d->property; property = property.mid(10); // strip "qproperty-" @@ -2820,7 +2832,7 @@ static void updateObjects(const QList<const QObject *>& objects) QCoreApplication::sendEvent(widget, &event); QList<const QObject *> children; children.reserve(widget->children().size() + 1); - for (auto child: qAsConst(widget->children())) + for (auto child: std::as_const(widget->children())) children.append(child); updateObjects(children); } @@ -2888,7 +2900,9 @@ bool QStyleSheetStyle::initObject(const QObject *obj) const const_cast<QWidget *>(w)->setAttribute(Qt::WA_StyleSheet, true); } - QObject::connect(obj, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(objectDestroyed(QObject*)), Qt::UniqueConnection); + connect(obj, &QObject::destroyed, + styleSheetCaches, &QStyleSheetStyleCaches::objectDestroyed, + Qt::UniqueConnection); return true; } @@ -2902,7 +2916,7 @@ void QStyleSheetStyle::polish(QWidget *w) if (styleSheetCaches->styleRulesCache.contains(w)) { // the widget accessed its style pointer before polish (or repolish) - // (exemple: the QAbstractSpinBox constructor ask for the stylehint) + // (example: the QAbstractSpinBox constructor ask for the stylehint) styleSheetCaches->styleRulesCache.remove(w); styleSheetCaches->hasStyleRuleCache.remove(w); styleSheetCaches->renderRulesCache.remove(w); @@ -2915,7 +2929,7 @@ void QStyleSheetStyle::polish(QWidget *w) //set the WA_Hover attribute if one of the selector depends of the hover state QList<StyleRule> rules = styleRules(w); - for (int i = 0; i < rules.count(); i++) { + for (int i = 0; i < rules.size(); i++) { const Selector& selector = rules.at(i).selectors.at(0); quint64 negated = 0; quint64 cssClass = selector.pseudoClass(&negated); @@ -2932,10 +2946,10 @@ void QStyleSheetStyle::polish(QWidget *w) QRenderRule rule = renderRule(sa, PseudoElement_None, PseudoClass_Enabled); if ((rule.hasBorder() && rule.border()->hasBorderImage()) || (rule.hasBackground() && !rule.background()->pixmap.isNull())) { - QObject::connect(sa->horizontalScrollBar(), SIGNAL(valueChanged(int)), - sa, SLOT(update()), Qt::UniqueConnection); - QObject::connect(sa->verticalScrollBar(), SIGNAL(valueChanged(int)), - sa, SLOT(update()), Qt::UniqueConnection); + connect(sa->horizontalScrollBar(), &QScrollBar::valueChanged, + sa, QOverload<>::of(&QAbstractScrollArea::update), Qt::UniqueConnection); + connect(sa->verticalScrollBar(), &QScrollBar::valueChanged, + sa, QOverload<>::of(&QAbstractScrollArea::update), Qt::UniqueConnection); } } #endif @@ -3005,7 +3019,7 @@ void QStyleSheetStyle::repolish(QWidget *w) { QList<const QObject *> children; children.reserve(w->children().size() + 1); - for (auto child: qAsConst(w->children())) + for (auto child: std::as_const(w->children())) children.append(child); children.append(w); styleSheetCaches->styleSheetCache.remove(w); @@ -3038,13 +3052,13 @@ void QStyleSheetStyle::unpolish(QWidget *w) setGeometry(w); w->setAttribute(Qt::WA_StyleSheetTarget, false); w->setAttribute(Qt::WA_StyleSheet, false); - QObject::disconnect(w, nullptr, this, nullptr); + w->disconnect(this); #if QT_CONFIG(scrollarea) if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(w)) { - QObject::disconnect(sa->horizontalScrollBar(), SIGNAL(valueChanged(int)), - sa, SLOT(update())); - QObject::disconnect(sa->verticalScrollBar(), SIGNAL(valueChanged(int)), - sa, SLOT(update())); + disconnect(sa->horizontalScrollBar(), &QScrollBar::valueChanged, + sa, QOverload<>::of(&QAbstractScrollArea::update)); + disconnect(sa->verticalScrollBar(), &QScrollBar::valueChanged, + sa, QOverload<>::of(&QAbstractScrollArea::update)); } #endif baseStyle()->unpolish(w); @@ -3060,16 +3074,6 @@ void QStyleSheetStyle::unpolish(QApplication *app) styleSheetCaches->styleSheetCache.remove(qApp); } -#if QT_CONFIG(tabbar) -inline static bool verticalTabs(QTabBar::Shape shape) -{ - return shape == QTabBar::RoundedWest - || shape == QTabBar::RoundedEast - || shape == QTabBar::TriangularWest - || shape == QTabBar::TriangularEast; -} -#endif // QT_CONFIG(tabbar) - void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *w) const { @@ -3273,7 +3277,6 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC rule.configurePalette(&toolOpt.palette, QPalette::ButtonText, QPalette::Button); toolOpt.font = rule.font.resolve(toolOpt.font); toolOpt.rect = rule.borderRect(opt->rect); - bool customArrow = tool->features & QStyleOptionToolButton::Arrow; const auto customArrowElement = [tool]{ switch (tool->arrowType) { case Qt::DownArrow: return PseudoElement_DownArrow; @@ -3284,9 +3287,29 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } return PseudoElement_None; }; - bool customDropDown = tool->features & QStyleOptionToolButton::MenuButtonPopup; + // if arrow/menu/indicators are requested, either draw them using the available rule, + // or let the base style draw them; but not both + const bool drawArrow = tool->features & QStyleOptionToolButton::Arrow; + bool customArrow = drawArrow && hasStyleRule(w, customArrowElement()); + if (customArrow) { + toolOpt.features &= ~QStyleOptionToolButton::Arrow; + toolOpt.text = QString(); // we need to draw the arrow and the text ourselves + } + bool drawDropDown = tool->features & QStyleOptionToolButton::MenuButtonPopup; + bool customDropDown = drawDropDown && hasStyleRule(w, PseudoElement_ToolButtonMenu); bool customDropDownArrow = false; - bool customMenuIndicator = !customDropDown && (tool->features & QStyleOptionToolButton::HasMenu); + bool drawMenuIndicator = tool->features & QStyleOptionToolButton::HasMenu; + if (customDropDown) { + toolOpt.subControls &= ~QStyle::SC_ToolButtonMenu; + customDropDownArrow = hasStyleRule(w, PseudoElement_ToolButtonMenuArrow); + if (customDropDownArrow) + toolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu); + } + const bool customMenuIndicator = (!drawDropDown && drawMenuIndicator) + && hasStyleRule(w, PseudoElement_ToolButtonMenuIndicator); + if (customMenuIndicator) + toolOpt.features &= ~QStyleOptionToolButton::HasMenu; + if (rule.hasNativeBorder()) { if (tool->subControls & SC_ToolButton) { //in some case (eg. the button is "auto raised") the style doesn't draw the background @@ -3300,28 +3323,23 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC if (!(bflags & (State_Sunken | State_On | State_Raised))) rule.drawBackground(p, toolOpt.rect); } - customArrow = customArrow && hasStyleRule(w, customArrowElement()); - if (customArrow) - toolOpt.features &= ~QStyleOptionToolButton::Arrow; - customDropDown = customDropDown && hasStyleRule(w, PseudoElement_ToolButtonMenu); - if (customDropDown) { - toolOpt.subControls &= ~QStyle::SC_ToolButtonMenu; - customDropDownArrow = hasStyleRule(w, PseudoElement_ToolButtonMenuArrow); - if (customDropDownArrow) - toolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu); - } - customMenuIndicator = customMenuIndicator && hasStyleRule(w, PseudoElement_ToolButtonMenuIndicator); - if (customMenuIndicator) - toolOpt.features &= ~QStyleOptionToolButton::HasMenu; - - if (rule.baseStyleCanDraw() && !(tool->features & QStyleOptionToolButton::Arrow)) { - baseStyle()->drawComplexControl(cc, &toolOpt, p, w); - } else { - QWindowsStyle::drawComplexControl(cc, &toolOpt, p, w); - } - if (!customArrow && !customDropDown && !customMenuIndicator) - return; + QStyleOptionToolButton nativeToolOpt(toolOpt); + // don't draw natively if we have a custom rule for menu indicators and/or buttons + if (customMenuIndicator) + nativeToolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu); + if (customDropDown || customDropDownArrow) + nativeToolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu | QStyleOptionToolButton::MenuButtonPopup); + // Let base or windows style draw the button, which will include the menu-button + if (rule.baseStyleCanDraw() && !(tool->features & QStyleOptionToolButton::Arrow)) + baseStyle()->drawComplexControl(cc, &nativeToolOpt, p, w); + else + QWindowsStyle::drawComplexControl(cc, &nativeToolOpt, p, w); + // if we did draw natively, don't draw custom + if (nativeToolOpt.features & (QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu)) + drawMenuIndicator = false; + if (nativeToolOpt.features & QStyleOptionToolButton::MenuButtonPopup && !customDropDownArrow) + drawDropDown = false; } else { rule.drawRule(p, opt->rect); toolOpt.rect = rule.contentsRect(opt->rect); @@ -3331,7 +3349,8 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } const QRect cr = toolOpt.rect; - if (customDropDown) { + // Draw DropDownButton unless drawn before + if (drawDropDown) { if (opt->subControls & QStyle::SC_ToolButtonMenu) { QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolButtonMenu); QRect menuButtonRect = subControlRect(CC_ToolButton, opt, QStyle::SC_ToolButtonMenu, w); @@ -3342,7 +3361,7 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC baseStyle()->drawPrimitive(PE_IndicatorButtonDropDown, &toolOpt, p, w); } - if (customDropDownArrow) { + if (customDropDownArrow || drawMenuIndicator) { QRenderRule arrowRule = renderRule(w, opt, PseudoElement_ToolButtonMenuArrow); QRect arrowRect = arrowRule.hasGeometry() ? positionRect(w, arrowRule, PseudoElement_ToolButtonMenuArrow, menuButtonRect, toolOpt.direction) @@ -3355,11 +3374,12 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } } } - } else if (customMenuIndicator) { + } else if (drawMenuIndicator) { QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolButtonMenuIndicator); - QRect r = subRule.hasGeometry() - ? positionRect(w, subRule, PseudoElement_ToolButtonMenuIndicator, toolOpt.rect, toolOpt.direction) - : subRule.contentsRect(opt->rect); + + // content padding does not impact the indicator, so use the original rect to + // calculate position of the sub element within the toplevel rule + QRect r = positionRect(w, rule, subRule, PseudoElement_ToolButtonMenuIndicator, opt->rect, toolOpt.direction); if (subRule.hasDrawable()) { subRule.drawRule(p, r); } else { @@ -3369,29 +3389,37 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } toolOpt.rect = cr; + // If we don't have a custom arrow, then the arrow will have been rendered + // already by the base style when drawing the label. if (customArrow) { const auto arrowElement = customArrowElement(); QRenderRule subRule = renderRule(w, opt, arrowElement); - QRect r = subRule.hasGeometry() ? positionRect(w, subRule, arrowElement, toolOpt.rect, toolOpt.direction) - : subRule.contentsRect(toolOpt.rect); - if (subRule.hasDrawable()) { - subRule.drawRule(p, r); - } else { - toolOpt.rect = r; - const auto arrowElement = [&toolOpt] { - switch (toolOpt.arrowType) { - case Qt::DownArrow: return QStyle::PE_IndicatorArrowDown; - case Qt::UpArrow: return QStyle::PE_IndicatorArrowUp; - case Qt::LeftArrow: return QStyle::PE_IndicatorArrowLeft; - case Qt::RightArrow: return QStyle::PE_IndicatorArrowRight; - case Qt::NoArrow: break; - } - return QStyle::PE_IndicatorArrowDown; // never happens - }; - baseStyle()->drawPrimitive(arrowElement(), &toolOpt, p, w); + QRect arrowRect = subRule.hasGeometry() ? positionRect(w, subRule, arrowElement, toolOpt.rect, toolOpt.direction) + : subRule.contentsRect(toolOpt.rect); + + switch (toolOpt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: + break; + case Qt::ToolButtonTextOnly: + case Qt::ToolButtonTextBesideIcon: + case Qt::ToolButtonTextUnderIcon: { + // The base style needs to lay out the contents and will render the styled + // arrow icons, unless the geometry is defined in the style sheet. + toolOpt.text = tool->text; + if (!subRule.hasGeometry()) + toolOpt.features |= QStyleOptionToolButton::Arrow; + drawControl(CE_ToolButtonLabel, &toolOpt, p, w); + if (!subRule.hasGeometry()) + return; + break; } + case Qt::ToolButtonFollowStyle: + // QToolButton handles this, so must never happen + Q_ASSERT(false); + break; + } + subRule.drawRule(p, arrowRect); } - return; } break; @@ -3473,14 +3501,14 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC if (hasStyleRule(w, PseudoElement_MdiCloseButton) || hasStyleRule(w, PseudoElement_MdiNormalButton) || hasStyleRule(w, PseudoElement_MdiMinButton)) { - QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList(); + QList<QVariant> layout = rule.styleHint("button-layout"_L1).toList(); if (layout.isEmpty()) - layout = subControlLayout(QLatin1String("mNX")); + layout = subControlLayout("mNX"); QStyleOptionComplex optCopy(*opt); optCopy.subControls = { }; - for (int i = 0; i < layout.count(); i++) { - int layoutButton = layout[i].toInt(); + for (const QVariant &val : std::as_const(layout)) { + int layoutButton = val.toInt(); if (layoutButton < PseudoElement_MdiCloseButton || layoutButton > PseudoElement_MdiNormalButton) continue; @@ -3511,6 +3539,7 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC break; subRule.drawRule(p, opt->rect); QHash<QStyle::SubControl, QRect> layout = titleBarLayout(w, tb); + const auto paintDeviceDpr = p->device()->devicePixelRatio(); QRect ir; ir = layout[SC_TitleBarLabel]; @@ -3521,8 +3550,6 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC p->drawText(ir.x(), ir.y(), ir.width(), ir.height(), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); } - QPixmap pm; - ir = layout[SC_TitleBarSysMenu]; if (ir.isValid()) { QRenderRule subSubRule = renderRule(w, opt, PseudoElement_TitleBarSysMenu); @@ -3532,7 +3559,9 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC tb->icon.paint(p, ir); } else { int iconSize = pixelMetric(PM_SmallIconSize, tb, w); - pm = standardIcon(SP_TitleBarMenuButton, nullptr, w).pixmap(iconSize, iconSize); + const QSize sz(iconSize, iconSize); + const auto pm = standardIcon(SP_TitleBarMenuButton, nullptr, w) + .pixmap(sz, paintDeviceDpr); drawItemPixmap(p, ir, Qt::AlignCenter, pm); } } @@ -3542,15 +3571,14 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC QRenderRule subSubRule = renderRule(w, opt, PseudoElement_TitleBarCloseButton); subSubRule.drawRule(p, ir); - QSize sz = subSubRule.contentsRect(ir).size(); - if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) - pm = standardIcon(SP_DockWidgetCloseButton, nullptr, w).pixmap(sz); - else - pm = standardIcon(SP_TitleBarCloseButton, nullptr, w).pixmap(sz); + const QSize sz = subSubRule.contentsRect(ir).size(); + const auto type = ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) + ? SP_DockWidgetCloseButton : SP_TitleBarCloseButton; + const auto pm = standardIcon(type, nullptr, w).pixmap(sz, paintDeviceDpr); drawItemPixmap(p, ir, Qt::AlignCenter, pm); } - int pes[] = { + constexpr std::array<int, 6> pes = { PseudoElement_TitleBarMaxButton, PseudoElement_TitleBarMinButton, PseudoElement_TitleBarNormalButton, @@ -3559,15 +3587,15 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC PseudoElement_TitleBarContextHelpButton }; - for (unsigned int i = 0; i < sizeof(pes)/sizeof(int); i++) { - int pe = pes[i]; + for (int pe : pes) { QStyle::SubControl sc = knownPseudoElements[pe].subControl; ir = layout[sc]; if (!ir.isValid()) continue; QRenderRule subSubRule = renderRule(w, opt, pe); subSubRule.drawRule(p, ir); - pm = standardIcon(subControlIcon(pe), nullptr, w).pixmap(subSubRule.contentsRect(ir).size()); + const QSize sz = subSubRule.contentsRect(ir).size(); + const auto pm = standardIcon(subControlIcon(pe), nullptr, w).pixmap(sz, paintDeviceDpr); drawItemPixmap(p, ir, Qt::AlignCenter, pm); } @@ -3590,7 +3618,9 @@ void QStyleSheetStyle::renderMenuItemIcon(const QStyleOptionMenuItem *mi, QPaint ? (mi->state & QStyle::State_Selected ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; const bool checked = mi->checkType != QStyleOptionMenuItem::NotCheckable && mi->checked; - const QPixmap pixmap(mi->icon.pixmap(pixelMetric(PM_SmallIconSize), mode, + const auto iconSize = pixelMetric(PM_SmallIconSize, mi, w); + const QSize sz(iconSize, iconSize); + const QPixmap pixmap(mi->icon.pixmap(sz, p->device()->devicePixelRatio(), mode, checked ? QIcon::On : QIcon::Off)); const int pixw = pixmap.width() / pixmap.devicePixelRatio(); const int pixh = pixmap.height() / pixmap.devicePixelRatio(); @@ -3725,7 +3755,8 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q if (button->state & State_On) state = QIcon::On; - QPixmap pixmap = icon.pixmap(button->iconSize, mode, state); + const auto paintDeviceDpr = p->device()->devicePixelRatio(); + QPixmap pixmap = icon.pixmap(button->iconSize, paintDeviceDpr, mode, state); int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); int labelWidth = pixmapWidth; @@ -3924,7 +3955,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q int text_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; if (!styleHint(SH_UnderlineShortcut, &mi, w)) text_flags |= Qt::TextHideMnemonic; - int t = s.indexOf(QLatin1Char('\t')); + qsizetype t = s.indexOf(u'\t'); if (t >= 0) { QRect vShortcutRect = visualRect(opt->direction, mi.rect, QRect(textRect.topRight(), QPoint(mi.rect.right(), textRect.bottom()))); @@ -3955,7 +3986,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q QWindowsStyle::drawControl(ce, &mi, p, w); if (mi.checkType != QStyleOptionMenuItem::NotCheckable && !mi.checked) { // We have a style defined, but QWindowsStyle won't draw anything if not checked. - // So we mimick what QWindowsStyle would do. + // So we mimic what QWindowsStyle would do. int checkcol = qMax<int>(mi.maxIconWidth, QWindowsStylePrivate::windowsCheckMarkWidth); QRect vCheckRect = visualRect(opt->direction, mi.rect, QRect(mi.rect.x(), mi.rect.y(), checkcol, mi.rect.height())); if (mi.state.testFlag(State_Enabled) && mi.state.testFlag(State_Selected)) { @@ -4027,7 +4058,8 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q if (spacing == -1) spacing = 6; QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; - QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode); + const auto paintDeviceDpr = p->device()->devicePixelRatio(); + QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, paintDeviceDpr, mode); QRect iconRect(editRect); iconRect.setWidth(cb->iconSize.width()); iconRect = alignedRect(cb->direction, @@ -4088,15 +4120,22 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q case CE_HeaderLabel: if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - QStyleOptionHeader hdr(*header); + QStyleOptionHeaderV2 hdr; + QStyleOptionHeader &v1Copy = hdr; + if (auto v2 = qstyleoption_cast<const QStyleOptionHeaderV2 *>(opt)) + hdr = *v2; + else + v1Copy = *header; QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection); if (hasStyleRule(w, PseudoElement_HeaderViewUpArrow) || hasStyleRule(w, PseudoElement_HeaderViewDownArrow)) { - const QRect arrowRect = subElementRect(SE_HeaderArrow, opt, w); - if (hdr.orientation == Qt::Horizontal) - hdr.rect.setWidth(hdr.rect.width() - arrowRect.width()); - else - hdr.rect.setHeight(hdr.rect.height() - arrowRect.height()); + if (hdr.sortIndicator != QStyleOptionHeader::None) { + const QRect arrowRect = subElementRect(SE_HeaderArrow, opt, w); + if (hdr.orientation == Qt::Horizontal) + hdr.rect.setWidth(hdr.rect.width() - arrowRect.width()); + else + hdr.rect.setHeight(hdr.rect.height() - arrowRect.height()); + } } subRule.configurePalette(&hdr.palette, QPalette::ButtonText, QPalette::Button); if (subRule.hasFont) { @@ -4196,6 +4235,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q --chunkCount; }; } else if (chunkWidth > 0) { + const auto ceil = [](qreal x) { return int(x) + (x > 0 && x != int(x)); }; const int chunkCount = ceil(qreal(fillWidth)/chunkWidth); int x = reverse ? r.left() + r.width() - chunkWidth : r.x(); @@ -4236,12 +4276,11 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q if (rule.hasDrawable()) { rule.drawFrame(p, opt->rect); p->save(); - switch (sgOpt->corner) { - case Qt::BottomRightCorner: break; - case Qt::BottomLeftCorner: p->rotate(90); break; - case Qt::TopLeftCorner: p->rotate(180); break; - case Qt::TopRightCorner: p->rotate(270); break; - default: break; + static constexpr int rotation[] = { 180, 270, 90, 0 }; + if (rotation[sgOpt->corner]) { + p->translate(opt->rect.center()); + p->rotate(rotation[sgOpt->corner]); + p->translate(-opt->rect.center()); } rule.drawImage(p, opt->rect); p->restore(); @@ -4331,13 +4370,41 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q vopt->state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base); QWindowsStyle::drawControl(ce, &optCopy, p, w); } else { + p->save(); if (hasStyleRule(w, PseudoElement_Indicator)) { - subRule.configurePalette(&optCopy.palette, vopt->state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text, - vopt->state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base); - } else { - subRule.configurePalette(&optCopy.palette, QPalette::Text, QPalette::NoRole); + // there is a rule for the indicator, but no rule for the item itself (otherwise + // the previous path would have been taken); only draw the indicator using the + // rule (via QWindows/QCommonStyle), then let the base style handle the rest. + QStyleOptionViewItem optIndicator(*vopt); + subRule.configurePalette(&optIndicator.palette, + vopt->state & QStyle::State_Selected + ? QPalette::HighlightedText + : QPalette::Text, + vopt->state & QStyle::State_Selected + ? QPalette::Highlight + : QPalette::Base); + // only draw the indicator; no text, icon or background + optIndicator.backgroundBrush = Qt::NoBrush; // no background + optIndicator.text.clear(); + optIndicator.icon = QIcon(); + QWindowsStyle::drawControl(ce, &optIndicator, p, w); + + // Now draw text, background,icon, and highlight, but not the indicator with + // the base style. Since we can't turn off HasCheckIndicator to prevent the base + // style from drawing the check indicator again (it would change how the item + // gets laid out) we have to clip the indicator that's already been painted. + const QRect crStyle = subElementRect(QStyle::SE_ItemViewItemCheckIndicator, + &optIndicator, w); + const QRect crBase = baseStyle()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, + &optIndicator, w); + const QRegion clipRegion = QRegion(p->hasClipping() ? p->clipRegion() + : QRegion(optIndicator.rect)) + - crStyle.united(crBase); + p->setClipRegion(clipRegion); } + subRule.configurePalette(&optCopy.palette, QPalette::Text, QPalette::NoRole); baseStyle()->drawControl(ce, &optCopy, p, w); + p->restore(); } return; } @@ -4355,6 +4422,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q case CE_TabBarTabLabel: case CE_TabBarTabShape: if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + const auto foregroundRole = w ? w->foregroundRole() : QPalette::WindowText; QRenderRule subRule = renderRule(w, opt, PseudoElement_TabBarTab); QRect r = positionRect(w, subRule, PseudoElement_TabBarTab, opt->rect, opt->direction); if (ce == CE_TabBarTabShape && subRule.hasDrawable() && tab->shape < QTabBar::TriangularNorth) { @@ -4362,7 +4430,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q return; } QStyleOptionTab tabCopy(*tab); - subRule.configurePalette(&tabCopy.palette, QPalette::WindowText, QPalette::Base); + subRule.configurePalette(&tabCopy.palette, foregroundRole, QPalette::Base); QFont oldFont = p->font(); if (subRule.hasFont) p->setFont(subRule.font.resolve(p->font())); @@ -4420,7 +4488,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, r.width()); drawItemText(p, r, - alignment, dwOpt->palette, + alignment | Qt::TextHideMnemonic, dwOpt->palette, dwOpt->state & State_Enabled, titleText, QPalette::WindowText); @@ -4582,11 +4650,14 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op case PE_PanelLineEdit: if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { - if (QWidget *container = containerWidget(w); container != w) { - QRenderRule containerRule = renderRule(container, opt); - if (!containerRule.hasNativeBorder() || !containerRule.baseStyleCanDraw()) - return; - rule = containerRule; + // Fall back to container widget's render rule + if (w) { + if (QWidget *container = containerWidget(w); container != w) { + QRenderRule containerRule = renderRule(container, opt); + if (!containerRule.hasNativeBorder() || !containerRule.baseStyleCanDraw()) + return; + rule = containerRule; + } } if (rule.hasNativeBorder()) { @@ -4697,10 +4768,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { QRenderRule subRule = renderRule(w, opt, PseudoElement_TreeViewBranch); if (subRule.hasDrawable()) { - if ((vopt->state & QStyle::State_Selected) && vopt->showDecorationSelected) - p->fillRect(vopt->rect, vopt->palette.highlight()); - else if (vopt->features & QStyleOptionViewItem::Alternate) - p->fillRect(vopt->rect, vopt->palette.alternateBase()); + proxy()->drawPrimitive(PE_PanelItemViewRow, vopt, p, w); subRule.drawRule(p, opt->rect); } else { baseStyle()->drawPrimitive(pe, vopt, p, w); @@ -4768,6 +4836,17 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op pseudoElement = PseudoElement_DockWidgetSeparator; break; + case PE_PanelItemViewRow: + // For compatibility reasons, QTreeView draws different parts of + // the background of an item row separately, before calling the + // delegate to draw the item. The row background of an item is + // however not separately styleable through a style sheet, but + // only indirectly through the background of the item. To get the + // same background for all parts drawn by QTreeView, we have to + // use the background rule for the item here. + if (renderRule(w, opt, PseudoElement_ViewItem).hasBackground()) + pseudoElement = PseudoElement_ViewItem; + break; case PE_PanelItemViewItem: pseudoElement = PseudoElement_ViewItem; break; @@ -4794,6 +4873,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op w = w->parentWidget(); //match on the QTabBar instead of the CloseButton } pseudoElement = PseudoElement_TabBarTabCloseButton; + break; #endif default: @@ -5052,8 +5132,14 @@ int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const if (!rule.hasNativeBorder() || rule.hasBox()) return 0; break; + + case PM_ScrollView_ScrollBarOverlap: + if (!proxy()->styleHint(SH_ScrollBar_Transient, opt, w)) + return 0; + break; #endif // QT_CONFIG(scrollbar) + case PM_ProgressBarChunkWidth: subRule = renderRule(w, opt, PseudoElement_ProgressBarChunk); if (subRule.hasContentsSize()) { @@ -5126,9 +5212,8 @@ int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const case PM_MessageBoxIconSize: case PM_ButtonIconSize: case PM_SmallIconSize: - if (rule.hasStyleHint(QLatin1String("icon-size"))) { - return rule.styleHint(QLatin1String("icon-size")).toSize().width(); - } + if (rule.hasStyleHint("icon-size"_L1)) + return rule.styleHint("icon-size"_L1).toSize().width(); break; case PM_DockWidgetTitleMargin: { @@ -5321,7 +5406,7 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op } if ((pe == PseudoElement_Item) && (subRule.hasBox() || subRule.hasBorder() || subRule.hasFont)) { QSize sz(csz); - if (mi->text.contains(QLatin1Char('\t'))) + if (mi->text.contains(u'\t')) sz.rwidth() += 12; //as in QCommonStyle if (!mi->icon.isNull()) { const int pmSmall = pixelMetric(PM_SmallIconSize); @@ -5381,13 +5466,25 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op if (subRule.hasBox() || !subRule.hasNativeBorder()) sz = csz + QSize(vertical ? 0 : spaceForIcon, vertical ? spaceForIcon : 0); if (subRule.hasFont) { - QFont ruleFont = subRule.font.resolve(w->font()); - QFontMetrics fm(ruleFont); - const QSize textSize = fm.size(Qt::TextShowMnemonic, text) - + QSize(pixelMetric(PM_TabBarTabHSpace, opt, w), - pixelMetric(PM_TabBarTabVSpace, opt, w)); - sz = sz.expandedTo(vertical ? textSize.transposed() : textSize); + // first we remove the space needed for the text using the default font + const QSize oldTextSize = opt->fontMetrics.size(Qt::TextShowMnemonic, text); + (vertical ? sz.rheight() : sz.rwidth()) -= oldTextSize.width(); + + // then we add the space needed when using the rule font to the relevant + // dimension, and constraint the other dimension to the maximum to make + // sure we don't grow, but also don't clip icons or buttons. + const QFont ruleFont = subRule.font.resolve(w->font()); + const QFontMetrics fm(ruleFont); + const QSize textSize = fm.size(Qt::TextShowMnemonic, text); + if (vertical) { + sz.rheight() += textSize.width(); + sz.rwidth() = qMax(textSize.height(), sz.width()); + } else { + sz.rwidth() += textSize.width(); + sz.rheight() = qMax(textSize.height(), sz.height()); + } } + return subRule.boxSize(subRule.adjustSize(sz)); } sz = subRule.adjustSize(csz); @@ -5402,13 +5499,13 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op && !hasStyleRule(w, PseudoElement_MdiMinButton)) break; - QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList(); + QList<QVariant> layout = rule.styleHint("button-layout"_L1).toList(); if (layout.isEmpty()) - layout = subControlLayout(QLatin1String("mNX")); + layout = subControlLayout("mNX"); int width = 0, height = 0; - for (int i = 0; i < layout.count(); i++) { - int layoutButton = layout[i].toInt(); + for (const QVariant &val : std::as_const(layout)) { + int layoutButton = val.toInt(); if (layoutButton < PseudoElement_MdiCloseButton || layoutButton > PseudoElement_MdiNormalButton) continue; @@ -5446,65 +5543,65 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op /*! \internal */ -static QLatin1String propertyNameForStandardPixmap(QStyle::StandardPixmap sp) +static QLatin1StringView propertyNameForStandardPixmap(QStyle::StandardPixmap sp) { switch (sp) { - case QStyle::SP_TitleBarMenuButton: return QLatin1String("titlebar-menu-icon"); - case QStyle::SP_TitleBarMinButton: return QLatin1String("titlebar-minimize-icon"); - case QStyle::SP_TitleBarMaxButton: return QLatin1String("titlebar-maximize-icon"); - case QStyle::SP_TitleBarCloseButton: return QLatin1String("titlebar-close-icon"); - case QStyle::SP_TitleBarNormalButton: return QLatin1String("titlebar-normal-icon"); - case QStyle::SP_TitleBarShadeButton: return QLatin1String("titlebar-shade-icon"); - case QStyle::SP_TitleBarUnshadeButton: return QLatin1String("titlebar-unshade-icon"); - case QStyle::SP_TitleBarContextHelpButton: return QLatin1String("titlebar-contexthelp-icon"); - case QStyle::SP_DockWidgetCloseButton: return QLatin1String("dockwidget-close-icon"); - case QStyle::SP_MessageBoxInformation: return QLatin1String("messagebox-information-icon"); - case QStyle::SP_MessageBoxWarning: return QLatin1String("messagebox-warning-icon"); - case QStyle::SP_MessageBoxCritical: return QLatin1String("messagebox-critical-icon"); - case QStyle::SP_MessageBoxQuestion: return QLatin1String("messagebox-question-icon"); - case QStyle::SP_DesktopIcon: return QLatin1String("desktop-icon"); - case QStyle::SP_TrashIcon: return QLatin1String("trash-icon"); - case QStyle::SP_ComputerIcon: return QLatin1String("computer-icon"); - case QStyle::SP_DriveFDIcon: return QLatin1String("floppy-icon"); - case QStyle::SP_DriveHDIcon: return QLatin1String("harddisk-icon"); - case QStyle::SP_DriveCDIcon: return QLatin1String("cd-icon"); - case QStyle::SP_DriveDVDIcon: return QLatin1String("dvd-icon"); - case QStyle::SP_DriveNetIcon: return QLatin1String("network-icon"); - case QStyle::SP_DirOpenIcon: return QLatin1String("directory-open-icon"); - case QStyle::SP_DirClosedIcon: return QLatin1String("directory-closed-icon"); - case QStyle::SP_DirLinkIcon: return QLatin1String("directory-link-icon"); - case QStyle::SP_FileIcon: return QLatin1String("file-icon"); - case QStyle::SP_FileLinkIcon: return QLatin1String("file-link-icon"); - case QStyle::SP_FileDialogStart: return QLatin1String("filedialog-start-icon"); - case QStyle::SP_FileDialogEnd: return QLatin1String("filedialog-end-icon"); - case QStyle::SP_FileDialogToParent: return QLatin1String("filedialog-parent-directory-icon"); - case QStyle::SP_FileDialogNewFolder: return QLatin1String("filedialog-new-directory-icon"); - case QStyle::SP_FileDialogDetailedView: return QLatin1String("filedialog-detailedview-icon"); - case QStyle::SP_FileDialogInfoView: return QLatin1String("filedialog-infoview-icon"); - case QStyle::SP_FileDialogContentsView: return QLatin1String("filedialog-contentsview-icon"); - case QStyle::SP_FileDialogListView: return QLatin1String("filedialog-listview-icon"); - case QStyle::SP_FileDialogBack: return QLatin1String("filedialog-backward-icon"); - case QStyle::SP_DirIcon: return QLatin1String("directory-icon"); - case QStyle::SP_DialogOkButton: return QLatin1String("dialog-ok-icon"); - case QStyle::SP_DialogCancelButton: return QLatin1String("dialog-cancel-icon"); - case QStyle::SP_DialogHelpButton: return QLatin1String("dialog-help-icon"); - case QStyle::SP_DialogOpenButton: return QLatin1String("dialog-open-icon"); - case QStyle::SP_DialogSaveButton: return QLatin1String("dialog-save-icon"); - case QStyle::SP_DialogCloseButton: return QLatin1String("dialog-close-icon"); - case QStyle::SP_DialogApplyButton: return QLatin1String("dialog-apply-icon"); - case QStyle::SP_DialogResetButton: return QLatin1String("dialog-reset-icon"); - case QStyle::SP_DialogDiscardButton: return QLatin1String("dialog-discard-icon"); - case QStyle::SP_DialogYesButton: return QLatin1String("dialog-yes-icon"); - case QStyle::SP_DialogNoButton: return QLatin1String("dialog-no-icon"); - case QStyle::SP_ArrowUp: return QLatin1String("uparrow-icon"); - case QStyle::SP_ArrowDown: return QLatin1String("downarrow-icon"); - case QStyle::SP_ArrowLeft: return QLatin1String("leftarrow-icon"); - case QStyle::SP_ArrowRight: return QLatin1String("rightarrow-icon"); - case QStyle::SP_ArrowBack: return QLatin1String("backward-icon"); - case QStyle::SP_ArrowForward: return QLatin1String("forward-icon"); - case QStyle::SP_DirHomeIcon: return QLatin1String("home-icon"); - case QStyle::SP_LineEditClearButton: return QLatin1String("lineedit-clear-button-icon"); - default: return QLatin1String(""); + case QStyle::SP_TitleBarMenuButton: return "titlebar-menu-icon"_L1; + case QStyle::SP_TitleBarMinButton: return "titlebar-minimize-icon"_L1; + case QStyle::SP_TitleBarMaxButton: return "titlebar-maximize-icon"_L1; + case QStyle::SP_TitleBarCloseButton: return "titlebar-close-icon"_L1; + case QStyle::SP_TitleBarNormalButton: return "titlebar-normal-icon"_L1; + case QStyle::SP_TitleBarShadeButton: return "titlebar-shade-icon"_L1; + case QStyle::SP_TitleBarUnshadeButton: return "titlebar-unshade-icon"_L1; + case QStyle::SP_TitleBarContextHelpButton: return "titlebar-contexthelp-icon"_L1; + case QStyle::SP_DockWidgetCloseButton: return "dockwidget-close-icon"_L1; + case QStyle::SP_MessageBoxInformation: return "messagebox-information-icon"_L1; + case QStyle::SP_MessageBoxWarning: return "messagebox-warning-icon"_L1; + case QStyle::SP_MessageBoxCritical: return "messagebox-critical-icon"_L1; + case QStyle::SP_MessageBoxQuestion: return "messagebox-question-icon"_L1; + case QStyle::SP_DesktopIcon: return "desktop-icon"_L1; + case QStyle::SP_TrashIcon: return "trash-icon"_L1; + case QStyle::SP_ComputerIcon: return "computer-icon"_L1; + case QStyle::SP_DriveFDIcon: return "floppy-icon"_L1; + case QStyle::SP_DriveHDIcon: return "harddisk-icon"_L1; + case QStyle::SP_DriveCDIcon: return "cd-icon"_L1; + case QStyle::SP_DriveDVDIcon: return "dvd-icon"_L1; + case QStyle::SP_DriveNetIcon: return "network-icon"_L1; + case QStyle::SP_DirOpenIcon: return "directory-open-icon"_L1; + case QStyle::SP_DirClosedIcon: return "directory-closed-icon"_L1; + case QStyle::SP_DirLinkIcon: return "directory-link-icon"_L1; + case QStyle::SP_FileIcon: return "file-icon"_L1; + case QStyle::SP_FileLinkIcon: return "file-link-icon"_L1; + case QStyle::SP_FileDialogStart: return "filedialog-start-icon"_L1; + case QStyle::SP_FileDialogEnd: return "filedialog-end-icon"_L1; + case QStyle::SP_FileDialogToParent: return "filedialog-parent-directory-icon"_L1; + case QStyle::SP_FileDialogNewFolder: return "filedialog-new-directory-icon"_L1; + case QStyle::SP_FileDialogDetailedView: return "filedialog-detailedview-icon"_L1; + case QStyle::SP_FileDialogInfoView: return "filedialog-infoview-icon"_L1; + case QStyle::SP_FileDialogContentsView: return "filedialog-contentsview-icon"_L1; + case QStyle::SP_FileDialogListView: return "filedialog-listview-icon"_L1; + case QStyle::SP_FileDialogBack: return "filedialog-backward-icon"_L1; + case QStyle::SP_DirIcon: return "directory-icon"_L1; + case QStyle::SP_DialogOkButton: return "dialog-ok-icon"_L1; + case QStyle::SP_DialogCancelButton: return "dialog-cancel-icon"_L1; + case QStyle::SP_DialogHelpButton: return "dialog-help-icon"_L1; + case QStyle::SP_DialogOpenButton: return "dialog-open-icon"_L1; + case QStyle::SP_DialogSaveButton: return "dialog-save-icon"_L1; + case QStyle::SP_DialogCloseButton: return "dialog-close-icon"_L1; + case QStyle::SP_DialogApplyButton: return "dialog-apply-icon"_L1; + case QStyle::SP_DialogResetButton: return "dialog-reset-icon"_L1; + case QStyle::SP_DialogDiscardButton: return "dialog-discard-icon"_L1; + case QStyle::SP_DialogYesButton: return "dialog-yes-icon"_L1; + case QStyle::SP_DialogNoButton: return "dialog-no-icon"_L1; + case QStyle::SP_ArrowUp: return "uparrow-icon"_L1; + case QStyle::SP_ArrowDown: return "downarrow-icon"_L1; + case QStyle::SP_ArrowLeft: return "leftarrow-icon"_L1; + case QStyle::SP_ArrowRight: return "rightarrow-icon"_L1; + case QStyle::SP_ArrowBack: return "backward-icon"_L1; + case QStyle::SP_ArrowForward: return "forward-icon"_L1; + case QStyle::SP_DirHomeIcon: return "home-icon"_L1; + case QStyle::SP_LineEditClearButton: return "lineedit-clear-button-icon"_L1; + default: return ""_L1; } } @@ -5535,7 +5632,8 @@ QPixmap QStyleSheetStyle::standardPixmap(StandardPixmap standardPixmap, const QS QRenderRule rule = renderRule(w, opt); if (rule.hasStyleHint(s)) { QIcon icon = qvariant_cast<QIcon>(rule.styleHint(s)); - return icon.pixmap(16, 16); // ###: unhard-code this if someone complains + const auto dpr = w ? w->devicePixelRatio() : qApp->devicePixelRatio(); + return icon.pixmap(QSize(16, 16), dpr); } } return baseStyle()->standardPixmap(standardPixmap, opt, w); @@ -5560,25 +5658,25 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi QRenderRule rule = renderRule(w, opt); QString s; switch (sh) { - case SH_LineEdit_PasswordCharacter: s = QLatin1String("lineedit-password-character"); break; - case SH_LineEdit_PasswordMaskDelay: s = QLatin1String("lineedit-password-mask-delay"); break; - case SH_DitherDisabledText: s = QLatin1String("dither-disabled-text"); break; - case SH_EtchDisabledText: s = QLatin1String("etch-disabled-text"); break; - case SH_ItemView_ActivateItemOnSingleClick: s = QLatin1String("activate-on-singleclick"); break; - case SH_ItemView_ShowDecorationSelected: s = QLatin1String("show-decoration-selected"); break; - case SH_Table_GridLineColor: s = QLatin1String("gridline-color"); break; - case SH_DialogButtonLayout: s = QLatin1String("button-layout"); break; - case SH_ToolTipLabel_Opacity: s = QLatin1String("opacity"); break; - case SH_ComboBox_Popup: s = QLatin1String("combobox-popup"); break; - case SH_ComboBox_ListMouseTracking: s = QLatin1String("combobox-list-mousetracking"); break; - case SH_MenuBar_AltKeyNavigation: s = QLatin1String("menubar-altkey-navigation"); break; - case SH_Menu_Scrollable: s = QLatin1String("menu-scrollable"); break; - case SH_DrawMenuBarSeparator: s = QLatin1String("menubar-separator"); break; - case SH_MenuBar_MouseTracking: s = QLatin1String("mouse-tracking"); break; - case SH_SpinBox_ClickAutoRepeatRate: s = QLatin1String("spinbox-click-autorepeat-rate"); break; - case SH_SpinControls_DisableOnBounds: s = QLatin1String("spincontrol-disable-on-bounds"); break; - case SH_MessageBox_TextInteractionFlags: s = QLatin1String("messagebox-text-interaction-flags"); break; - case SH_ToolButton_PopupDelay: s = QLatin1String("toolbutton-popup-delay"); break; + case SH_LineEdit_PasswordCharacter: s = "lineedit-password-character"_L1; break; + case SH_LineEdit_PasswordMaskDelay: s = "lineedit-password-mask-delay"_L1; break; + case SH_DitherDisabledText: s = "dither-disabled-text"_L1; break; + case SH_EtchDisabledText: s = "etch-disabled-text"_L1; break; + case SH_ItemView_ActivateItemOnSingleClick: s = "activate-on-singleclick"_L1; break; + case SH_ItemView_ShowDecorationSelected: s = "show-decoration-selected"_L1; break; + case SH_Table_GridLineColor: s = "gridline-color"_L1; break; + case SH_DialogButtonLayout: s = "button-layout"_L1; break; + case SH_ToolTipLabel_Opacity: s = "opacity"_L1; break; + case SH_ComboBox_Popup: s = "combobox-popup"_L1; break; + case SH_ComboBox_ListMouseTracking: s = "combobox-list-mousetracking"_L1; break; + case SH_MenuBar_AltKeyNavigation: s = "menubar-altkey-navigation"_L1; break; + case SH_Menu_Scrollable: s = "menu-scrollable"_L1; break; + case SH_DrawMenuBarSeparator: s = "menubar-separator"_L1; break; + case SH_MenuBar_MouseTracking: s = "mouse-tracking"_L1; break; + case SH_SpinBox_ClickAutoRepeatRate: s = "spinbox-click-autorepeat-rate"_L1; break; + case SH_SpinControls_DisableOnBounds: s = "spincontrol-disable-on-bounds"_L1; break; + case SH_MessageBox_TextInteractionFlags: s = "messagebox-text-interaction-flags"_L1; break; + case SH_ToolButton_PopupDelay: s = "toolbutton-popup-delay"_L1; break; case SH_ToolBox_SelectedPageTitleBold: if (renderRule(w, opt, PseudoElement_ToolBoxTab).hasFont) return 0; @@ -5587,12 +5685,12 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi if (rule.hasPalette() && rule.palette()->foreground.style() != Qt::NoBrush) return rule.palette()->foreground.color().rgba(); break; - case SH_ScrollView_FrameOnlyAroundContents: s = QLatin1String("scrollview-frame-around-contents"); break; - case SH_ScrollBar_ContextMenu: s = QLatin1String("scrollbar-contextmenu"); break; - case SH_ScrollBar_LeftClickAbsolutePosition: s = QLatin1String("scrollbar-leftclick-absolute-position"); break; - case SH_ScrollBar_MiddleClickAbsolutePosition: s = QLatin1String("scrollbar-middleclick-absolute-position"); break; - case SH_ScrollBar_RollBetweenButtons: s = QLatin1String("scrollbar-roll-between-buttons"); break; - case SH_ScrollBar_ScrollWhenPointerLeavesControl: s = QLatin1String("scrollbar-scroll-when-pointer-leaves-control"); break; + case SH_ScrollView_FrameOnlyAroundContents: s = "scrollview-frame-around-contents"_L1; break; + case SH_ScrollBar_ContextMenu: s = "scrollbar-contextmenu"_L1; break; + case SH_ScrollBar_LeftClickAbsolutePosition: s = "scrollbar-leftclick-absolute-position"_L1; break; + case SH_ScrollBar_MiddleClickAbsolutePosition: s = "scrollbar-middleclick-absolute-position"_L1; break; + case SH_ScrollBar_RollBetweenButtons: s = "scrollbar-roll-between-buttons"_L1; break; + case SH_ScrollBar_ScrollWhenPointerLeavesControl: s = "scrollbar-scroll-when-pointer-leaves-control"_L1; break; case SH_TabBar_Alignment: #if QT_CONFIG(tabwidget) if (qobject_cast<const QTabWidget *>(w)) { @@ -5601,7 +5699,7 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi return rule.position()->position; } #endif // QT_CONFIG(tabwidget) - s = QLatin1String("alignment"); + s = "alignment"_L1; break; #if QT_CONFIG(tabbar) case SH_TabBar_CloseButtonPosition: @@ -5615,8 +5713,8 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi } break; #endif - case SH_TabBar_ElideMode: s = QLatin1String("tabbar-elide-mode"); break; - case SH_TabBar_PreferNoArrows: s = QLatin1String("tabbar-prefer-no-arrows"); break; + case SH_TabBar_ElideMode: s = "tabbar-elide-mode"_L1; break; + case SH_TabBar_PreferNoArrows: s = "tabbar-prefer-no-arrows"_L1; break; case SH_ComboBox_PopupFrameStyle: #if QT_CONFIG(combobox) if (qobject_cast<const QComboBox *>(w)) { @@ -5630,8 +5728,8 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi } #endif // QT_CONFIG(combobox) break; - case SH_DialogButtonBox_ButtonsHaveIcons: s = QLatin1String("dialogbuttonbox-buttons-have-icons"); break; - case SH_Workspace_FillSpaceOnMaximize: s = QLatin1String("mdi-fill-space-on-maximize"); break; + case SH_DialogButtonBox_ButtonsHaveIcons: s = "dialogbuttonbox-buttons-have-icons"_L1; break; + case SH_Workspace_FillSpaceOnMaximize: s = "mdi-fill-space-on-maximize"_L1; break; case SH_TitleBar_NoBorder: if (rule.hasBorder()) return !rule.border()->borders[LeftEdge]; @@ -5642,10 +5740,14 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi return 1; break; } - case SH_ItemView_ArrowKeysNavigateIntoChildren: s = QLatin1String("arrow-keys-navigate-into-children"); break; - case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: s = QLatin1String("paint-alternating-row-colors-for-empty-area"); break; - case SH_TitleBar_ShowToolTipsOnButtons: s = QLatin1String("titlebar-show-tooltips-on-buttons"); break; - case SH_Widget_Animation_Duration: s = QLatin1String("widget-animation-duration"); break; + case SH_ItemView_ArrowKeysNavigateIntoChildren: s = "arrow-keys-navigate-into-children"_L1; break; + case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: s = "paint-alternating-row-colors-for-empty-area"_L1; break; + case SH_TitleBar_ShowToolTipsOnButtons: s = "titlebar-show-tooltips-on-buttons"_L1; break; + case SH_Widget_Animation_Duration: s = "widget-animation-duration"_L1; break; + case SH_ScrollBar_Transient: + if (!rule.hasNativeBorder() || rule.hasBox() || rule.hasDrawable()) + return 0; + break; default: break; } if (!s.isEmpty() && rule.hasStyleHint(s)) { @@ -5964,14 +6066,14 @@ QRect QStyleSheetStyle::subControlRect(ComplexControl cc, const QStyleOptionComp if (hasStyleRule(w, PseudoElement_MdiCloseButton) || hasStyleRule(w, PseudoElement_MdiNormalButton) || hasStyleRule(w, PseudoElement_MdiMinButton)) { - QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList(); + QList<QVariant> layout = rule.styleHint("button-layout"_L1).toList(); if (layout.isEmpty()) - layout = subControlLayout(QLatin1String("mNX")); + layout = subControlLayout("mNX"); int x = 0, width = 0; QRenderRule subRule; - for (int i = 0; i < layout.count(); i++) { - int layoutButton = layout[i].toInt(); + for (const QVariant &val : std::as_const(layout)) { + int layoutButton = val.toInt(); if (layoutButton < PseudoElement_MdiCloseButton || layoutButton > PseudoElement_MdiNormalButton) continue; @@ -6127,8 +6229,22 @@ QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, c case SE_HeaderLabel: { QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection); - if (subRule.hasBox() || !subRule.hasNativeBorder()) - return subRule.contentsRect(opt->rect); + if (subRule.hasBox() || !subRule.hasNativeBorder()) { + auto r = subRule.contentsRect(opt->rect); + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + // Subtract width needed for arrow, if there is one + if (header->sortIndicator != QStyleOptionHeader::None) { + const auto arrowRect = subElementRect(SE_HeaderArrow, opt, w); + if (arrowRect.isValid()) { + if (opt->state & State_Horizontal) + r.setWidth(r.width() - arrowRect.width()); + else + r.setHeight(r.height() - arrowRect.height()); + } + } + } + return r; + } } break; @@ -6256,7 +6372,7 @@ QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, c #endif // QT_CONFIG(toolbar) // On mac we make pixel adjustments to layouts which are not - // desireable when you have custom style sheets on them + // desirable when you have custom style sheets on them case SE_CheckBoxLayoutItem: case SE_ComboBoxLayoutItem: case SE_DateTimeEditLayoutItem: @@ -6290,7 +6406,7 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const { // Qt's fontDialog relies on the font of the sample edit for its selection, // we should never override it. - if (w->objectName() == QLatin1String("qt_fontDialog_sampleEdit")) + if (w->objectName() == "qt_fontDialog_sampleEdit"_L1) return; QWidget *container = containerWidget(w); @@ -6388,7 +6504,7 @@ Qt::Alignment QStyleSheetStyle::resolveAlignment(Qt::LayoutDirection layDir, Qt: // (and hence has the correct object name). bool QStyleSheetStyle::isNaturalChild(const QObject *obj) { - if (obj->objectName().startsWith(QLatin1String("qt_"))) + if (obj->objectName().startsWith("qt_"_L1)) return true; return false; @@ -6396,6 +6512,9 @@ bool QStyleSheetStyle::isNaturalChild(const QObject *obj) QPixmap QStyleSheetStyle::loadPixmap(const QString &fileName, const QObject *context) { + if (fileName.isEmpty()) + return {}; + qreal ratio = -1.0; if (const QWidget *widget = qobject_cast<const QWidget *>(context)) { if (QScreen *screen = QApplication::screenAt(widget->mapToGlobal(QPoint(0, 0)))) |