summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2019-10-10 14:33:53 +0200
committerRichard Moe Gustavsen <richard.gustavsen@qt.io>2019-11-14 20:28:35 +0100
commit01ec11507d7ef3de09bad9d1ef8e6d4a3d6c4428 (patch)
tree10371581dfcbc8e47955a07fa14376e3c4a351ad /src
parenta4751f8824723acaee4b9d8aa78a59c2aa36cb3e (diff)
QStyleSheetStyle: add new property to QPushButton: icon
There is currently no proper way to change the icon of a pushbutton from css. But there is a need for doing so (QTBUG-2982), and the typical work-around is to instead use the css property 'qproperty-icon'. But setting qproperties from the style is not a good idea in the first place, since it modifies the state of the widget it draws. Moreover, such properties are only set once (in QStyle::polish()), and will not have any effect on pseudo states, like hover. To close this gap, this patch will add a css property 'icon' that can be set on a QPushButton. This property will follow normal css cascading, and respect pseudo states, equal to any other css property. [ChangeLog][QtWidgets][QStyle] You can now set the CSS property 'icon' on a QPushButton to override which icon to draw. Fixes: QTBUG-79137 Change-Id: Ie7e0b0fa4f19471f51108cd4ca931356219d562e Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de> Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/text/qcssparser.cpp32
-rw-r--r--src/gui/text/qcssparser_p.h2
-rw-r--r--src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc17
-rw-r--r--src/widgets/styles/qstylesheetstyle.cpp51
4 files changed, 86 insertions, 16 deletions
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index ce7c7610c1..793ef5774b 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -123,6 +123,7 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "font-variant", FontVariant },
{ "font-weight", FontWeight },
{ "height", Height },
+ { "icon", QtIcon },
{ "image", QtImage },
{ "image-position", QtImageAlignment },
{ "left", Left },
@@ -1379,6 +1380,37 @@ bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
return hit;
}
+bool ValueExtractor::extractIcon(QIcon *icon, QSize *size)
+{
+ // Find last declaration that specifies an icon
+ const auto declaration = std::find_if(
+ declarations.rbegin(), declarations.rend(),
+ [](const Declaration &decl) { return decl.d->propertyId == QtIcon; });
+ if (declaration == declarations.rend())
+ return false;
+
+ *icon = declaration->iconValue();
+
+ // If the value contains a URI, try to get the size of the icon
+ if (declaration->d->values.isEmpty())
+ return true;
+
+ const auto &propertyValue = declaration->d->values.constFirst();
+ if (propertyValue.type != Value::Uri)
+ return true;
+
+ // First try to read just the size from the image without loading it
+ const QString url(propertyValue.variant.toString());
+ QImageReader imageReader(url);
+ *size = imageReader.size();
+ if (!size->isNull())
+ return true;
+
+ // Get the size by loading the image instead
+ *size = imageReader.read().size();
+ return true;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Declaration
QColor Declaration::colorValue(const QPalette &pal) const
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index ab85e76cf3..b8bf259dda 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -198,6 +198,7 @@ enum Property {
QtLineHeightType,
FontKerning,
QtForegroundTextureCacheKey,
+ QtIcon,
NumProperties
};
@@ -855,6 +856,7 @@ struct Q_GUI_EXPORT ValueExtractor
bool extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg);
int extractStyleFeatures();
bool extractImage(QIcon *icon, Qt::Alignment *a, QSize *size);
+ bool extractIcon(QIcon *icon, QSize *size);
int lengthValue(const Declaration &decl);
diff --git a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc
index c8f374a1b1..35bad6786a 100644
--- a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc
+++ b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc
@@ -993,6 +993,9 @@
\li Supports the \l{box model}. Supports the \l{#default-ps}{:default},
\l{#flat-ps}{:flat}, \l{#checked-ps}{:checked} pseudo states.
+ Since 5.15, the \l{#icon-prop}{icon} property can be set to
+ override the button icon.
+
For QPushButton with a menu, the menu indicator is styled
using the \l{#menu-indicator-sub}{::menu-indicator}
subcontrol. Appearance of checkable push buttons can be
@@ -1946,6 +1949,20 @@
See also \l{#width-prop}{width}.
\row
+ \li \b{\c icon} \target icon-prop
+ \li \l{#Url}{Url}+
+ \li The icon that is used, for widgets that have an icon.
+
+ The only widget currently supporting this property is QPushButton.
+
+ \note It's the application's responsibilty to assign an icon to a
+ button (using the QAbstractButton API), and not the style's. So be
+ careful setting it unless your stylesheet is targeting a specific
+ application.
+
+ Available since 5.15.
+
+ \row
\li \b{\c icon-size} \target icon-size-prop
\li \l{#Length}{Length}
\li The width and height of the icon in a widget.
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp
index 3f57992311..0568d749c8 100644
--- a/src/widgets/styles/qstylesheetstyle.cpp
+++ b/src/widgets/styles/qstylesheetstyle.cpp
@@ -534,6 +534,7 @@ public:
const QStyleSheetOutlineData *outline() const { return ou; }
const QStyleSheetGeometryData *geometry() const { return geo; }
const QStyleSheetPositionData *position() const { return p; }
+ const QStyleSheetImageData *icon() const { return iconPtr; }
bool hasModification() const;
@@ -569,6 +570,7 @@ public:
bool hasGeometry() const { return geo != 0; }
bool hasDrawable() const { return !hasNativeBorder() || hasBackground() || hasImage(); }
bool hasImage() const { return img != 0; }
+ bool hasIcon() const { return iconPtr != 0; }
QSize minimumContentsSize() const
{ return geo ? QSize(geo->minWidth, geo->minHeight) : QSize(0, 0); }
@@ -628,6 +630,7 @@ public:
QSharedDataPointer<QStyleSheetGeometryData> geo;
QSharedDataPointer<QStyleSheetPositionData> p;
QSharedDataPointer<QStyleSheetImageData> img;
+ QSharedDataPointer<QStyleSheetImageData> iconPtr;
int clipset;
QPainterPath clipPath;
@@ -969,11 +972,16 @@ QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QObject
if (v.extractPalette(&fg, &sfg, &sbg, &abg))
pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg);
- QIcon icon;
+ QIcon imgIcon;
alignment = Qt::AlignCenter;
+ QSize imgSize;
+ if (v.extractImage(&imgIcon, &alignment, &imgSize))
+ img = new QStyleSheetImageData(imgIcon, alignment, imgSize);
+
+ QIcon icon;
QSize size;
- if (v.extractImage(&icon, &alignment, &size))
- img = new QStyleSheetImageData(icon, alignment, size);
+ if (v.extractIcon(&icon, &size))
+ iconPtr = new QStyleSheetImageData(icon, Qt::AlignCenter, size);
int adj = -255;
hasFont = v.extractFont(&font, &adj);
@@ -3521,15 +3529,25 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
if (rule.hasFont)
p->setFont(rule.font.resolve(p->font()));
- if (rule.hasPosition() && rule.position()->textAlignment != 0) {
- Qt::Alignment textAlignment = rule.position()->textAlignment;
- QRect textRect = button->rect;
+ if (rule.hasPosition() || rule.hasIcon()) {
uint tf = Qt::TextShowMnemonic;
+ QRect textRect = button->rect;
+
+ const uint horizontalAlignMask = Qt::AlignHCenter | Qt::AlignLeft | Qt::AlignRight;
const uint verticalAlignMask = Qt::AlignVCenter | Qt::AlignTop | Qt::AlignLeft;
- tf |= (textAlignment & verticalAlignMask) ? (textAlignment & verticalAlignMask) : Qt::AlignVCenter;
- if (!styleHint(SH_UnderlineShortcut, button, w))
- tf |= Qt::TextHideMnemonic;
- if (!button->icon.isNull()) {
+
+ if (rule.hasPosition() && rule.position()->textAlignment != 0) {
+ Qt::Alignment textAlignment = rule.position()->textAlignment;
+ tf |= (textAlignment & verticalAlignMask) ? (textAlignment & verticalAlignMask) : Qt::AlignVCenter;
+ tf |= (textAlignment & horizontalAlignMask) ? (textAlignment & horizontalAlignMask) : Qt::AlignHCenter;
+ if (!styleHint(SH_UnderlineShortcut, button, w))
+ tf |= Qt::TextHideMnemonic;
+ } else {
+ tf |= Qt::AlignVCenter | Qt::AlignHCenter;
+ }
+
+ QIcon icon = rule.hasIcon() ? rule.icon()->icon : button->icon;
+ if (!icon.isNull()) {
//Group both icon and text
QRect iconRect;
QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
@@ -3539,7 +3557,7 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
if (button->state & State_On)
state = QIcon::On;
- QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ QPixmap pixmap = icon.pixmap(button->iconSize, mode, state);
int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
int labelWidth = pixmapWidth;
@@ -3550,10 +3568,10 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
labelWidth += (textWidth + iconSpacing);
//Determine label alignment:
- if (textAlignment & Qt::AlignLeft) { /*left*/
+ if (tf & Qt::AlignLeft) { /*left*/
iconRect = QRect(textRect.x(), textRect.y() + (textRect.height() - labelHeight) / 2,
pixmapWidth, pixmapHeight);
- } else if (textAlignment & Qt::AlignHCenter) { /* center */
+ } else if (tf & Qt::AlignHCenter) { /* center */
iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
textRect.y() + (textRect.height() - labelHeight) / 2,
pixmapWidth, pixmapHeight);
@@ -3565,7 +3583,9 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
iconRect = visualRect(button->direction, textRect, iconRect);
- tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead
+ // Left align, adjust the text-rect according to the icon instead
+ tf &= ~horizontalAlignMask;
+ tf |= Qt::AlignLeft;
if (button->direction == Qt::RightToLeft)
textRect.setRight(iconRect.left() - iconSpacing);
@@ -3576,9 +3596,8 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
iconRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, w),
pixelMetric(PM_ButtonShiftVertical, opt, w));
p->drawPixmap(iconRect, pixmap);
- } else {
- tf |= textAlignment;
}
+
if (button->state & (State_On | State_Sunken))
textRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, w),
pixelMetric(PM_ButtonShiftVertical, opt, w));