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>
This commit is contained in:
parent
a4751f8824
commit
01ec11507d
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
@ -1945,6 +1948,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}
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user