1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qlayout.h>
32#include "qstyle.h"
33#include <qevent.h>
34#include <qpainter.h>
35#include <qpixmap.h>
36#include <qapplication.h>
37#include <qwidget.h>
38#include <qlabel.h>
39#include <qstyleoption.h>
40#include <qscrollbar.h>
41#include <qprogressbar.h>
42#include <qtoolbutton.h>
43#include <qtoolbar.h>
44
45#include <qcommonstyle.h>
46#include <qproxystyle.h>
47#include <qstylefactory.h>
48
49#include <qimagereader.h>
50#include <qimagewriter.h>
51#include <qmenu.h>
52#include <qpushbutton.h>
53#include <qspinbox.h>
54#include <qcombobox.h>
55#include <qradiobutton.h>
56#include <qlineedit.h>
57#include <qmdiarea.h>
58#include <qscrollarea.h>
59#include <qwidget.h>
60
61#include <algorithm>
62
63#include <QtTest/private/qtesthelpers_p.h>
64
65using namespace QTestPrivate;
66
67class tst_QStyle : public QObject
68{
69 Q_OBJECT
70
71private slots:
72 void drawItemPixmap();
73 void cleanup();
74#ifndef QT_NO_STYLE_FUSION
75 void testFusionStyle();
76#endif
77 void testWindowsStyle();
78#if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) && !defined(Q_OS_WINRT)
79 void testWindowsVistaStyle();
80#endif
81#ifdef Q_OS_MAC
82 void testMacStyle();
83#endif
84 void testStyleFactory();
85 void testProxyStyle();
86 void pixelMetric();
87#if !defined(QT_NO_STYLE_WINDOWS) && !defined(QT_NO_STYLE_FUSION)
88 void progressBarChangeStyle();
89#endif
90 void defaultFont();
91 void testDrawingShortcuts();
92 void testFrameOnlyAroundContents();
93
94 void testProxyCalled();
95 void testStyleOptionInit();
96private:
97 bool testAllFunctions(QStyle *);
98 bool testScrollBarSubControls(const QStyle *style);
99 void testPainting(QStyle *style, const QString &platform);
100 void lineUpLayoutTest(QStyle *);
101};
102
103class MyWidget : public QWidget
104{
105public:
106 using QWidget::QWidget;
107
108protected:
109 void paintEvent(QPaintEvent *) override;
110};
111
112void tst_QStyle::cleanup()
113{
114 QVERIFY(QApplication::topLevelWidgets().isEmpty());
115}
116
117void tst_QStyle::testStyleFactory()
118{
119 const QStringList keys = QStyleFactory::keys();
120#ifndef QT_NO_STYLE_FUSION
121 QVERIFY(keys.contains("Fusion"));
122#endif
123#ifndef QT_NO_STYLE_WINDOWS
124 QVERIFY(keys.contains("Windows"));
125#endif
126
127 for (const QString &styleName : keys) {
128 QScopedPointer<QStyle> style(QStyleFactory::create(styleName));
129 QVERIFY2(!style.isNull(),
130 qPrintable(QString::fromLatin1("Fail to load style '%1'").arg(styleName)));
131 }
132}
133
134class CustomProxy : public QProxyStyle
135{
136 int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr,
137 const QWidget *widget = nullptr) const override
138 {
139 if (metric == QStyle::PM_ButtonIconSize)
140 return 13;
141 return QProxyStyle::pixelMetric(metric, option, widget);
142 }
143};
144
145void tst_QStyle::testProxyStyle()
146{
147 QProxyStyle *proxyStyle = new QProxyStyle();
148 QVERIFY(proxyStyle->baseStyle());
149 QStyle *style = QStyleFactory::create("Windows");
150 QCOMPARE(style->proxy(), style);
151
152 proxyStyle->setBaseStyle(style);
153 QCOMPARE(style->proxy(), proxyStyle);
154 QCOMPARE(style->parent(), proxyStyle);
155 QCOMPARE(proxyStyle->baseStyle(), style);
156
157 QVERIFY(testAllFunctions(proxyStyle));
158 proxyStyle->setBaseStyle(nullptr);
159 QVERIFY(proxyStyle->baseStyle());
160 QApplication::setStyle(proxyStyle);
161
162 QProxyStyle* baseStyle = new QProxyStyle("Windows");
163 QCOMPARE(baseStyle->baseStyle()->objectName(), style->objectName());
164
165 QProxyStyle doubleProxy(baseStyle);
166 QVERIFY(testAllFunctions(&doubleProxy));
167
168 CustomProxy customStyle;
169 QLineEdit edit;
170 edit.setStyle(&customStyle);
171 QVERIFY(!customStyle.parent());
172 QCOMPARE(edit.style()->pixelMetric(QStyle::PM_ButtonIconSize), 13);
173}
174
175void tst_QStyle::drawItemPixmap()
176{
177 MyWidget testWidget;
178 testWidget.setObjectName("testObject");
179 testWidget.resize(w: 300, h: 300);
180 testWidget.showNormal();
181
182 QImage image = testWidget.grab().toImage();
183 const QRgb green = QColor(Qt::green).rgb();
184 QVERIFY(image.reinterpretAsFormat(QImage::Format_RGB32));
185 const QRgb *bits = reinterpret_cast<const QRgb *>(image.constBits());
186 const QRgb *end = bits + image.sizeInBytes() / sizeof(QRgb);
187#ifdef Q_OS_WINRT
188 QEXPECT_FAIL("", "QWidget::resize does not work on WinRT", Continue);
189#endif
190 QVERIFY(std::all_of(bits, end, [green] (QRgb r) { return r == green; }));
191}
192
193bool tst_QStyle::testAllFunctions(QStyle *style)
194{
195 QStyleOption opt;
196 QWidget testWidget;
197 opt.init(w: &testWidget);
198
199 testWidget.setStyle(style);
200
201 //Tests styleHint with default arguments for potential crashes
202 for ( int hint = 0 ; hint < int(QStyle::SH_Menu_Mask); ++hint) {
203 style->styleHint(stylehint: QStyle::StyleHint(hint));
204 style->styleHint(stylehint: QStyle::StyleHint(hint), opt: &opt, widget: &testWidget);
205 }
206
207 //Tests pixelMetric with default arguments for potential crashes
208 for ( int pm = 0 ; pm < int(QStyle::PM_LayoutVerticalSpacing); ++pm) {
209 style->pixelMetric(metric: QStyle::PixelMetric(pm));
210 style->pixelMetric(metric: QStyle::PixelMetric(pm), option: &opt, widget: &testWidget);
211 }
212
213 //Tests drawControl with default arguments for potential crashes
214 for ( int control = 0 ; control < int(QStyle::CE_ColumnViewGrip); ++control) {
215 QPixmap surface(QSize(200, 200));
216 QPainter painter(&surface);
217 style->drawControl(element: QStyle::ControlElement(control), opt: &opt, p: &painter, w: nullptr);
218 }
219
220 //Tests drawComplexControl with default arguments for potential crashes
221 {
222 QPixmap surface(QSize(200, 200));
223 QPainter painter(&surface);
224 QStyleOptionComboBox copt1;
225 copt1.init(w: &testWidget);
226
227 QStyleOptionGroupBox copt2;
228 copt2.init(w: &testWidget);
229 QStyleOptionSizeGrip copt3;
230 copt3.init(w: &testWidget);
231 QStyleOptionSlider copt4;
232 copt4.init(w: &testWidget);
233 copt4.minimum = 0;
234 copt4.maximum = 100;
235 copt4.tickInterval = 25;
236 copt4.sliderValue = 50;
237 QStyleOptionSpinBox copt5;
238 copt5.init(w: &testWidget);
239 QStyleOptionTitleBar copt6;
240 copt6.init(w: &testWidget);
241 QStyleOptionToolButton copt7;
242 copt7.init(w: &testWidget);
243 QStyleOptionComplex copt9;
244 copt9.initFrom(w: &testWidget);
245
246 style->drawComplexControl(cc: QStyle::CC_SpinBox, opt: &copt5, p: &painter, widget: nullptr);
247 style->drawComplexControl(cc: QStyle::CC_ComboBox, opt: &copt1, p: &painter, widget: nullptr);
248 style->drawComplexControl(cc: QStyle::CC_ScrollBar, opt: &copt4, p: &painter, widget: nullptr);
249 style->drawComplexControl(cc: QStyle::CC_Slider, opt: &copt4, p: &painter, widget: nullptr);
250 style->drawComplexControl(cc: QStyle::CC_ToolButton, opt: &copt7, p: &painter, widget: nullptr);
251 style->drawComplexControl(cc: QStyle::CC_TitleBar, opt: &copt6, p: &painter, widget: nullptr);
252 style->drawComplexControl(cc: QStyle::CC_GroupBox, opt: &copt2, p: &painter, widget: nullptr);
253 style->drawComplexControl(cc: QStyle::CC_Dial, opt: &copt4, p: &painter, widget: nullptr);
254 }
255
256 //Check standard pixmaps/icons
257 for ( int i = 0 ; i < int(QStyle::SP_ToolBarVerticalExtensionButton); ++i) {
258 QPixmap pixmap = style->standardPixmap(standardPixmap: QStyle::StandardPixmap(i));
259 if (pixmap.isNull()) {
260 qWarning(msg: "missing StandardPixmap: %d", i);
261 }
262 QIcon icn = style->standardIcon(standardIcon: QStyle::StandardPixmap(i));
263 if (icn.isNull()) {
264 qWarning(msg: "missing StandardIcon: %d", i);
265 }
266 }
267
268 style->itemPixmapRect(r: QRect(0, 0, 100, 100), flags: Qt::AlignHCenter, pixmap: QPixmap(200, 200));
269 style->itemTextRect(fm: QFontMetrics(QApplication::font()), r: QRect(0, 0, 100, 100),
270 flags: Qt::AlignHCenter, enabled: true, text: QLatin1String("Test"));
271
272 return testScrollBarSubControls(style);
273}
274
275bool tst_QStyle::testScrollBarSubControls(const QStyle *style)
276{
277 const bool isMacStyle = style->objectName().compare(other: QLatin1String("macintosh"),
278 cs: Qt::CaseInsensitive) == 0;
279 QScrollBar scrollBar;
280 setFrameless(&scrollBar);
281 scrollBar.show();
282 const QStyleOptionSlider opt = qt_qscrollbarStyleOption(scrollBar: &scrollBar);
283 for (int sc : {1, 2, 4, 8}) {
284 const auto subControl = static_cast<QStyle::SubControl>(sc);
285 const QRect sr = style->subControlRect(cc: QStyle::CC_ScrollBar, opt: &opt, sc: subControl, widget: &scrollBar);
286 if (sr.isNull()) {
287 // macOS scrollbars no longer have these, so there's no reason to fail
288 if (!(isMacStyle && (subControl == QStyle::SC_ScrollBarAddLine ||
289 subControl == QStyle::SC_ScrollBarSubLine))) {
290 qWarning() << "Unexpected null rect for subcontrol" << subControl;
291 return false;
292 }
293 }
294 }
295 return true;
296}
297
298#ifndef QT_NO_STYLE_FUSION
299void tst_QStyle::testFusionStyle()
300{
301 QScopedPointer<QStyle> fstyle(QStyleFactory::create("Fusion"));
302 QVERIFY(!fstyle.isNull());
303 QVERIFY(testAllFunctions(fstyle.data()));
304 lineUpLayoutTest(fstyle.data());
305}
306#endif
307
308void tst_QStyle::testWindowsStyle()
309{
310 QScopedPointer<QStyle> wstyle(QStyleFactory::create("Windows"));
311 QVERIFY(!wstyle.isNull());
312 QVERIFY(testAllFunctions(wstyle.data()));
313 lineUpLayoutTest(wstyle.data());
314
315 // Tests drawing indeterminate progress with 0 size: QTBUG-15973
316 QStyleOptionProgressBar pb;
317 pb.rect = QRect(0,0,-9,0);
318 QPixmap surface(QSize(200, 200));
319 QPainter painter(&surface);
320 wstyle->drawControl(element: QStyle::CE_ProgressBar, opt: &pb, p: &painter, w: nullptr);
321}
322
323#if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) && !defined(Q_OS_WINRT)
324void tst_QStyle::testWindowsVistaStyle()
325{
326 QScopedPointer<QStyle> vistastyle(QStyleFactory::create("WindowsVista"));
327 QVERIFY(!vistastyle.isNull());
328 QVERIFY(testAllFunctions(vistastyle.data()));
329}
330#endif
331
332#ifdef Q_OS_MAC
333void tst_QStyle::testMacStyle()
334{
335 QStyle *mstyle = QStyleFactory::create("Macintosh");
336 QVERIFY(testAllFunctions(mstyle));
337 delete mstyle;
338}
339#endif
340
341// Helper class...
342void MyWidget::paintEvent(QPaintEvent *)
343{
344 QPainter p(this);
345 QPixmap big(400,400);
346 big.fill(fillColor: Qt::green);
347 style()->drawItemPixmap(painter: &p, rect: rect(), alignment: Qt::AlignCenter, pixmap: big);
348}
349
350
351class Qt42Style : public QCommonStyle
352{
353 Q_OBJECT
354public:
355 int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr,
356 const QWidget *widget = nullptr) const override;
357
358 int margin_toplevel = 10;
359 int margin = 5;
360 int spacing = 0;
361};
362
363int Qt42Style::pixelMetric(PixelMetric metric, const QStyleOption * /* option = 0*/,
364 const QWidget * /* widget = 0*/ ) const
365{
366 switch (metric) {
367 case QStyle::PM_DefaultTopLevelMargin:
368 return margin_toplevel;
369 case QStyle::PM_DefaultChildMargin:
370 return margin;
371 case QStyle::PM_DefaultLayoutSpacing:
372 return spacing;
373 default:
374 break;
375 }
376 return -1;
377}
378
379
380void tst_QStyle::pixelMetric()
381{
382 QScopedPointer<Qt42Style> style(new Qt42Style);
383 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), 10);
384 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), 5);
385 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), 0);
386
387 style->margin_toplevel = 0;
388 style->margin = 0;
389 style->spacing = 0;
390 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), 0);
391 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), 0);
392 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), 0);
393
394 style->margin_toplevel = -1;
395 style->margin = -1;
396 style->spacing = -1;
397 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), -1);
398 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), -1);
399 QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), -1);
400}
401
402#if !defined(QT_NO_STYLE_WINDOWS) && !defined(QT_NO_STYLE_FUSION)
403void tst_QStyle::progressBarChangeStyle()
404{
405 //test a crashing situation (task 143530)
406 //where changing the styles and deleting a progressbar would crash
407
408 QStyle *style1 = QStyleFactory::create("Windows");
409 QStyle *style2 = QStyleFactory::create("Fusion");
410
411 QProgressBar *progress=new QProgressBar;
412 progress->setStyle(style1);
413
414 progress->show();
415
416 progress->setStyle(style2);
417
418 QTest::qWait(ms: 100);
419 delete progress;
420
421 QTest::qWait(ms: 100);
422
423 //before the correction, there would be a crash here
424 delete style1;
425 delete style2;
426}
427#endif
428
429void tst_QStyle::lineUpLayoutTest(QStyle *style)
430{
431 QWidget widget;
432 setFrameless(&widget);
433 QHBoxLayout layout;
434 QFont font;
435 font.setPointSize(9); //Plastique is lined up for odd numbers...
436 widget.setFont(font);
437 QSpinBox spinbox(&widget);
438 QLineEdit lineedit(&widget);
439 QComboBox combo(&widget);
440 combo.setEditable(true);
441 layout.addWidget(&spinbox);
442 layout.addWidget(&lineedit);
443 layout.addWidget(&combo);
444 widget.setLayout(&layout);
445 widget.setStyle(style);
446 // propagate the style.
447 const auto children = widget.findChildren<QWidget *>();
448 for (QWidget *w : children)
449 w->setStyle(style);
450 widget.show();
451 QVERIFY(QTest::qWaitForWindowExposed(&widget));
452
453#ifdef Q_OS_WIN
454 const int limit = 2; // Aero style has larger margins
455#else
456 const int limit = 1;
457#endif
458 const int slDiff = qAbs(t: spinbox.height() - lineedit.height());
459 const int scDiff = qAbs(t: spinbox.height() - combo.height());
460 QVERIFY2(slDiff <= limit,
461 qPrintable(QString::fromLatin1("%1 exceeds %2 for %3")
462 .arg(slDiff).arg(limit).arg(style->objectName())));
463 QVERIFY2(scDiff <= limit,
464 qPrintable(QString::fromLatin1("%1 exceeds %2 for %3")
465 .arg(scDiff).arg(limit).arg(style->objectName())));
466}
467
468void tst_QStyle::defaultFont()
469{
470 QFont defaultFont = QApplication::font();
471 QFont pointFont = defaultFont;
472 pointFont.setPixelSize(9);
473 QApplication::setFont(pointFont);
474 QPushButton button;
475 setFrameless(&button);
476 button.show();
477 QCoreApplication::processEvents();
478 QApplication::setFont(defaultFont);
479}
480
481class DrawTextStyle : public QProxyStyle
482{
483 Q_OBJECT
484public:
485 using QProxyStyle::QProxyStyle;
486
487 void drawItemText(QPainter *painter, const QRect &rect,
488 int flags, const QPalette &pal, bool enabled,
489 const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const override
490 {
491 alignment = flags;
492 QProxyStyle::drawItemText(painter, rect, flags, pal, enabled, text, textRole);
493 }
494
495 mutable int alignment = 0;
496};
497
498
499void tst_QStyle::testDrawingShortcuts()
500{
501 {
502 QWidget w;
503 setFrameless(&w);
504 QToolButton *tb = new QToolButton(&w);
505 tb->setText("&abc");
506 QScopedPointer<DrawTextStyle> dts(new DrawTextStyle);
507 w.show();
508 tb->setStyle(dts.data());
509 tb->grab();
510 QStyleOptionToolButton sotb;
511 sotb.initFrom(w: tb);
512 bool showMnemonic = dts->styleHint(hint: QStyle::SH_UnderlineShortcut, option: &sotb, widget: tb);
513 QVERIFY(dts->alignment & (showMnemonic ? Qt::TextShowMnemonic : Qt::TextHideMnemonic));
514 }
515 {
516 QToolBar w;
517 setFrameless(&w);
518 QToolButton *tb = new QToolButton(&w);
519 tb->setText("&abc");
520 QScopedPointer<DrawTextStyle> dts(new DrawTextStyle);
521 w.addWidget(widget: tb);
522 w.show();
523 tb->setStyle(dts.data());
524 tb->grab();
525 QStyleOptionToolButton sotb;
526 sotb.initFrom(w: tb);
527 bool showMnemonic = dts->styleHint(hint: QStyle::SH_UnderlineShortcut, option: &sotb, widget: tb);
528 QVERIFY(dts->alignment & (showMnemonic ? Qt::TextShowMnemonic : Qt::TextHideMnemonic));
529 }
530}
531
532static const int SCROLLBAR_SPACING = 33;
533
534class FrameTestStyle : public QProxyStyle {
535public:
536 FrameTestStyle() : QProxyStyle("Windows") { }
537
538 int styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget,
539 QStyleHintReturn *returnData) const override
540 {
541 if (hint == QStyle::SH_ScrollView_FrameOnlyAroundContents)
542 return 1;
543 return QProxyStyle ::styleHint(hint, option: opt, widget, returnData);
544 }
545
546 int pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const override
547 {
548 if (pm == QStyle::PM_ScrollView_ScrollBarSpacing)
549 return SCROLLBAR_SPACING;
550 return QProxyStyle ::pixelMetric(metric: pm, option ,widget);
551 }
552};
553
554void tst_QStyle::testFrameOnlyAroundContents()
555{
556 QScrollArea area;
557 area.setGeometry(ax: 0, ay: 0, aw: 200, ah: 200);
558 QScopedPointer<QStyle> winStyle(QStyleFactory::create("Windows"));
559 FrameTestStyle frameStyle;
560 QWidget *widget = new QWidget(&area);
561 widget->setGeometry(ax: 0, ay: 0, aw: 400, ah: 400);
562 area.setStyle(winStyle.data());
563 area.verticalScrollBar()->setStyle(winStyle.data());
564 area.setWidget(widget);
565 area.setVisible(true);
566 int viewPortWidth = area.viewport()->width();
567 area.verticalScrollBar()->setStyle(&frameStyle);
568 area.setStyle(&frameStyle);
569 // Test that we reserve space for scrollbar spacing
570#ifdef Q_OS_WINRT
571 QEXPECT_FAIL("", "QWidget::setGeometry does not work on WinRT", Continue);
572#endif
573 QCOMPARE(viewPortWidth, area.viewport()->width() + SCROLLBAR_SPACING);
574}
575
576
577class ProxyTest: public QProxyStyle
578{
579 Q_OBJECT
580public:
581 using QProxyStyle::QProxyStyle;
582
583 void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
584 const QWidget *w) const override
585 {
586 called = true;
587 return QProxyStyle::drawPrimitive(element: pe, option: opt, painter: p, widget: w);
588 }
589
590 mutable bool called = false;
591};
592
593
594void tst_QStyle::testProxyCalled()
595{
596 QToolButton b;
597 b.setArrowType(Qt::DownArrow);
598 QStyleOptionToolButton opt;
599 opt.init(w: &b);
600 opt.features |= QStyleOptionToolButton::Arrow;
601 QPixmap surface(QSize(200, 200));
602 QPainter painter(&surface);
603
604 const QStringList keys = QStyleFactory::keys();
605 QVector<QStyle*> styles;
606 styles.reserve(size: keys.size() + 1);
607
608 styles << new QCommonStyle();
609
610 for (const QString &key : keys)
611 styles << QStyleFactory::create(key);
612
613 for (QStyle *style : styles) {
614 ProxyTest testStyle;
615 testStyle.setBaseStyle(style);
616 style->drawControl(element: QStyle::CE_ToolButtonLabel, opt: &opt, p: &painter, w: &b);
617 QVERIFY(testStyle.called);
618 delete style;
619 }
620}
621
622
623class TestStyleOptionInitProxy: public QProxyStyle
624{
625 Q_OBJECT
626public:
627 mutable bool invalidOptionsDetected = false;
628
629 using QProxyStyle::QProxyStyle;
630
631 void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w) const override {
632 checkStyleEnum<QStyle::PrimitiveElement>(element: pe, opt);
633 return QProxyStyle::drawPrimitive(element: pe, option: opt, painter: p, widget: w);
634 }
635
636 void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const override {
637 checkStyleEnum<QStyle::ControlElement>(element, opt);
638 return QProxyStyle::drawControl(element, option: opt, painter: p, widget: w);
639 }
640
641 QRect subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget) const override {
642 checkStyleEnum<QStyle::SubElement>(element: subElement, opt: option);
643 return QProxyStyle::subElementRect(element: subElement, option, widget);
644 }
645
646 void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *widget) const override {
647 checkStyleEnum<QStyle::ComplexControl>(element: cc, opt);
648 return QProxyStyle::drawComplexControl(control: cc, option: opt, painter: p, widget);
649 }
650
651 QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const override {
652 checkStyleEnum<QStyle::ComplexControl>(element: cc, opt);
653 return QProxyStyle::subControlRect(cc, opt, sc, widget);
654 }
655
656 int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override {
657 checkStyleEnum<QStyle::PixelMetric>(element: metric, opt: option);
658 return QProxyStyle::pixelMetric(metric, option, widget);
659 }
660
661 QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize, const QWidget *w) const override {
662 checkStyleEnum<QStyle::ContentsType>(element: ct, opt);
663 return QProxyStyle::sizeFromContents(type: ct, option: opt, size: contentsSize, widget: w);
664 }
665
666 int styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const override {
667 checkStyleEnum<QStyle::StyleHint>(element: stylehint, opt);
668 return QProxyStyle::styleHint(hint: stylehint, option: opt, widget, returnData);
669 }
670
671 QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget) const override {
672 checkStyleEnum<QStyle::StandardPixmap>(element: standardPixmap, opt);
673 return QProxyStyle::standardPixmap(standardPixmap, opt, widget);
674 }
675
676 QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const override {
677 checkStyleEnum<QStyle::StandardPixmap>(element: standardIcon, opt: option);
678 return QProxyStyle::standardIcon(standardIcon, option, widget);
679 }
680
681 QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const override {
682 checkStyle(info: QString::asprintf(format: "QIcon::Mode(%i)", iconMode).toLatin1(), opt);
683 return QProxyStyle::generatedIconPixmap(iconMode, pixmap, opt);
684 }
685
686 int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget) const override {
687 checkStyle(info: QString::asprintf(format: "QSizePolicy::ControlType(%i), QSizePolicy::ControlType(%i)", control1, control2).toLatin1(), opt: option);
688 return QProxyStyle::layoutSpacing(control1, control2, orientation, option, widget);
689 }
690
691private:
692 void checkStyle(const QByteArray &info, const QStyleOption *opt) const {
693 if (opt && (opt->version == 0 || opt->styleObject == nullptr) ) {
694 invalidOptionsDetected = true;
695 qWarning() << baseStyle()->metaObject()->className()
696 << "Invalid QStyleOption found for"
697 << info;
698 qWarning() << "Version:" << opt->version << "StyleObject:" << opt->styleObject;
699 }
700 }
701
702 template<typename MEnum>
703 void checkStyleEnum(MEnum element, const QStyleOption *opt) const {
704 static QMetaEnum _enum = QMetaEnum::fromType<MEnum>();
705 checkStyle(info: _enum.valueToKey(value: element), opt);
706 }
707};
708
709void tst_QStyle::testStyleOptionInit()
710{
711 QStringList keys = QStyleFactory::keys();
712 keys.prepend(t: QString()); // QCommonStyle marker
713
714 for (const QString &key : qAsConst(t&: keys)) {
715 QStyle* style = key.isEmpty() ? new QCommonStyle : QStyleFactory::create(key);
716 TestStyleOptionInitProxy testStyle;
717 testStyle.setBaseStyle(style);
718 testAllFunctions(style);
719 QVERIFY(!testStyle.invalidOptionsDetected);
720 }
721}
722
723QTEST_MAIN(tst_QStyle)
724#include "tst_qstyle.moc"
725

source code of qtbase/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp