diff options
author | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2020-06-05 09:28:00 +0200 |
---|---|---|
committer | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2020-06-08 09:06:43 +0000 |
commit | 62fa08d89eadfcd79cd4a51b997223eb6ec24d5e (patch) | |
tree | 5838ee3777cd5ac03db9a369afab175e8600e0d8 /src | |
parent | 2275a0896c7b7e648641f4c6977a02006a581577 (diff) |
Native style: make it possible to set nine patch margins from the style
Up till now, all images have been scaled using nine patch images
with margins set to be at the center. This was doomed to be too simplistic
at some point, at that point is now (when working on comboboxes).
So instead, calculate the nine patch margins from the style. This
will let the different styles set pixel perfect margins per control that
matches the image they draw. If left unspecified by a style, the default
implementation in QCommonStyle will return the old logic of using the center.
We also add support for specifying if an image can be scaled horizontally
or vertically by setting the right or bottom nine patch margin to -1
respectively. E.g on macOS, an NSButton cannot be scaled vertically, so the
image we draw will not look native if we do.
Change-Id: Icaf232748b8d15f06f9b289e164b5c8fb86a6c7b
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src')
14 files changed, 69 insertions, 13 deletions
diff --git a/src/imports/nativestyle/items/qquickstyleitem.cpp b/src/imports/nativestyle/items/qquickstyleitem.cpp index be1e9316..1034459d 100644 --- a/src/imports/nativestyle/items/qquickstyleitem.cpp +++ b/src/imports/nativestyle/items/qquickstyleitem.cpp @@ -114,27 +114,44 @@ QSGNode *QQuickStyleItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePa if (!node) node = window()->createNinePatchNode(); - QRectF bounds = boundingRect(); auto texture = window()->createTextureFromImage(m_paintedImage, QQuickWindow::TextureCanUseAtlas); - QSize padding = m_useNinePatchImage ? m_styleItemGeometry.minimumSize / 2 : QSize(0, 0); - if (boundingRect().width() < m_styleItemGeometry.minimumSize.width()) - padding.setWidth(0); - if (boundingRect().height() < m_styleItemGeometry.minimumSize.height()) - padding.setHeight(0); + QRectF bounds = boundingRect(); + const qreal scale = window()->devicePixelRatio(); + const QSizeF ninePatchImageSize = m_paintedImage.rect().size() / scale; #ifdef QT_DEBUG if (m_debugFlags.testFlag(ShowUnscaled)) { - const qreal scale = window()->devicePixelRatio(); - const QSizeF ninePatchImageSize = m_paintedImage.rect().size() / scale; bounds = QRectF(QPointF(), ninePatchImageSize); qqc2Debug() << "Setting paint node bounds to size of image:" << bounds; } #endif + QMargins padding = m_useNinePatchImage ? m_styleItemGeometry.ninePatchMargins : QMargins(0, 0, 0, 0); + if (padding.right() == -1) { + // Special case: a right padding of -1 means that + // the image should not scale horizontally. + bounds.setWidth(ninePatchImageSize.width()); + padding.setLeft(0); + padding.setRight(0); + } else if (boundingRect().width() < m_styleItemGeometry.minimumSize.width()) { + // If the item size is smaller that the image, using nine-patch scaling + // ends up wrapping it. In that case we scale the whole image instead. + padding.setLeft(0); + padding.setRight(0); + } + if (padding.bottom() == -1) { + bounds.setHeight(ninePatchImageSize.height()); + padding.setTop(0); + padding.setBottom(0); + } else if (boundingRect().height() < m_styleItemGeometry.minimumSize.height()) { + padding.setTop(0); + padding.setBottom(0); + } + node->setBounds(bounds); node->setTexture(texture); node->setDevicePixelRatio(window()->devicePixelRatio()); - node->setPadding(padding.width(), padding.height(), padding.width(), padding.height()); + node->setPadding(padding.left(), padding.top(), padding.right(), padding.bottom()); node->update(); return node; @@ -259,9 +276,17 @@ void QQuickStyleItem::paintControlToImage() painter.drawLine(p.x() - offset, p.y() - offset, p.x() - offset, p.y() + m_contentSize.height()); } if (m_debugFlags.testFlag(ShowUnscaled)) { - const QPoint center = m_paintedImage.rect().center() / scale; - painter.drawLine(center.x(), 0, center.x(), m_paintedImage.rect().height()); - painter.drawLine(0, center.y(), m_paintedImage.rect().width(), center.y()); + const QMargins m = m_styleItemGeometry.ninePatchMargins; + const int w = int(m_paintedImage.rect().width() / scale); + const int h = int(m_paintedImage.rect().height() / scale); + if (m.right() != -1) { + painter.drawLine(m.left(), 0, m.left(), h); + painter.drawLine(w - m.right(), 0, w - m.right(), h); + } + if (m.bottom() != -1) { + painter.drawLine(0, m.top(), w, m.top()); + painter.drawLine(0, h - m.bottom(), w, h - m.bottom()); + } } } #endif diff --git a/src/imports/nativestyle/items/qquickstyleitem.h b/src/imports/nativestyle/items/qquickstyleitem.h index bab038ce..47247e15 100644 --- a/src/imports/nativestyle/items/qquickstyleitem.h +++ b/src/imports/nativestyle/items/qquickstyleitem.h @@ -144,6 +144,7 @@ struct StyleItemGeometry QSize implicitSize; QRect contentRect; QRect layoutRect; + QMargins ninePatchMargins; }; QDebug operator<<(QDebug debug, const StyleItemGeometry &cg); diff --git a/src/imports/nativestyle/items/qquickstyleitembutton.cpp b/src/imports/nativestyle/items/qquickstyleitembutton.cpp index 4d5d32e0..6f3fbfec 100644 --- a/src/imports/nativestyle/items/qquickstyleitembutton.cpp +++ b/src/imports/nativestyle/items/qquickstyleitembutton.cpp @@ -59,6 +59,7 @@ StyleItemGeometry QQuickStyleItemButton::calculateGeometry() styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_PushButtonContents, &styleOption); geometry.layoutRect = style()->subElementRect(QStyle::SE_PushButtonLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_PushButtonBevel, &styleOption, geometry.minimumSize); return geometry; } diff --git a/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp b/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp index e97f7d94..e5ee9133 100644 --- a/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp @@ -60,6 +60,7 @@ StyleItemGeometry QQuickStyleItemCheckBox::calculateGeometry() styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_CheckBoxContents, &styleOption); geometry.layoutRect = style()->subElementRect(QStyle::SE_CheckBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_CheckBox, &styleOption, geometry.minimumSize); // Spacing seems to already be baked into SE_CheckBoxContents, so ignore until needed //const int space = style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &styleOption); diff --git a/src/imports/nativestyle/items/qquickstyleitemframe.cpp b/src/imports/nativestyle/items/qquickstyleitemframe.cpp index fe5d1370..42a6c231 100644 --- a/src/imports/nativestyle/items/qquickstyleitemframe.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemframe.cpp @@ -40,12 +40,14 @@ StyleItemGeometry QQuickStyleItemFrame::calculateGeometry() { QStyleOptionFrame styleOption; initStyleOption(styleOption); - StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_Frame, &styleOption, QSize(0, 0)); geometry.implicitSize = contentSize(); styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_FrameContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); + return geometry; } diff --git a/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp b/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp index fab8fb3a..2b4154d2 100644 --- a/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp @@ -60,6 +60,7 @@ StyleItemGeometry QQuickStyleItemGroupBox::calculateGeometry() styleOption.rect.setSize(geometry.implicitSize); geometry.contentRect = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxContents); geometry.layoutRect = style()->subElementRect(QStyle::SE_GroupBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_GroupBox, &styleOption, geometry.minimumSize); const QQuickStyleMargins oldGroupBoxPadding = m_groupBoxPadding; const QRect frame = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxFrame); diff --git a/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp b/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp index 5561184c..c8bb251f 100644 --- a/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp @@ -60,6 +60,7 @@ StyleItemGeometry QQuickStyleItemRadioButton::calculateGeometry() styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_RadioButtonContents, &styleOption); geometry.layoutRect = style()->subElementRect(QStyle::SE_RadioButtonLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_RadioButton, &styleOption, geometry.minimumSize); return geometry; } diff --git a/src/imports/nativestyle/items/qquickstyleitemslider.cpp b/src/imports/nativestyle/items/qquickstyleitemslider.cpp index 99b0e312..5ca282cc 100644 --- a/src/imports/nativestyle/items/qquickstyleitemslider.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemslider.cpp @@ -63,6 +63,7 @@ StyleItemGeometry QQuickStyleItemSlider::calculateGeometry() geometry.minimumSize = style()->sizeFromContents(QStyle::CT_Slider, &styleOption, QSize(0, 0)); geometry.implicitSize = geometry.minimumSize; geometry.layoutRect = style()->subElementRect(QStyle::SE_SliderLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_Slider, &styleOption, geometry.minimumSize); return geometry; } diff --git a/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp b/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp index 9ebc62b1..a2c4f1d1 100644 --- a/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp @@ -62,6 +62,7 @@ StyleItemGeometry QQuickStyleItemSpinBox::calculateGeometry() styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subControlRect(QStyle::CC_SpinBox, &styleOption, QStyle::SC_SpinBoxEditField); geometry.layoutRect = style()->subElementRect(QStyle::SE_SpinBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_SpinBox, &styleOption, geometry.minimumSize); } else { geometry.implicitSize = geometry.minimumSize; } diff --git a/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp b/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp index 88dbf065..c149444b 100644 --- a/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp @@ -60,6 +60,7 @@ StyleItemGeometry QQuickStyleItemTextArea::calculateGeometry() geometry.implicitSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, contentSize()); styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_LineEditContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); return geometry; } diff --git a/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp b/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp index ed8aaf46..1a262033 100644 --- a/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp +++ b/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp @@ -59,6 +59,7 @@ StyleItemGeometry QQuickStyleItemTextField::calculateGeometry() geometry.implicitSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, contentSize()); styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); geometry.contentRect = style()->subElementRect(QStyle::SE_LineEditContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); return geometry; } diff --git a/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp b/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp index 0cb8e848..e8dadb9e 100644 --- a/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp +++ b/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp @@ -4694,6 +4694,22 @@ QFont QCommonStyle::font(QStyle::ControlElement element, const QStyle::State sta return QGuiApplication::font(); } +QMargins QCommonStyle::ninePatchMargins(QStyle::ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const +{ + // By default, we just divide the image at the center + int w = imageSize.width() / 2; + int h = imageSize.height() / 2; + return QMargins(w, h, w, h); +} + +QMargins QCommonStyle::ninePatchMargins(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const +{ + // By default, we just divide the image at the center + int w = imageSize.width() / 2; + int h = imageSize.height() / 2; + return QMargins(w, h, w, h); +} + int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintReturn *hret) const { int ret = 0; diff --git a/src/imports/nativestyle/qstyle/qquickcommonstyle.h b/src/imports/nativestyle/qstyle/qquickcommonstyle.h index 77626843..894194a0 100644 --- a/src/imports/nativestyle/qstyle/qquickcommonstyle.h +++ b/src/imports/nativestyle/qstyle/qquickcommonstyle.h @@ -62,6 +62,8 @@ public: QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const override; QFont font(ControlElement element, const QStyle::State state) const override; + QMargins ninePatchMargins(ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const override; + QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const override; int pixelMetric(PixelMetric m, const QStyleOption *opt = nullptr) const override; int styleHint(StyleHint sh, const QStyleOption *opt = nullptr, QStyleHintReturn *shret = nullptr) const override; diff --git a/src/imports/nativestyle/qstyle/qquickstyle.h b/src/imports/nativestyle/qstyle/qquickstyle.h index 91e09f4b..bc0cccf7 100644 --- a/src/imports/nativestyle/qstyle/qquickstyle.h +++ b/src/imports/nativestyle/qstyle/qquickstyle.h @@ -782,6 +782,8 @@ public: virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const = 0; virtual QFont font(ControlElement element, const QStyle::State state) const = 0; + virtual QMargins ninePatchMargins(ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const = 0; + virtual QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const = 0; virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const = 0; |