7#include <private/qqc2qstylehelper_p.h>
8#include <private/qquickbutton_p.h>
9#include <private/qquickcontrol_p.h>
10#include <private/qquickstyleitembutton_p.h>
11#include <private/qquickwindow_p.h>
13#include <QtQuick/qquickrendercontrol.h>
14#include <QtQuick/qquickwindow.h>
15#include <QtQuick/qsgninepatchnode.h>
17#include <QtQml/qqml.h>
19#include <QtCore/qdir.h>
20#include <QtCore/qscopedvaluerollback.h>
24QDebug operator<<(QDebug debug,
const QQuickStyleMargins &padding)
26 QDebugStateSaver saver(debug);
28 debug <<
"StyleMargins(";
29 debug << padding.left() <<
", ";
30 debug << padding.top() <<
", ";
31 debug << padding.right() <<
", ";
32 debug << padding.bottom();
39 QDebugStateSaver saver(debug);
41 debug <<
"StyleItemGeometry(";
42 debug <<
"implicitSize:" << cg.implicitSize <<
", ";
43 debug <<
"contentRect:" << cg.contentRect <<
", ";
44 debug <<
"layoutRect:" << cg.layoutRect <<
", ";
45 debug <<
"minimumSize:" << cg.minimumSize <<
", ";
46 debug <<
"9patchMargins:" << cg.ninePatchMargins;
55 static int multiplier = [&]() {
56 const qreal dpr = window()->effectiveDevicePixelRatio();
57 for (
int m = 1; m <= 10; ++m) {
58 const qreal v = m * dpr;
63 qWarning() <<
"The current dpr (" << dpr <<
") is not supported"
64 <<
"by the style and might result in drawing artifacts";
68 return int(qCeil(qreal(size) / qreal(multiplier)) * multiplier);
74 setFlag(QQuickItem::ItemHasContents);
86 if (QQuickWindow *win = window()) {
88 m_connectedWindow = win;
94 m_dirty.setFlag(DirtyFlag::Image);
95 if (isComponentComplete())
101 m_dirty.setFlag(DirtyFlag::Geometry);
102 if (isComponentComplete())
108 QSGNinePatchNode *node =
static_cast<QSGNinePatchNode *>(oldNode);
110 node = window()->createNinePatchNode();
112 if (m_paintedImage.isNull()) {
119 const auto texture = window()->createTextureFromImage(m_paintedImage, QQuickWindow::TextureCanUseAtlas);
121 QRectF bounds = boundingRect();
122 const qreal dpr = window()->effectiveDevicePixelRatio();
123 const QSizeF unscaledImageSize = QSizeF(m_paintedImage.size()) / dpr;
129 if (bounds.width() < unscaledImageSize.width())
130 bounds.setWidth(unscaledImageSize.width());
131 if (bounds.height() < unscaledImageSize.height())
132 bounds.setHeight(unscaledImageSize.height());
135 if (m_debugFlags.testFlag(Unscaled)) {
136 bounds.setSize(unscaledImageSize);
137 qqc2Info() <<
"Setting qsg node size to the unscaled size of m_paintedImage:" << bounds;
141 if (m_useNinePatchImage) {
142 QMargins padding = m_styleItemGeometry.ninePatchMargins;
143 if (padding.right() == -1) {
149 if (padding.bottom() == -1) {
151 padding.setBottom(0);
153 node->setPadding(padding.left(), padding.top(), padding.right(), padding.bottom());
156 node->setBounds(bounds);
157 node->setTexture(texture);
158 node->setDevicePixelRatio(dpr);
167 if (item->metaObject()->indexOfProperty(
"qqc2_style_small") != -1)
168 return QStyle::State_Small;
169 if (item->metaObject()->indexOfProperty(
"qqc2_style_mini") != -1)
170 return QStyle::State_Mini;
171 return QStyle::State_None;
176 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(window);
177 return renderWindow ? renderWindow : window;
184 styleOption.control =
const_cast<QQuickItem *>(control<QQuickItem>());
185 styleOption.window = effectiveWindow(window());
186 styleOption.palette = QQuickItemPrivate::get(m_control)->palette()->toQPalette();
187 styleOption.rect = QRect(QPoint(0, 0), imageSize());
189 styleOption.state = QStyle::State_None;
190 styleOption.state |= controlSize(styleOption.control);
193 if (
const auto quickControl =
dynamic_cast<QQuickControl *>(m_control.data()))
194 styleOption.direction = quickControl->isMirrored() ? Qt::RightToLeft : Qt::LeftToRight;
196 if (styleOption.window) {
197 if (styleOption.window->isActive())
198 styleOption.state |= QStyle::State_Active;
199 if (m_control->isEnabled())
200 styleOption.state |= QStyle::State_Enabled;
201 if (m_control->hasActiveFocus())
202 styleOption.state |= QStyle::State_HasFocus;
203 if (m_control->isUnderMouse())
204 styleOption.state |= QStyle::State_MouseOver;
206 styleOption.state |= QStyle::State_KeyboardFocusChange;
209 if (m_overrideState != None) {
212 if (m_overrideState & AlwaysHovered)
213 styleOption.state |= QStyle::State_MouseOver;
214 else if (m_overrideState & NeverHovered)
215 styleOption.state &= ~QStyle::State_MouseOver;
223 QQuickItem::geometryChange(newGeometry, oldGeometry);
234 QQuickItem::itemChange(change, data);
237 case QQuickItem::ItemVisibleHasChanged:
241 case QQuickItem::ItemSceneChange: {
243 QQuickWindow *win = data.window;
244 if (m_connectedWindow)
248 m_connectedWindow = win;
257 if (event->type() == QEvent::ApplicationPaletteChange) {
259 if (
auto *style = QQuickStyleItem::style())
263 return QQuickItem::event(event);
269 m_dirty.setFlag(DirtyFlag::Geometry,
false);
273 const QSize oldMinimumSize = minimumSize();
275 m_styleItemGeometry = calculateGeometry();
278 if (m_styleItemGeometry.minimumSize.isEmpty())
279 qmlWarning(
this) <<
"(StyleItem) minimumSize is empty!";
282 if (m_styleItemGeometry.implicitSize.isEmpty()) {
285 m_styleItemGeometry.implicitSize = m_styleItemGeometry.minimumSize;
286 qqc2Info() <<
"implicitSize is empty, using minimumSize instead";
290 if (m_styleItemGeometry.implicitSize.width() < m_styleItemGeometry.minimumSize.width())
291 qmlWarning(
this) <<
"(StyleItem) implicit width is smaller than minimum width!";
292 if (m_styleItemGeometry.implicitSize.height() < m_styleItemGeometry.minimumSize.height())
293 qmlWarning(
this) <<
"(StyleItem) implicit height is smaller than minimum height!";
297 emit contentPaddingChanged();
299 emit layoutMarginsChanged();
300 if (minimumSize() != oldMinimumSize)
301 emit minimumSizeChanged();
303 setImplicitSize(m_styleItemGeometry.implicitSize.width(), m_styleItemGeometry.implicitSize.height());
306 <<
"bounding rect:" << boundingRect()
307 <<
"layout margins:" << layoutMargins()
308 <<
"content padding:" << contentPadding()
309 <<
"input content size:" << m_contentSize;
315 const QSize imgSize = imageSize();
316 if (imgSize.isEmpty())
319 m_dirty.setFlag(DirtyFlag::Image,
false);
332 const qreal dpr = window()->effectiveDevicePixelRatio();
333 const int alignedW =
int(dprAlignedSize(imgSize.width()) * dpr);
334 const int alignedH =
int(dprAlignedSize(imgSize.height()) * dpr);
335 const QSize alignedSize = QSize(alignedW, alignedH);
337 if (m_paintedImage.size() != alignedSize) {
338 m_paintedImage = QImage(alignedSize, QImage::Format_ARGB32_Premultiplied);
339 m_paintedImage.setDevicePixelRatio(dpr);
340 qqc2Info() <<
"created image with dpr aligned size:" << alignedSize;
343 m_paintedImage.fill(Qt::transparent);
345 QPainter painter(&m_paintedImage);
346 paintEvent(&painter);
349 if (m_debugFlags != NoDebug) {
350 painter.setPen(QColor(255, 0, 0, 255));
351 if (m_debugFlags.testFlag(ImageRect))
352 painter.drawRect(QRect(QPoint(0, 0), alignedSize / dpr));
353 if (m_debugFlags.testFlag(LayoutRect)) {
354 const auto m = layoutMargins();
355 QRect rect = QRect(QPoint(0, 0), imgSize);
356 rect.adjust(m.left(), m.top(), -m.right(), -m.bottom());
357 painter.drawRect(rect);
359 if (m_debugFlags.testFlag(ContentRect)) {
360 const auto p = contentPadding();
361 QRect rect = QRect(QPoint(0, 0), imgSize);
362 rect.adjust(p.left(), p.top(), -p.right(), -p.bottom());
363 painter.drawRect(rect);
365 if (m_debugFlags.testFlag(InputContentSize)) {
366 const int offset = 2;
367 const QPoint p = m_styleItemGeometry.contentRect.topLeft();
368 painter.drawLine(p.x() - offset, p.y() - offset, p.x() + m_contentSize.width(), p.y() - offset);
369 painter.drawLine(p.x() - offset, p.y() - offset, p.x() - offset, p.y() + m_contentSize.height());
371 if (m_debugFlags.testFlag(NinePatchMargins)) {
372 const QMargins m = m_styleItemGeometry.ninePatchMargins;
373 if (m.right() != -1) {
374 painter.drawLine(m.left(), 0, m.left(), imgSize.height());
375 painter.drawLine(imgSize.width() - m.right(), 0, imgSize.width() - m.right(), imgSize.height());
377 if (m.bottom() != -1) {
378 painter.drawLine(0, m.top(), imgSize.width(), m.top());
379 painter.drawLine(0, imgSize.height() - m.bottom(), imgSize.width(), imgSize.height() - m.bottom());
382 if (m_debugFlags.testFlag(SaveImage)) {
385 static QString filename = QStringLiteral(
"styleitem_saveimage_");
386 const QString path = QDir::current().absoluteFilePath(filename);
387 const QString name = path + QString::number(nr) + QStringLiteral(
".png");
388 m_paintedImage.save(name);
389 qDebug() <<
"image saved to:" << name;
399 QScopedValueRollback<
bool> guard(m_polishing,
true);
401 const bool dirtyGeometry = m_dirty & DirtyFlag::Geometry;
402 const bool dirtyImage = isVisible() && ((m_dirty & DirtyFlag::Image) || (!m_useNinePatchImage && dirtyGeometry));
407 paintControlToImage();
411void QQuickStyleItem::addDebugInfo()
418 static const auto debugString = qEnvironmentVariable(
"QQC2_NATIVESTYLE_DEBUG");
419 static const auto matchAll = debugString.startsWith(QLatin1String(
"All "));
420 static const auto prefix = QStringLiteral(
"QQuickStyleItem");
421 if (debugString.isEmpty())
424 const auto objectName = m_control->objectName();
425 const auto typeName = QString::fromUtf8(metaObject()->className()).remove(prefix);
426 const bool matchName = !objectName.isEmpty() && debugString.startsWith(objectName);
427 const bool matchType = debugString.startsWith(typeName);
429 if (!(matchAll || matchName || matchType))
432#define QQC2_DEBUG_FLAG(FLAG)
433 if (debugString.contains(QLatin1String(#FLAG), Qt::CaseInsensitive)) m_debugFlags |= FLAG
435 QQC2_DEBUG_FLAG(Info);
436 QQC2_DEBUG_FLAG(ImageRect);
437 QQC2_DEBUG_FLAG(ContentRect);
438 QQC2_DEBUG_FLAG(LayoutRect);
439 QQC2_DEBUG_FLAG(InputContentSize);
440 QQC2_DEBUG_FLAG(DontUseNinePatchImage);
441 QQC2_DEBUG_FLAG(NinePatchMargins);
442 QQC2_DEBUG_FLAG(Unscaled);
443 QQC2_DEBUG_FLAG(Debug);
444 QQC2_DEBUG_FLAG(SaveImage);
446 if (m_debugFlags & (DontUseNinePatchImage
450 | NinePatchMargins)) {
453 m_debugFlags |= DontUseNinePatchImage;
454 m_useNinePatchImage =
false;
457 if (m_debugFlags != NoDebug)
458 qDebug() <<
"debug options set for" << typeName <<
"(" << objectName <<
"):" << m_debugFlags;
460 qDebug() <<
"available debug options:" << DebugFlags(0xFFFF);
466 Q_ASSERT_X(m_control, Q_FUNC_INFO,
"You need to assign a value to property 'control'");
470 QQuickItem::componentComplete();
478 return m_contentSize.width();
483 if (qFuzzyCompare(m_contentSize.width(), contentWidth))
486 m_contentSize.setWidth(contentWidth);
492 return m_contentSize.height();
497 if (qFuzzyCompare(m_contentSize.height(), contentHeight))
500 m_contentSize.setHeight(contentHeight);
506 const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize);
507 return QQuickStyleMargins(outerRect, m_styleItemGeometry.contentRect);
519 if (m_styleItemGeometry.layoutRect.isValid()) {
520 const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize);
521 margins = QQuickStyleMargins(outerRect, m_styleItemGeometry.layoutRect);
530 return m_styleItemGeometry.minimumSize;
537 return m_useNinePatchImage ? m_styleItemGeometry.minimumSize : size().toSize();
542 return m_styleItemGeometry.focusFrameRadius;
558 return QGuiApplication::font();
563#include "moc_qquickstyleitem_p.cpp"
virtual void connectToControl() const
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
qreal focusFrameRadius() const
void itemChange(ItemChange change, const ItemChangeData &data) override
Called when change occurs for this item.
void updatePolish() override
This function should perform any layout as required for this item.
void setContentHeight(qreal contentHeight)
QQuickStyleMargins contentPadding() const
~QQuickStyleItem() override
QQuickStyleMargins layoutMargins() const
QSize minimumSize() const
void setContentWidth(qreal contentWidth)
void initStyleOptionBase(QStyleOption &styleOption) const
QSGNode * updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
bool event(QEvent *event) override
\reimp
QDebug operator<<(QDebug debug, const StyleItemGeometry &cg)
static QWindow * effectiveWindow(QQuickWindow *window)
#define qqc2InfoHeading(HEADING)