7#include <QtCore/qscopedvaluerollback.h>
8#include <QtCore/qdir.h>
10#include <QtQuick/qsgninepatchnode.h>
11#include <QtQuick/private/qquickwindow_p.h>
12#include <QtQuick/qquickwindow.h>
13#include <QtQuick/qquickrendercontrol.h>
15#include <QtQuickTemplates2/private/qquickcontrol_p.h>
16#include <QtQuickTemplates2/private/qquickbutton_p.h>
18#include <QtQml/qqml.h>
27 QDebugStateSaver saver(debug);
29 debug <<
"StyleMargins(";
30 debug << padding.left() <<
", ";
31 debug << padding.top() <<
", ";
32 debug << padding.right() <<
", ";
33 debug << padding.bottom();
40 QDebugStateSaver saver(debug);
42 debug <<
"StyleItemGeometry(";
43 debug <<
"implicitSize:" << cg.implicitSize <<
", ";
44 debug <<
"contentRect:" << cg.contentRect <<
", ";
45 debug <<
"layoutRect:" << cg.layoutRect <<
", ";
46 debug <<
"minimumSize:" << cg.minimumSize <<
", ";
47 debug <<
"9patchMargins:" << cg.ninePatchMargins;
56 static int multiplier = [&]() {
57 const qreal dpr = window()->effectiveDevicePixelRatio();
58 for (
int m = 1; m <= 10; ++m) {
59 const qreal v = m * dpr;
64 qWarning() <<
"The current dpr (" << dpr <<
") is not supported"
65 <<
"by the style and might result in drawing artifacts";
69 return int(qCeil(qreal(size) / qreal(multiplier)) * multiplier);
75 setFlag(QQuickItem::ItemHasContents);
84 connect(m_control, &QQuickStyleItem::enabledChanged,
this, &QQuickStyleItem::markImageDirty);
85 connect(m_control, &QQuickItem::activeFocusChanged,
this, &QQuickStyleItem::markImageDirty);
87 if (QQuickWindow *win = window()) {
88 connect(win, &QQuickWindow::activeChanged,
this, &QQuickStyleItem::markImageDirty);
89 m_connectedWindow = win;
95 m_dirty.setFlag(DirtyFlag::Image);
96 if (isComponentComplete())
102 m_dirty.setFlag(DirtyFlag::Geometry);
103 if (isComponentComplete())
109 QSGNinePatchNode *node =
static_cast<QSGNinePatchNode *>(oldNode);
111 node = window()->createNinePatchNode();
113 if (m_paintedImage.isNull()) {
120 const auto texture = window()->createTextureFromImage(m_paintedImage, QQuickWindow::TextureCanUseAtlas);
122 QRectF bounds = boundingRect();
123 const qreal dpr = window()->effectiveDevicePixelRatio();
124 const QSizeF unscaledImageSize = QSizeF(m_paintedImage.size()) / dpr;
130 if (bounds.width() < unscaledImageSize.width())
131 bounds.setWidth(unscaledImageSize.width());
132 if (bounds.height() < unscaledImageSize.height())
133 bounds.setHeight(unscaledImageSize.height());
136 if (m_debugFlags.testFlag(Unscaled)) {
137 bounds.setSize(unscaledImageSize);
138 qqc2Info() <<
"Setting qsg node size to the unscaled size of m_paintedImage:" << bounds;
142 if (m_useNinePatchImage) {
143 QMargins padding = m_styleItemGeometry.ninePatchMargins;
144 if (padding.right() == -1) {
150 if (padding.bottom() == -1) {
152 padding.setBottom(0);
154 node->setPadding(padding.left(), padding.top(), padding.right(), padding.bottom());
157 node->setBounds(bounds);
158 node->setTexture(texture);
159 node->setDevicePixelRatio(dpr);
168 if (item->metaObject()->indexOfProperty(
"qqc2_style_small") != -1)
169 return QStyle::State_Small;
170 if (item->metaObject()->indexOfProperty(
"qqc2_style_mini") != -1)
171 return QStyle::State_Mini;
172 return QStyle::State_None;
177 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(window);
178 return renderWindow ? renderWindow : window;
185 styleOption.control =
const_cast<QQuickItem *>(control<QQuickItem>());
186 styleOption.window = effectiveWindow(window());
187 styleOption.palette = QQuickItemPrivate::get(m_control)->palette()->toQPalette();
188 styleOption.rect = QRect(QPoint(0, 0), imageSize());
190 styleOption.state = QStyle::State_None;
191 styleOption.state |= controlSize(styleOption.control);
194 if (
const auto quickControl =
dynamic_cast<QQuickControl *>(m_control.data()))
195 styleOption.direction = quickControl->isMirrored() ? Qt::RightToLeft : Qt::LeftToRight;
197 if (styleOption.window) {
198 if (styleOption.window->isActive())
199 styleOption.state |= QStyle::State_Active;
200 if (m_control->isEnabled())
201 styleOption.state |= QStyle::State_Enabled;
202 if (m_control->hasActiveFocus())
203 styleOption.state |= QStyle::State_HasFocus;
204 if (m_control->isUnderMouse())
205 styleOption.state |= QStyle::State_MouseOver;
207 styleOption.state |= QStyle::State_KeyboardFocusChange;
210 if (m_overrideState != None) {
213 if (m_overrideState & AlwaysHovered)
214 styleOption.state |= QStyle::State_MouseOver;
215 else if (m_overrideState & NeverHovered)
216 styleOption.state &= ~QStyle::State_MouseOver;
224 QQuickItem::geometryChange(newGeometry, oldGeometry);
235 QQuickItem::itemChange(change, data);
238 case QQuickItem::ItemVisibleHasChanged:
242 case QQuickItem::ItemSceneChange: {
244 QQuickWindow *win = data.window;
245 if (m_connectedWindow)
246 disconnect(m_connectedWindow, &QQuickWindow::activeChanged,
this, &QQuickStyleItem::markImageDirty);
248 connect(win, &QQuickWindow::activeChanged,
this, &QQuickStyleItem::markImageDirty);
249 m_connectedWindow = win;
258 if (event->type() == QEvent::ApplicationPaletteChange) {
260 if (
auto *style = QQuickStyleItem::style())
264 return QQuickItem::event(event);
270 m_dirty.setFlag(DirtyFlag::Geometry,
false);
274 const QSize oldMinimumSize = minimumSize();
276 m_styleItemGeometry = calculateGeometry();
279 if (m_styleItemGeometry.minimumSize.isEmpty())
280 qmlWarning(
this) <<
"(StyleItem) minimumSize is empty!";
283 if (m_styleItemGeometry.implicitSize.isEmpty()) {
286 m_styleItemGeometry.implicitSize = m_styleItemGeometry.minimumSize;
287 qqc2Info() <<
"implicitSize is empty, using minimumSize instead";
291 if (m_styleItemGeometry.implicitSize.width() < m_styleItemGeometry.minimumSize.width())
292 qmlWarning(
this) <<
"(StyleItem) implicit width is smaller than minimum width!";
293 if (m_styleItemGeometry.implicitSize.height() < m_styleItemGeometry.minimumSize.height())
294 qmlWarning(
this) <<
"(StyleItem) implicit height is smaller than minimum height!";
298 emit contentPaddingChanged();
300 emit layoutMarginsChanged();
301 if (minimumSize() != oldMinimumSize)
302 emit minimumSizeChanged();
304 setImplicitSize(m_styleItemGeometry.implicitSize.width(), m_styleItemGeometry.implicitSize.height());
307 <<
"bounding rect:" << boundingRect()
308 <<
"layout margins:" << layoutMargins()
309 <<
"content padding:" << contentPadding()
310 <<
"input content size:" << m_contentSize;
316 const QSize imgSize = imageSize();
317 if (imgSize.isEmpty())
320 m_dirty.setFlag(DirtyFlag::Image,
false);
333 const qreal dpr = window()->effectiveDevicePixelRatio();
334 const int alignedW =
int(dprAlignedSize(imgSize.width()) * dpr);
335 const int alignedH =
int(dprAlignedSize(imgSize.height()) * dpr);
336 const QSize alignedSize = QSize(alignedW, alignedH);
338 if (m_paintedImage.size() != alignedSize) {
339 m_paintedImage = QImage(alignedSize, QImage::Format_ARGB32_Premultiplied);
340 m_paintedImage.setDevicePixelRatio(dpr);
341 qqc2Info() <<
"created image with dpr aligned size:" << alignedSize;
344 m_paintedImage.fill(Qt::transparent);
347 paintEvent(&painter);
350 if (m_debugFlags != NoDebug) {
351 painter.setPen(QColor(255, 0, 0, 255));
352 if (m_debugFlags.testFlag(ImageRect))
353 painter.drawRect(QRect(QPoint(0, 0), alignedSize / dpr));
354 if (m_debugFlags.testFlag(LayoutRect)) {
355 const auto m = layoutMargins();
356 QRect rect = QRect(QPoint(0, 0), imgSize);
357 rect.adjust(m.left(), m.top(), -m.right(), -m.bottom());
358 painter.drawRect(rect);
360 if (m_debugFlags.testFlag(ContentRect)) {
361 const auto p = contentPadding();
362 QRect rect = QRect(QPoint(0, 0), imgSize);
363 rect.adjust(p.left(), p.top(), -p.right(), -p.bottom());
364 painter.drawRect(rect);
366 if (m_debugFlags.testFlag(InputContentSize)) {
367 const int offset = 2;
368 const QPoint p = m_styleItemGeometry.contentRect.topLeft();
369 painter.drawLine(p.x() - offset, p.y() - offset, p.x() + m_contentSize.width(), p.y() - offset);
370 painter.drawLine(p.x() - offset, p.y() - offset, p.x() - offset, p.y() + m_contentSize.height());
372 if (m_debugFlags.testFlag(NinePatchMargins)) {
373 const QMargins m = m_styleItemGeometry.ninePatchMargins;
374 if (m.right() != -1) {
375 painter.drawLine(m.left(), 0, m.left(), imgSize.height());
376 painter.drawLine(imgSize.width() - m.right(), 0, imgSize.width() - m.right(), imgSize.height());
378 if (m.bottom() != -1) {
379 painter.drawLine(0, m.top(), imgSize.width(), m.top());
380 painter.drawLine(0, imgSize.height() - m.bottom(), imgSize.width(), imgSize.height() - m.bottom());
383 if (m_debugFlags.testFlag(SaveImage)) {
386 static QString filename = QStringLiteral(
"styleitem_saveimage_");
387 const QString path = QDir::current().absoluteFilePath(filename);
388 const QString name = path + QString::number(nr) + QStringLiteral(
".png");
389 m_paintedImage.save(name);
390 qDebug() <<
"image saved to:" << name;
400 QScopedValueRollback<
bool> guard(m_polishing,
true);
402 const bool dirtyGeometry = m_dirty & DirtyFlag::Geometry;
403 const bool dirtyImage = isVisible() && ((m_dirty & DirtyFlag::Image) || (!m_useNinePatchImage && dirtyGeometry));
408 paintControlToImage();
412void QQuickStyleItem::addDebugInfo()
419 static const auto debugString = qEnvironmentVariable(
"QQC2_NATIVESTYLE_DEBUG");
420 static const auto matchAll = debugString.startsWith(QLatin1String(
"All "));
421 static const auto prefix = QStringLiteral(
"QQuickStyleItem");
422 if (debugString.isEmpty())
425 const auto objectName = m_control->objectName();
426 const auto typeName = QString::fromUtf8(metaObject()->className()).remove(prefix);
427 const bool matchName = !objectName.isEmpty() && debugString.startsWith(objectName);
428 const bool matchType = debugString.startsWith(typeName);
430 if (!(matchAll || matchName || matchType))
433#define QQC2_DEBUG_FLAG(FLAG)
434 if (debugString.contains(QLatin1String(#FLAG), Qt::CaseInsensitive)) m_debugFlags |= FLAG
436 QQC2_DEBUG_FLAG(Info);
437 QQC2_DEBUG_FLAG(ImageRect);
438 QQC2_DEBUG_FLAG(ContentRect);
439 QQC2_DEBUG_FLAG(LayoutRect);
440 QQC2_DEBUG_FLAG(InputContentSize);
441 QQC2_DEBUG_FLAG(DontUseNinePatchImage);
442 QQC2_DEBUG_FLAG(NinePatchMargins);
443 QQC2_DEBUG_FLAG(Unscaled);
444 QQC2_DEBUG_FLAG(Debug);
445 QQC2_DEBUG_FLAG(SaveImage);
447 if (m_debugFlags & (DontUseNinePatchImage
451 | NinePatchMargins)) {
454 m_debugFlags |= DontUseNinePatchImage;
455 m_useNinePatchImage =
false;
458 if (m_debugFlags != NoDebug)
459 qDebug() <<
"debug options set for" << typeName <<
"(" << objectName <<
"):" << m_debugFlags;
461 qDebug() <<
"available debug options:" << DebugFlags(0xFFFF);
467 Q_ASSERT_X(m_control, Q_FUNC_INFO,
"You need to assign a value to property 'control'");
471 QQuickItem::componentComplete();
479 return m_contentSize.width();
484 if (qFuzzyCompare(m_contentSize.width(), contentWidth))
487 m_contentSize.setWidth(contentWidth);
493 return m_contentSize.height();
498 if (qFuzzyCompare(m_contentSize.height(), contentHeight))
501 m_contentSize.setHeight(contentHeight);
507 const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize);
508 return QQuickStyleMargins(outerRect, m_styleItemGeometry.contentRect);
520 if (m_styleItemGeometry.layoutRect.isValid()) {
521 const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize);
522 margins = QQuickStyleMargins(outerRect, m_styleItemGeometry.layoutRect);
531 return m_styleItemGeometry.minimumSize;
538 return m_useNinePatchImage ? m_styleItemGeometry.minimumSize : size().toSize();
543 return m_styleItemGeometry.focusFrameRadius;
559 return QGuiApplication::font();
564#include "moc_qquickstyleitem.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
static QWindow * effectiveWindow(QQuickWindow *window)
#define qqc2InfoHeading(HEADING)