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#include <QtWidgets/QApplication>
30#include <QtWidgets/QCheckBox>
31#include <QtWidgets/QComboBox>
32#include <QtWidgets/QDateEdit>
33#include <QtWidgets/QDialog>
34#include <QtWidgets/QDialogButtonBox>
35#include <QtWidgets/QGridLayout>
36#include <QtWidgets/QGroupBox>
37#include <QtWidgets/QLabel>
38#include <QtWidgets/QHeaderView>
39#include <QtWidgets/QLineEdit>
40#include <QtWidgets/QMainWindow>
41#include <QtWidgets/QMenu>
42#include <QtWidgets/QPushButton>
43#include <QtWidgets/QProgressBar>
44#include <QtWidgets/QSpinBox>
45#include <QtWidgets/QSplitter>
46#include <QtWidgets/QStyle>
47#include <QtWidgets/QStyleFactory>
48#include <QtWidgets/QTableWidget>
49#include <QtWidgets/QToolButton>
50#include <QtWidgets/QToolTip>
51#include <QtWidgets/QTreeView>
52#include <QtWidgets/QVBoxLayout>
53
54#include <QtGui/QPainter>
55#include <QtGui/QScreen>
56
57#include <QtTest/QtTest>
58
59#include <QtCore/QDebug>
60#include <QtCore/QMetaObject>
61#include <QtCore/QScopedPointer>
62
63#include <private/qstylesheetstyle_p.h>
64#include <private/qhighdpiscaling_p.h>
65#include <QtTest/private/qtesthelpers_p.h>
66
67using namespace QTestPrivate;
68
69class tst_QStyleSheetStyle : public QObject
70{
71 Q_OBJECT
72public:
73 tst_QStyleSheetStyle();
74
75 static void initMain();
76
77private slots:
78 void init();
79 void cleanup();
80 void repolish();
81 void repolish_without_crashing();
82 void numinstances();
83 void widgetsBeforeAppStyleSheet();
84 void widgetsAfterAppStyleSheet();
85 void applicationStyleSheet();
86 void windowStyleSheet();
87 void widgetStyleSheet();
88 void reparentWithNoChildStyleSheet();
89 void reparentWithChildStyleSheet();
90 void dynamicProperty();
91 // NB! Invoking this slot after layoutSpacing crashes on Mac.
92 void namespaces();
93#ifdef Q_OS_MAC
94 void layoutSpacing();
95#endif
96 void qproperty();
97 void palettePropagation_data();
98 void palettePropagation();
99 void fontPropagation_data();
100 void fontPropagation();
101 void widgetStylePropagation_data();
102 void widgetStylePropagation();
103 void onWidgetDestroyed();
104 void fontPrecedence();
105 void focusColors();
106#ifndef QT_NO_CURSOR
107 void hoverColors();
108#endif
109 void background();
110 void tabAlignment();
111 void attributesList();
112 void minmaxSizes();
113 void task206238_twice();
114 void transparent();
115 void proxyStyle();
116 void dialogButtonBox();
117 void emptyStyleSheet();
118 void toolTip();
119 void embeddedFonts();
120 void opaquePaintEvent_data();
121 void opaquePaintEvent();
122 void complexWidgetFocus();
123 void task188195_baseBackground();
124 void task232085_spinBoxLineEditBg();
125 void changeStyleInChangeEvent();
126 void QTBUG15910_crashNullWidget();
127 void QTBUG36933_brokenPseudoClassLookup();
128 void styleSheetChangeBeforePolish();
129 void placeholderColor();
130 void enumPropertySelector_data();
131 void enumPropertySelector();
132 //at the end because it mess with the style.
133 void widgetStyle();
134 void appStyle();
135 void QTBUG11658_cachecrash();
136 void styleSheetTargetAttribute();
137 void unpolish();
138
139 void highdpiImages_data();
140 void highdpiImages();
141
142private:
143 static QColor COLOR(const QWidget &w)
144 {
145 w.ensurePolished();
146 return w.palette().color(cr: w.foregroundRole());
147 }
148
149 static QColor APPCOLOR(const QWidget &w)
150 {
151 w.ensurePolished();
152 return QApplication::palette(&w).color(cr: w.foregroundRole());
153 }
154
155 static QColor BACKGROUND(const QWidget &w)
156 {
157 w.ensurePolished();
158 return w.palette().color(cr: w.backgroundRole());
159 }
160
161 static QColor APPBACKGROUND(const QWidget &w)
162 {
163 w.ensurePolished();
164 return QApplication::palette(&w).color(cr: w.backgroundRole());
165 }
166
167 static int FONTSIZE(const QWidget &w)
168 {
169 w.ensurePolished();
170 return w.font().pointSize();
171 }
172
173 static int APPFONTSIZE(const QWidget &w) { return QApplication::font(&w).pointSize(); }
174
175 const QRect m_availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
176 QSize m_testSize;
177};
178
179// highdpiImages() tests HighDPI scaling; disable initially.
180void tst_QStyleSheetStyle::initMain()
181{
182 QCoreApplication::setAttribute(attribute: Qt::AA_DisableHighDpiScaling);
183}
184
185tst_QStyleSheetStyle::tst_QStyleSheetStyle()
186{
187 const int testSize = qMax(a: 200, b: m_availableGeometry.width() / 10);
188 m_testSize.setWidth(testSize);
189 m_testSize.setHeight(testSize);
190}
191
192void tst_QStyleSheetStyle::init()
193{
194 qApp->setStyleSheet(QString());
195 QCoreApplication::setAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles, on: false);
196}
197
198void tst_QStyleSheetStyle::cleanup()
199{
200 QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty());
201}
202
203void tst_QStyleSheetStyle::numinstances()
204{
205 QWidget w;
206 w.setWindowTitle(QTest::currentTestFunction());
207 w.resize(m_testSize);
208 centerOnScreen(w: &w);
209 QCommonStyle *style = new QCommonStyle;
210 style->setParent(&w);
211 QWidget c(&w);
212 w.show();
213
214 // set and unset application stylesheet
215 QCOMPARE(QStyleSheetStyle::numinstances, 0);
216 qApp->setStyleSheet("* { color: red; }");
217 QCOMPARE(QStyleSheetStyle::numinstances, 1);
218 qApp->setStyleSheet(QString());
219 QCOMPARE(QStyleSheetStyle::numinstances, 0);
220
221 // set and unset application stylesheet+widget
222 qApp->setStyleSheet("* { color: red; }");
223 w.setStyleSheet("color: red;");
224 QCOMPARE(QStyleSheetStyle::numinstances, 2);
225 w.setStyle(style);
226 QCOMPARE(QStyleSheetStyle::numinstances, 2);
227 qApp->setStyleSheet(QString());
228 QCOMPARE(QStyleSheetStyle::numinstances, 1);
229 w.setStyleSheet(QString());
230 QCOMPARE(QStyleSheetStyle::numinstances, 0);
231
232 // set and unset widget stylesheet
233 w.setStyle(nullptr);
234 w.setStyleSheet("color: red");
235 QCOMPARE(QStyleSheetStyle::numinstances, 1);
236 c.setStyle(style);
237 QCOMPARE(QStyleSheetStyle::numinstances, 2);
238 w.setStyleSheet(QString());
239 QCOMPARE(QStyleSheetStyle::numinstances, 0);
240}
241
242void tst_QStyleSheetStyle::widgetsBeforeAppStyleSheet()
243{
244 QPushButton w1; // widget with no stylesheet
245 const QColor red(Qt::red);
246 const QColor white(Qt::white);
247 qApp->setStyleSheet("* { color: red; }");
248 QCOMPARE(COLOR(w1), red);
249 w1.setStyleSheet("color: white");
250 QCOMPARE(COLOR(w1), white);
251 qApp->setStyleSheet(QString());
252 QCOMPARE(COLOR(w1), white);
253 w1.setStyleSheet(QString());
254 QCOMPARE(COLOR(w1), APPCOLOR(w1));
255}
256
257class FriendlySpinBox : public QSpinBox { friend class tst_QStyleSheetStyle; };
258
259void tst_QStyleSheetStyle::widgetsAfterAppStyleSheet()
260{
261 const QColor red(Qt::red);
262 const QColor white(Qt::white);
263 qApp->setStyleSheet("* { color: red; font-size: 32pt; }");
264 QPushButton w1;
265 FriendlySpinBox spin;
266 QCOMPARE(COLOR(w1), red);
267 QCOMPARE(COLOR(spin), red);
268 QCOMPARE(COLOR(*spin.lineEdit()), red);
269 QCOMPARE(FONTSIZE(w1), 32);
270 QCOMPARE(FONTSIZE(spin), 32);
271 QCOMPARE(FONTSIZE(*spin.lineEdit()), 32);
272 w1.setStyleSheet("color: white");
273 QCOMPARE(COLOR(w1), white);
274 QCOMPARE(COLOR(spin), red);
275 QCOMPARE(COLOR(*spin.lineEdit()), red);
276 w1.setStyleSheet(QString());
277 QCOMPARE(COLOR(w1), red);
278 QCOMPARE(COLOR(spin), red);
279 QCOMPARE(COLOR(*spin.lineEdit()), red);
280 w1.setStyleSheet("color: white");
281 QCOMPARE(COLOR(w1), white);
282 qApp->setStyleSheet(QString());
283 QCOMPARE(COLOR(w1), white);
284 QCOMPARE(COLOR(spin), APPCOLOR(spin));
285 QCOMPARE(COLOR(*spin.lineEdit()), APPCOLOR(*spin.lineEdit()));
286 w1.setStyleSheet(QString());
287 QCOMPARE(COLOR(w1), APPCOLOR(w1));
288 // QCOMPARE(FONTSIZE(w1), APPFONTSIZE(w1)); //### task 244261
289 QCOMPARE(FONTSIZE(spin), APPFONTSIZE(spin));
290 //QCOMPARE(FONTSIZE(*spin.lineEdit()), APPFONTSIZE(*spin.lineEdit())); //### task 244261
291}
292
293void tst_QStyleSheetStyle::applicationStyleSheet()
294{
295 const QColor red(Qt::red);
296 const QColor white(Qt::white);
297 QPushButton w1;
298 qApp->setStyleSheet("* { color: red; }");
299 QCOMPARE(COLOR(w1), red);
300 qApp->setStyleSheet("* { color: white; }");
301 QCOMPARE(COLOR(w1), white);
302 qApp->setStyleSheet(QString());
303 QCOMPARE(COLOR(w1), APPCOLOR(w1));
304 qApp->setStyleSheet("* { color: red }");
305 QCOMPARE(COLOR(w1), red);
306}
307
308void tst_QStyleSheetStyle::windowStyleSheet()
309{
310 const QColor red(Qt::red);
311 const QColor white(Qt::white);
312 QPushButton w1;
313 qApp->setStyleSheet(QString());
314 w1.setStyleSheet("* { color: red; }");
315 QCOMPARE(COLOR(w1), red);
316 w1.setStyleSheet("* { color: white; }");
317 QCOMPARE(COLOR(w1), white);
318 w1.setStyleSheet(QString());
319 QCOMPARE(COLOR(w1), APPCOLOR(w1));
320 w1.setStyleSheet("* { color: red }");
321 QCOMPARE(COLOR(w1), red);
322
323 qApp->setStyleSheet("* { color: green }");
324 QCOMPARE(COLOR(w1), red);
325 w1.setStyleSheet(QString());
326 QCOMPARE(COLOR(w1), QColor("green"));
327 qApp->setStyleSheet(QString());
328 QCOMPARE(COLOR(w1), APPCOLOR(w1));
329}
330
331void tst_QStyleSheetStyle::widgetStyleSheet()
332{
333 const QColor blue(Qt::blue);
334 const QColor red(Qt::red);
335 const QColor white(Qt::white);
336 QPushButton w1;
337 QPushButton *pb = new QPushButton(&w1);
338 QPushButton &w2 = *pb;
339
340 qApp->setStyleSheet(QString());
341 w1.setStyleSheet("* { color: red }");
342 QCOMPARE(COLOR(w1), red);
343 QCOMPARE(COLOR(w2), red);
344
345 w2.setStyleSheet("* { color: white }");
346 QCOMPARE(COLOR(w2), white);
347
348 w1.setStyleSheet("* { color: blue }");
349 QCOMPARE(COLOR(w1), blue);
350 QCOMPARE(COLOR(w2), white);
351
352 w1.setStyleSheet(QString());
353 QCOMPARE(COLOR(w1), APPCOLOR(w1));
354 QCOMPARE(COLOR(w2), white);
355
356 w2.setStyleSheet(QString());
357 QCOMPARE(COLOR(w1), APPCOLOR(w1));
358 QCOMPARE(COLOR(w2), APPCOLOR(w2));
359}
360
361void tst_QStyleSheetStyle::reparentWithNoChildStyleSheet()
362{
363 const QColor blue(Qt::blue);
364 const QColor red(Qt::red);
365 const QColor white(Qt::white);
366 QPushButton p1, p2;
367 QPushButton *pb = new QPushButton(&p1);
368 QPushButton &c1 = *pb; // child with no stylesheet
369
370 qApp->setStyleSheet(QString());
371 p1.setStyleSheet("* { color: red }");
372 QCOMPARE(COLOR(c1), red);
373 c1.setParent(&p2);
374 QCOMPARE(COLOR(c1), APPCOLOR(c1));
375
376 p2.setStyleSheet("* { color: white }");
377 QCOMPARE(COLOR(c1), white);
378
379 c1.setParent(&p1);
380 QCOMPARE(COLOR(c1), red);
381
382 qApp->setStyleSheet("* { color: blue }");
383 c1.setParent(nullptr);
384 QCOMPARE(COLOR(c1), blue);
385 delete pb;
386}
387
388void tst_QStyleSheetStyle::reparentWithChildStyleSheet()
389{
390 const QColor gray("gray");
391 const QColor white(Qt::white);
392 qApp->setStyleSheet(QString());
393 QPushButton p1, p2;
394 QPushButton *pb = new QPushButton(&p1);
395 QPushButton &c1 = *pb;
396
397 c1.setStyleSheet("background: gray");
398 QCOMPARE(BACKGROUND(c1), gray);
399 c1.setParent(&p2);
400 QCOMPARE(BACKGROUND(c1), gray);
401
402 qApp->setStyleSheet("* { color: white }");
403 c1.setParent(&p1);
404 QCOMPARE(BACKGROUND(c1), gray);
405 QCOMPARE(COLOR(c1), white);
406}
407
408void tst_QStyleSheetStyle::repolish()
409{
410 const QColor red(Qt::red);
411 const QColor white(Qt::white);
412 qApp->setStyleSheet(QString());
413 QPushButton p1;
414 p1.setStyleSheet("color: red; background: white");
415 QCOMPARE(BACKGROUND(p1), white);
416 p1.setStyleSheet("background: white");
417 QCOMPARE(COLOR(p1), APPCOLOR(p1));
418 p1.setStyleSheet("color: red");
419 QCOMPARE(COLOR(p1), red);
420 QCOMPARE(BACKGROUND(p1), APPBACKGROUND(p1));
421 p1.setStyleSheet(QString());
422 QCOMPARE(COLOR(p1), APPCOLOR(p1));
423 QCOMPARE(BACKGROUND(p1), APPBACKGROUND(p1));
424}
425
426void tst_QStyleSheetStyle::repolish_without_crashing()
427{
428 // This used to crash, QTBUG-69204
429 QMainWindow w;
430 w.resize(m_testSize);
431 w.setWindowTitle(QTest::currentTestFunction());
432 QScopedPointer<QSplitter> splitter1(new QSplitter(w.centralWidget()));
433 QScopedPointer<QSplitter> splitter2(new QSplitter);
434 QScopedPointer<QSplitter> splitter3(new QSplitter);
435 splitter2->addWidget(widget: splitter3.data());
436
437 splitter2->setStyleSheet("color: red");
438 QScopedPointer<QLabel> label(new QLabel);
439 label->setTextFormat(Qt::RichText);
440 splitter3->addWidget(widget: label.data());
441 label->setText("hey");
442
443 splitter1->addWidget(widget: splitter2.data());
444 w.show();
445 QCOMPARE(COLOR(*label), QColor(Qt::red));
446}
447
448void tst_QStyleSheetStyle::widgetStyle()
449{
450 qApp->setStyleSheet(QString());
451
452 QWidget *window1 = new QWidget;
453 window1->setObjectName("window1");
454 QWidget *widget1 = new QWidget(window1);
455 widget1->setObjectName("widget1");
456 QWidget *widget2 = new QWidget;
457 widget2->setObjectName("widget2");
458 QWidget *window2 = new QWidget;
459 window2->setObjectName("window2");
460 window1->ensurePolished();
461 window2->ensurePolished();
462 widget1->ensurePolished();
463 widget2->ensurePolished();
464
465 QPointer<QStyle> style1 = QStyleFactory::create("Windows");
466 QPointer<QStyle> style2 = QStyleFactory::create("Windows");
467
468 QStyle *appStyle = QApplication::style();
469
470 // Sanity: By default, a window inherits the application style
471 QCOMPARE(appStyle, window1->style());
472
473 // Setting a custom style on a widget
474 window1->setStyle(style1);
475 QCOMPARE(style1.data(), window1->style());
476
477 // Setting another style must not delete the older style
478 window1->setStyle(style2);
479 QCOMPARE(style2.data(), window1->style());
480 QVERIFY(!style1.isNull()); // case we have not already crashed
481
482 // Setting null style must make it follow the qApp style
483 window1->setStyle(nullptr);
484 QCOMPARE(window1->style(), appStyle);
485 QVERIFY(!style2.isNull()); // case we have not already crashed
486 QVERIFY(!style2.isNull()); // case we have not already crashed
487
488 // Sanity: Set the stylesheet
489 window1->setStyleSheet(":x { }");
490
491 QPointer<QStyleSheetStyle> proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
492 QVERIFY(!proxy.isNull());
493 QCOMPARE(proxy->base, nullptr); // and follows the application
494
495 // Set the stylesheet
496 window1->setStyle(style1);
497 QVERIFY(proxy.isNull()); // we create a new one each time
498 proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
499 QVERIFY(!proxy.isNull()); // it is a proxy
500 QCOMPARE(proxy->baseStyle(), style1.data()); // must have been replaced with the new one
501
502 // Update the stylesheet and check nothing changes
503 window1->setStyleSheet(":y { }");
504 QCOMPARE(window1->style()->metaObject()->className(), "QStyleSheetStyle"); // it is a proxy
505 QCOMPARE(proxy->baseStyle(), style1.data()); // the same guy
506
507 // Remove the stylesheet
508 proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
509 window1->setStyleSheet(QString());
510 QVERIFY(proxy.isNull()); // should have disappeared
511 QCOMPARE(window1->style(), style1.data()); // its restored
512
513 // Style Sheet existing children propagation
514 window1->setStyleSheet(":z { }");
515 proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
516 QVERIFY(!proxy.isNull()); // it is a proxy
517 QCOMPARE(window1->style(), widget1->style()); // proxy must have propagated
518 QCOMPARE(widget2->style(), appStyle); // widget2 is following the app style
519
520 // Style Sheet automatic propagation to new children
521 widget2->setParent(window1); // reparent in!
522 QCOMPARE(window1->style(), widget2->style()); // proxy must have propagated
523
524 // Style Sheet automatic removal from children who abandoned their parents
525 window2->setStyle(style2);
526 widget2->setParent(window2); // reparent
527 QCOMPARE(widget2->style(), appStyle); // widget2 is following the app style
528
529 // Style Sheet propagation on a child widget with a custom style
530 widget2->setStyle(style1);
531 window2->setStyleSheet(":x { }");
532 proxy = qobject_cast<QStyleSheetStyle *>(object: widget2->style());
533 QVERIFY(!proxy.isNull()); // it is a proxy
534 QCOMPARE(proxy->baseStyle(), style1.data());
535
536 // Style Sheet propagation on a child widget with a custom style already set
537 window2->setStyleSheet(QString());
538 QCOMPARE(window2->style(), style2.data());
539 QCOMPARE(widget2->style(), style1.data());
540 widget2->setStyle(nullptr);
541 window2->setStyleSheet(":x { }");
542 widget2->setStyle(style1);
543 proxy = qobject_cast<QStyleSheetStyle *>(object: widget2->style());
544 QVERIFY(!proxy.isNull()); // it is a proxy
545
546 // QApplication, QWidget both having a style sheet
547
548 // clean everything out
549 window1->setStyle(nullptr);
550 window1->setStyleSheet(QString());
551 window2->setStyle(nullptr);
552 window2->setStyleSheet(QString());
553 QApplication::setStyle(nullptr);
554
555 qApp->setStyleSheet("may_insanity_prevail { }"); // app has stylesheet
556 QCOMPARE(window1->style(), QApplication::style());
557 QCOMPARE(window1->style()->metaObject()->className(), "QStyleSheetStyle");
558 QCOMPARE(widget1->style()->metaObject()->className(), "QStyleSheetStyle"); // check the child
559 window1->setStyleSheet("may_more_insanity_prevail { }"); // window has stylesheet
560 QCOMPARE(window1->style()->metaObject()->className(), "QStyleSheetStyle"); // a new one
561 QCOMPARE(widget1->style(), window1->style()); // child follows...
562 proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
563 QVERIFY(!proxy.isNull());
564 QStyle *newStyle = QStyleFactory::create("Windows");
565 QApplication::setStyle(newStyle); // set a custom style on app
566 proxy = qobject_cast<QStyleSheetStyle *>(object: window1->style());
567 QVERIFY(!proxy.isNull()); // it is a proxy
568 QCOMPARE(proxy->baseStyle(), newStyle); // magic ;) the widget still follows the application
569 QCOMPARE(static_cast<QStyle *>(proxy), widget1->style()); // child still follows...
570
571 window1->setStyleSheet(QString()); // remove stylesheet
572 QCOMPARE(window1->style(), QApplication::style()); // is this cool or what
573 QCOMPARE(widget1->style(), QApplication::style()); // annoying child follows...
574 QScopedPointer<QStyle> wndStyle(QStyleFactory::create("Windows"));
575 window1->setStyle(wndStyle.data());
576 QCOMPARE(window1->style()->metaObject()->className(), "QStyleSheetStyle"); // auto wraps it
577 QCOMPARE(widget1->style(), window1->style()); // and auto propagates to child
578 qApp->setStyleSheet(QString()); // remove the app stylesheet
579 QCOMPARE(window1->style(), wndStyle.data()); // auto dewrap
580 QCOMPARE(widget1->style(), QApplication::style()); // and child state is restored
581 window1->setStyle(nullptr); // let sanity prevail
582 QApplication::setStyle(nullptr);
583
584 delete window1;
585 delete widget2;
586 delete window2;
587 delete style1;
588 delete style2;
589}
590
591void tst_QStyleSheetStyle::appStyle()
592{
593 qApp->setStyleSheet(QString());
594 // qApp style can never be 0
595 QVERIFY(QApplication::style() != nullptr);
596 QPointer<QStyle> style1 = QStyleFactory::create("Windows");
597 QPointer<QStyle> style2 = QStyleFactory::create("Windows");
598 QApplication::setStyle(style1);
599 // Basic sanity
600 QCOMPARE(QApplication::style(), style1.data());
601 QApplication::setStyle(style2);
602 QVERIFY(style1.isNull()); // qApp must have taken ownership and deleted it
603 // Setting null should not crash
604 QApplication::setStyle(nullptr);
605 QCOMPARE(QApplication::style(), style2.data());
606
607 // Set the stylesheet
608 qApp->setStyleSheet("whatever");
609 QPointer<QStyleSheetStyle> sss = static_cast<QStyleSheetStyle *>(QApplication::style());
610 QVERIFY(!sss.isNull());
611 QCOMPARE(sss->metaObject()->className(), "QStyleSheetStyle"); // must be our proxy now
612 QVERIFY(!style2.isNull()); // this should exist as it is the base of the proxy
613 QCOMPARE(sss->baseStyle(), style2.data());
614 style1 = QStyleFactory::create("Windows");
615 QApplication::setStyle(style1);
616 QVERIFY(style2.isNull()); // should disappear automatically
617 QVERIFY(sss.isNull()); // should disappear automatically
618
619 // Update the stylesheet and check nothing changes
620 sss = static_cast<QStyleSheetStyle *>(QApplication::style());
621 qApp->setStyleSheet("whatever2");
622 QCOMPARE(QApplication::style(), sss.data());
623 QCOMPARE(sss->baseStyle(), style1.data());
624
625 // Revert the stylesheet
626 qApp->setStyleSheet(QString());
627 QVERIFY(sss.isNull()); // should have disappeared
628 QCOMPARE(QApplication::style(), style1.data());
629
630 qApp->setStyleSheet(QString());
631 QCOMPARE(QApplication::style(), style1.data());
632}
633
634void tst_QStyleSheetStyle::dynamicProperty()
635{
636 qApp->setStyleSheet(QString());
637
638 QString appStyle = QApplication::style()->metaObject()->className();
639 QPushButton pb1(QStringLiteral("dynamicProperty_pb1"));
640 pb1.setMinimumWidth(m_testSize.width());
641 pb1.move(m_availableGeometry.topLeft() + QPoint(20, 100));
642
643 QPushButton pb2(QStringLiteral("dynamicProperty_pb2"));
644 pb2.setWindowTitle(QTest::currentTestFunction());
645 pb2.setMinimumWidth(m_testSize.width());
646 pb2.move(m_availableGeometry.topLeft() + QPoint(20, m_testSize.width() + 40));
647
648 pb1.setProperty(name: "type", value: "critical");
649 qApp->setStyleSheet("*[class~=\"QPushButton\"] { color: red; } *[type=\"critical\"] { background: white; }");
650 QVERIFY(COLOR(pb1) == Qt::red);
651 QVERIFY(BACKGROUND(pb1) == Qt::white);
652
653 pb2.setProperty(name: "class", value: "critical"); // dynamic class
654 pb2.setStyleSheet(QLatin1String(".critical[style~=\"") + appStyle + "\"] { color: blue }");
655 pb2.show();
656
657 QVERIFY(COLOR(pb2) == Qt::blue);
658}
659
660#ifdef Q_OS_MAC
661void tst_QStyleSheetStyle::layoutSpacing()
662{
663 qApp->setStyleSheet("* { color: red }");
664 QCheckBox ck1;
665 QCheckBox ck2;
666 QWidget window;
667 int spacing_widgetstyle = window.style()->layoutSpacing(ck1.sizePolicy().controlType(), ck2.sizePolicy().controlType(), Qt::Vertical);
668 int spacing_style = window.style()->layoutSpacing(ck1.sizePolicy().controlType(), ck2.sizePolicy().controlType(), Qt::Vertical);
669 QCOMPARE(spacing_widgetstyle, spacing_style);
670}
671#endif
672
673void tst_QStyleSheetStyle::qproperty()
674{
675 QPushButton pb;
676 pb.setStyleSheet("QPushButton { qproperty-text: hello; qproperty-checkable: 1; qproperty-checked: false}");
677 pb.ensurePolished();
678 QCOMPARE(pb.text(), QString("hello"));
679 QCOMPARE(pb.isCheckable(), true);
680 QCOMPARE(pb.isChecked(), false);
681}
682
683namespace ns {
684 class PushButton1 : public QPushButton {
685 Q_OBJECT
686 public:
687 using QPushButton::QPushButton;
688 };
689 class PushButton2 : public PushButton1 {
690 Q_OBJECT
691 public:
692 PushButton2() { setProperty(name: "class", value: "PushButtonTwo PushButtonDuo"); }
693 };
694}
695
696void tst_QStyleSheetStyle::namespaces()
697{
698 const QColor blue(Qt::blue);
699 const QColor red(Qt::red);
700 const QColor white(Qt::white);
701 ns::PushButton1 pb1;
702 qApp->setStyleSheet("ns--PushButton1 { background: white }");
703 QCOMPARE(BACKGROUND(pb1), white);
704 qApp->setStyleSheet(".ns--PushButton1 { background: red }");
705 QCOMPARE(BACKGROUND(pb1), red);
706
707 ns::PushButton2 pb2;
708 qApp->setStyleSheet("ns--PushButton1 { background: blue}");
709 QCOMPARE(BACKGROUND(pb2), blue);
710 qApp->setStyleSheet("ns--PushButton2 { background: magenta }");
711 QCOMPARE(BACKGROUND(pb2), QColor(Qt::magenta));
712 qApp->setStyleSheet(".PushButtonTwo { background: white; }");
713 QCOMPARE(BACKGROUND(pb2), white);
714 qApp->setStyleSheet(".PushButtonDuo { background: red; }");
715 QCOMPARE(BACKGROUND(pb2), red);
716}
717
718void tst_QStyleSheetStyle::palettePropagation_data()
719{
720 QTest::addColumn<QString>(name: "applicationStyleSheet");
721 QTest::addColumn<bool>(name: "widgetStylePropagation");
722 QTest::newRow(dataTag: "Widget style propagation") << " " << true;
723 QTest::newRow(dataTag: "Widget style propagation, no application style sheet") << QString() << true;
724 QTest::newRow(dataTag: "Default propagation") << " " << false;
725 QTest::newRow(dataTag: "Default propagation, no application style sheet") << QString() << false;
726}
727
728void tst_QStyleSheetStyle::palettePropagation()
729{
730 QFETCH(QString, applicationStyleSheet);
731 QFETCH(bool, widgetStylePropagation);
732
733 qApp->setStyleSheet(applicationStyleSheet);
734 QCoreApplication::setAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles, on: widgetStylePropagation);
735
736 QGroupBox gb;
737 QLabel *label = new QLabel(&gb);
738 QLabel &lb = *label;
739 label->setText("AsdF");
740
741 gb.setStyleSheet("QGroupBox { color: red }");
742 QCOMPARE(COLOR(gb), QColor(Qt::red));
743
744 if (widgetStylePropagation) {
745 QCOMPARE(COLOR(lb), QColor(Qt::red)); // palette should propagate in standard mode
746 } else {
747 QCOMPARE(COLOR(lb), APPCOLOR(lb)); // palette shouldn't propagate
748 }
749
750 QWidget window;
751 lb.setParent(&window);
752 if (widgetStylePropagation) {
753 // In standard propagation mode, widgets that are not explicitly
754 // targeted do not have their propagated palette unset when they are
755 // unpolished by changing parents. This is consistent with regular Qt
756 // widgets, who also maintain their propagated palette when changing
757 // parents
758 QCOMPARE(COLOR(lb), QColor(Qt::red));
759 } else {
760 QCOMPARE(COLOR(lb), APPCOLOR(lb));
761 }
762 lb.setParent(&gb);
763
764 gb.setStyleSheet("QGroupBox * { color: red }");
765
766 QCOMPARE(COLOR(lb), QColor(Qt::red));
767 QCOMPARE(COLOR(gb), APPCOLOR(gb));
768
769 window.setStyleSheet("* { color: white; }");
770 lb.setParent(&window);
771 QCOMPARE(COLOR(lb), QColor(Qt::white));
772}
773
774void tst_QStyleSheetStyle::fontPropagation_data()
775{
776 QTest::addColumn<QString>(name: "applicationStyleSheet");
777 QTest::addColumn<bool>(name: "widgetStylePropagation");
778 QTest::newRow(dataTag: "Widget style propagation") << " " << true;
779 QTest::newRow(dataTag: "Widget style propagation, no application style sheet") << QString() << true;
780 QTest::newRow(dataTag: "Default propagation") << " " << false;
781 QTest::newRow(dataTag: "Default propagation, no application style sheet") << QString() << false;
782}
783
784void tst_QStyleSheetStyle::fontPropagation()
785{
786 QFETCH(QString, applicationStyleSheet);
787 QFETCH(bool, widgetStylePropagation);
788
789 qApp->setStyleSheet(applicationStyleSheet);
790 QCoreApplication::setAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles, on: widgetStylePropagation);
791
792 QComboBox cb;
793 cb.addItem(atext: "item1");
794 cb.addItem(atext: "item2");
795
796 QAbstractItemView *popup = cb.view();
797 int viewFontSize = FONTSIZE(w: *popup);
798
799 cb.setStyleSheet("QComboBox { font-size: 20pt; }");
800 QCOMPARE(FONTSIZE(cb), 20);
801 if (widgetStylePropagation) {
802 QCOMPARE(FONTSIZE(*popup), 20);
803 } else {
804 QCOMPARE(FONTSIZE(*popup), viewFontSize);
805 }
806 QGroupBox gb;
807 QPushButton *push = new QPushButton(&gb);
808 QPushButton &pb = *push;
809 int buttonFontSize = FONTSIZE(w: pb);
810 int gbFontSize = FONTSIZE(w: gb);
811
812 gb.setStyleSheet("QGroupBox { font-size: 20pt }");
813 QCOMPARE(FONTSIZE(gb), 20);
814 if (widgetStylePropagation) {
815 QCOMPARE(FONTSIZE(pb), 20);
816 } else {
817 QCOMPARE(FONTSIZE(pb), buttonFontSize); // font does not propagate
818 }
819 gb.setStyleSheet("QGroupBox * { font-size: 20pt; }");
820 QCOMPARE(FONTSIZE(gb), gbFontSize);
821 QCOMPARE(FONTSIZE(pb), 20);
822
823 QWidget window;
824 window.setStyleSheet("* { font-size: 9pt }");
825 pb.setParent(&window);
826 QCOMPARE(FONTSIZE(pb), 9);
827 window.setStyleSheet(QString());
828 QCOMPARE(FONTSIZE(pb), buttonFontSize);
829
830 QTabWidget tw;
831 tw.setStyleSheet("QTabWidget { font-size: 20pt; }");
832 QCOMPARE(FONTSIZE(tw), 20);
833 QWidget *child = tw.findChild<QWidget *>(aName: "qt_tabwidget_tabbar");
834 QVERIFY2(child, "QTabWidget did not contain a widget named \"qt_tabwidget_tabbar\"");
835 QCOMPARE(FONTSIZE(*child), 20);
836}
837
838void tst_QStyleSheetStyle::onWidgetDestroyed()
839{
840 qApp->setStyleSheet(QString());
841 QLabel *l = new QLabel;
842 l->setStyleSheet("QLabel { color: red }");
843 QPointer<QStyleSheetStyle> ss = static_cast<QStyleSheetStyle *>(l->style());
844 delete l;
845 QVERIFY(ss.isNull());
846}
847
848void tst_QStyleSheetStyle::fontPrecedence()
849{
850 QLineEdit edit;
851 edit.setWindowTitle(QTest::currentTestFunction());
852 edit.setMinimumWidth(m_testSize.width());
853 centerOnScreen(w: &edit);
854 edit.show();
855 QFont font;
856 QVERIFY(FONTSIZE(edit) != 22); // Sanity check to make sure this test makes sense.
857 edit.setStyleSheet("QLineEdit { font-size: 22pt; }");
858 QCOMPARE(FONTSIZE(edit), 22);
859 font.setPointSize(16);
860 edit.setFont(font);
861 QCOMPARE(FONTSIZE(edit), 22);
862 edit.setStyleSheet(QString());
863 QCOMPARE(FONTSIZE(edit), 16);
864 font.setPointSize(18);
865 edit.setFont(font);
866 QCOMPARE(FONTSIZE(edit), 18);
867 edit.setStyleSheet("QLineEdit { font-size: 20pt; }");
868 QCOMPARE(FONTSIZE(edit), 20);
869 edit.setStyleSheet(QString());
870 QCOMPARE(FONTSIZE(edit), 18);
871 edit.hide();
872
873 QLineEdit edit2;
874 edit2.setReadOnly(true);
875 edit2.setStyleSheet("QLineEdit { font-size: 24pt; } QLineEdit:read-only { font-size: 26pt; }");
876 QCOMPARE(FONTSIZE(edit2), 26);
877}
878
879// Ensure primary will only return true if the color covers more than 50% of pixels
880static bool testForColors(const QImage& image, const QColor &color, bool ensurePrimary = false)
881{
882 int count = 0;
883 QRgb rgb = color.rgba();
884 int totalCount = image.height() * image.width();
885 for (int y = 0; y < image.height(); ++y) {
886 for (int x = 0; x < image.width(); ++x) {
887 // Because of antialiasing we allow a certain range of errors here.
888 QRgb pixel = image.pixel(x, y);
889
890 if (qAbs(t: int(pixel & 0xff) - int(rgb & 0xff)) +
891 qAbs(t: int((pixel & 0xff00) >> 8) - int((rgb & 0xff00) >> 8)) +
892 qAbs(t: int((pixel & 0xff0000) >> 16) - int((rgb & 0xff0000) >> 16)) <= 50) {
893 count++;
894 if (!ensurePrimary && count >=10 )
895 return true;
896 if (count > totalCount / 2)
897 return true;
898 }
899 }
900 }
901
902 return false;
903}
904
905class TestDialog : public QDialog
906{
907public:
908 explicit TestDialog(const QString &styleSheet);
909
910 QWidgetList widgets() const { return m_widgets; }
911 QLineEdit *focusDummy() const { return m_focusDummy; }
912
913private:
914 void addWidget(QWidget *w)
915 {
916 w->setStyleSheet(m_styleSheet);
917 m_layout->addWidget(w);
918 m_widgets.append(t: w);
919 }
920
921 const QString m_styleSheet;
922 QVBoxLayout* m_layout;
923 QLineEdit *m_focusDummy;
924 QWidgetList m_widgets;
925};
926
927TestDialog::TestDialog(const QString &styleSheet) :
928 m_styleSheet(styleSheet),
929 m_layout(new QVBoxLayout(this)),
930 m_focusDummy(new QLineEdit)
931{
932 m_layout->addWidget(m_focusDummy); // Avoids initial focus.
933 addWidget(w: new QPushButton("TESTING TESTING"));
934 addWidget(w: new QLineEdit("TESTING TESTING"));
935 addWidget(w: new QLabel("TESTING TESTING"));
936 QSpinBox *spinbox = new QSpinBox;
937 spinbox->setMaximum(1000000000);
938 spinbox->setValue(123456789);
939 addWidget(w: spinbox);
940 QComboBox *combobox = new QComboBox;
941 combobox->setEditable(true);
942 combobox->addItems(texts: QStringList{"TESTING TESTING"});
943 addWidget(w: combobox);
944 addWidget(w: new QLabel("<b>TESTING TESTING</b>"));
945}
946
947void tst_QStyleSheetStyle::focusColors()
948{
949 // Tests if colors can be changed by altering the focus of the widget.
950 // To avoid messy pixel-by-pixel comparison, we assume that the goal
951 // is reached if at least ten pixels of the right color can be found in
952 // the image.
953 // For this reason, we use unusual and extremely ugly colors! :-)
954 // Note that in case of anti-aliased text, ensuring that we have at least
955 // ten pixels of the right color requires quite a many characters, as the
956 // majority of the pixels will have slightly different colors due to the
957 // anti-aliasing effect.
958#if !defined(Q_OS_WIN32) && !(defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(Q_CC_INTEL))
959 QSKIP("This is a fragile test which fails on many esoteric platforms because of focus problems"
960 " (for example, QTBUG-33959)."
961 "That doesn't mean that the feature doesn't work in practice.");
962#endif
963
964 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
965 QSKIP("Wayland: This fails. Figure out why.");
966
967 TestDialog frame(QStringLiteral("*:focus { border:none; background: #e8ff66; color: #ff0084 }"));
968 frame.setWindowTitle(QTest::currentTestFunction());
969
970 centerOnScreen(w: &frame);
971 frame.show();
972
973 QApplication::setActiveWindow(&frame);
974 QVERIFY(QTest::qWaitForWindowActive(&frame));
975
976 for (QWidget *widget : frame.widgets()) {
977 widget->setFocus();
978 QApplication::processEvents();
979
980 QImage image(widget->width(), widget->height(), QImage::Format_ARGB32);
981 widget->render(target: &image);
982 if (image.depth() < 24)
983 QSKIP("Test doesn't support color depth < 24");
984
985 QVERIFY2(testForColors(image, QColor(0xe8, 0xff, 0x66)),
986 (QString::fromLatin1(widget->metaObject()->className())
987 + " did not contain background color #e8ff66, using style "
988 + QString::fromLatin1(QApplication::style()->metaObject()->className()))
989 .toLocal8Bit().constData());
990 QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)),
991 (QString::fromLatin1(widget->metaObject()->className())
992 + " did not contain text color #ff0084, using style "
993 + QString::fromLatin1(QApplication::style()->metaObject()->className()))
994 .toLocal8Bit().constData());
995 }
996}
997
998#ifndef QT_NO_CURSOR
999void tst_QStyleSheetStyle::hoverColors()
1000{
1001#ifdef Q_OS_MACOS
1002 QSKIP("This test is fragile on Mac, most likely due to QTBUG-33959.");
1003#endif
1004 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1005 QSKIP("Wayland: This fails. Figure out why.");
1006
1007 TestDialog frame(QStringLiteral("*:hover { border:none; background: #e8ff66; color: #ff0084 }"));
1008 frame.setWindowTitle(QTest::currentTestFunction());
1009
1010 centerOnScreen(w: &frame);
1011 // Move the mouse cursor out of the way to suppress spontaneous QEvent::Enter
1012 // events interfering with QApplicationPrivate::dispatchEnterLeave().
1013 // Mouse events can then be sent directly to the QWindow instead of the
1014 // QWidget, triggering the enter/leave handling within the dialog window,
1015 // speeding up the test.
1016 QCursor::setPos(frame.geometry().topLeft() - QPoint(100, 0));
1017 frame.show();
1018
1019 QApplication::setActiveWindow(&frame);
1020 QVERIFY(QTest::qWaitForWindowActive(&frame));
1021
1022 QWindow *frameWindow = frame.windowHandle();
1023 QVERIFY(frameWindow);
1024
1025 const QPoint dummyPos = frame.focusDummy()->geometry().center();
1026 QTest::mouseMove(window: frameWindow, pos: dummyPos);
1027
1028 for (QWidget *widget : frame.widgets()) {
1029 //move the mouse inside the widget, it should be colored
1030 const QRect widgetGeometry = widget->geometry();
1031 QTest::mouseMove(window: frameWindow, pos: widgetGeometry.center());
1032 QTRY_VERIFY2(widget->testAttribute(Qt::WA_UnderMouse), widget->metaObject()->className());
1033
1034 QImage image(widgetGeometry.size(), QImage::Format_ARGB32);
1035 widget->render(target: &image);
1036
1037 QVERIFY2(testForColors(image, QColor(0xe8, 0xff, 0x66)),
1038 (QString::fromLatin1(widget->metaObject()->className())
1039 + " did not contain background color #e8ff66").toLocal8Bit().constData());
1040 QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)),
1041 (QString::fromLatin1(widget->metaObject()->className())
1042 + " did not contain text color #ff0084").toLocal8Bit().constData());
1043
1044 //move the mouse outside the widget, it should NOT be colored
1045 QTest::mouseMove(window: frameWindow, pos: dummyPos);
1046 QTRY_VERIFY2(frame.focusDummy()->testAttribute(Qt::WA_UnderMouse), "FocusDummy");
1047
1048 widget->render(target: &image);
1049
1050 QVERIFY2(!testForColors(image, QColor(0xe8, 0xff, 0x66)),
1051 (QString::fromLatin1(widget->metaObject()->className())
1052 + " did contain background color #e8ff66").toLocal8Bit().constData());
1053 QVERIFY2(!testForColors(image, QColor(0xff, 0x00, 0x84)),
1054 (QString::fromLatin1(widget->metaObject()->className())
1055 + " did contain text color #ff0084").toLocal8Bit().constData());
1056
1057 //move the mouse again inside the widget, it should be colored
1058 QTest::mouseMove (window: frameWindow, pos: widgetGeometry.center());
1059 QTRY_VERIFY2(widget->testAttribute(Qt::WA_UnderMouse), widget->metaObject()->className());
1060
1061 widget->render(target: &image);
1062
1063 QVERIFY2(testForColors(image, QColor(0xe8, 0xff, 0x66)),
1064 (QString::fromLatin1(widget->metaObject()->className())
1065 + " did not contain background color #e8ff66").toLocal8Bit().constData());
1066 QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)),
1067 (QString::fromLatin1(widget->metaObject()->className())
1068 + " did not contain text color #ff0084").toLocal8Bit().constData());
1069 }
1070}
1071#endif
1072
1073class SingleInheritanceDialog : public QDialog
1074{
1075 Q_OBJECT
1076public:
1077 using QDialog::QDialog;
1078};
1079
1080class DoubleInheritanceDialog : public SingleInheritanceDialog
1081{
1082 Q_OBJECT
1083public:
1084 using SingleInheritanceDialog::SingleInheritanceDialog;
1085};
1086
1087void tst_QStyleSheetStyle::background()
1088{
1089 typedef QSharedPointer<QWidget> WidgetPtr;
1090
1091 const QString styleSheet = QStringLiteral("* { background-color: #e8ff66; }");
1092 QVector<WidgetPtr> widgets;
1093 const QPoint topLeft = m_availableGeometry.topLeft();
1094 // Testing inheritance styling of QDialog.
1095 WidgetPtr toplevel(new SingleInheritanceDialog);
1096 toplevel->resize(m_testSize);
1097 toplevel->move(topLeft + QPoint(20, 20));
1098 toplevel->setStyleSheet(styleSheet);
1099 widgets.append(t: toplevel);
1100
1101 toplevel = WidgetPtr(new DoubleInheritanceDialog);
1102 toplevel->resize(m_testSize);
1103 toplevel->move(topLeft + QPoint(20, m_testSize.height() + 120));
1104 toplevel->setStyleSheet(styleSheet);
1105 widgets.append(t: toplevel);
1106
1107 // Testing gradients in QComboBox.
1108 // First color
1109 toplevel = WidgetPtr(new QDialog);
1110 toplevel->resize(m_testSize);
1111 toplevel->move(topLeft + QPoint(m_testSize.width() + 120, 20));
1112 QGridLayout *layout = new QGridLayout(toplevel.data());
1113 QComboBox* cb = new QComboBox;
1114 cb->setMinimumWidth(160);
1115 cb->setStyleSheet("* { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop:0 #e8ff66, stop:1 #000000); }");
1116 layout->addWidget(cb, row: 0, column: 0);
1117 widgets.append(t: toplevel);
1118 // Second color
1119 toplevel = WidgetPtr(new QDialog);
1120 toplevel->resize(m_testSize);
1121 toplevel->move(topLeft + QPoint(m_testSize.width() + 120, m_testSize.height() + 120));
1122 layout = new QGridLayout(toplevel.data());
1123 cb = new QComboBox;
1124 cb->setMinimumWidth(160);
1125 cb->setStyleSheet("* { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop:0 #e8ff66, stop:1 #000000); }");
1126 layout->addWidget(cb, row: 0, column: 0);
1127 widgets.append(t: toplevel);
1128
1129 for (int c = 0; c < widgets.size(); ++c) {
1130 QWidget *widget = widgets.at(i: c).data();
1131 widget->setWindowTitle(QStringLiteral("background ") + QString::number(c));
1132 widget->show();
1133 QVERIFY(QTest::qWaitForWindowExposed(widget));
1134
1135 QImage image(widget->width(), widget->height(), QImage::Format_ARGB32);
1136 widget->render(target: &image);
1137 if (image.depth() < 24)
1138 QSKIP("Test doesn't support color depth < 24");
1139
1140 if (c == 2 && !QApplication::style()->objectName().compare(other: QLatin1String("fusion"), cs: Qt::CaseInsensitive))
1141 QEXPECT_FAIL("", "QTBUG-21468", Abort);
1142
1143 QVERIFY2(testForColors(image, QColor(0xe8, 0xff, 0x66)),
1144 (QString::number(c) + QLatin1Char(' ') + QString::fromLatin1(widget->metaObject()->className())
1145 + " did not contain background image with color #e8ff66")
1146 .toLocal8Bit().constData());
1147
1148 widget->hide();
1149 }
1150}
1151
1152void tst_QStyleSheetStyle::tabAlignment()
1153{
1154 QWidget topLevel;
1155 topLevel.setWindowTitle(QTest::currentTestFunction());
1156 QTabWidget tabWidget(&topLevel);
1157 tabWidget.addTab(widget: new QLabel("tab1"),"tab1");
1158 tabWidget.resize(QSize(400,400));
1159 centerOnScreen(w: &topLevel);
1160 topLevel.show();
1161 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1162 QTabBar *bar = tabWidget.findChild<QTabBar*>();
1163 QVERIFY(bar);
1164 //check the tab is on the right
1165 tabWidget.setStyleSheet("QTabWidget::tab-bar { alignment: right ; }");
1166 qApp->processEvents();
1167 QVERIFY(bar->geometry().right() > 380);
1168 QVERIFY(bar->geometry().left() > 200);
1169 //check the tab is on the middle
1170 tabWidget.setStyleSheet("QTabWidget::tab-bar { alignment: center ; }");
1171 QVERIFY(bar->geometry().right() < 300);
1172 QVERIFY(bar->geometry().left() > 100);
1173 //check the tab is on the left
1174 tabWidget.setStyleSheet("QTabWidget::tab-bar { alignment: left ; }");
1175 QVERIFY(bar->geometry().left() < 20);
1176 QVERIFY(bar->geometry().right() < 200);
1177
1178 tabWidget.setTabPosition(QTabWidget::West);
1179 //check the tab is on the top
1180 QVERIFY(bar->geometry().top() < 20);
1181 QVERIFY(bar->geometry().bottom() < 200);
1182 //check the tab is on the bottom
1183 tabWidget.setStyleSheet("QTabWidget::tab-bar { alignment: right ; }");
1184 QVERIFY(bar->geometry().bottom() > 380);
1185 QVERIFY(bar->geometry().top() > 200);
1186 //check the tab is on the middle
1187 tabWidget.setStyleSheet("QTabWidget::tab-bar { alignment: center ; }");
1188 QVERIFY(bar->geometry().bottom() < 300);
1189 QVERIFY(bar->geometry().top() > 100);
1190}
1191
1192void tst_QStyleSheetStyle::attributesList()
1193{
1194 const QColor blue(Qt::blue);
1195 const QColor red(Qt::red);
1196 QWidget w;
1197 QPushButton *p1=new QPushButton(&w);
1198 QPushButton *p2=new QPushButton(&w);
1199 QPushButton *p3=new QPushButton(&w);
1200 QPushButton *p4=new QPushButton(&w);
1201 p1->setProperty(name: "prop", value: QStringList() << "red");
1202 p2->setProperty(name: "prop", value: QStringList() << "foo" << "red");
1203 p3->setProperty(name: "prop", value: QStringList() << "foo" << "bar");
1204
1205 w.setStyleSheet(" QPushButton{ background-color:blue; } QPushButton[prop~=red] { background-color:red; }");
1206 QCOMPARE(BACKGROUND(*p1) , red);
1207 QCOMPARE(BACKGROUND(*p2) , red);
1208 QCOMPARE(BACKGROUND(*p3) , blue);
1209 QCOMPARE(BACKGROUND(*p4) , blue);
1210}
1211
1212void tst_QStyleSheetStyle::minmaxSizes()
1213{
1214 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1215 QSKIP("Wayland: This fails. Figure out why.");
1216
1217 QTabWidget tabWidget;
1218 tabWidget.resize(m_testSize);
1219 tabWidget.setWindowTitle(QTest::currentTestFunction());
1220 tabWidget.setObjectName("tabWidget");
1221 int index1 = tabWidget.addTab(widget: new QLabel("Tab1"),"a");
1222
1223 QWidget *page2=new QLabel("page2");
1224 page2->setObjectName("page2");
1225 page2->setStyleSheet("* {background-color: white; min-width: 250px; max-width:500px }");
1226 tabWidget.addTab(widget: page2,"Tab2");
1227 QWidget *page3=new QLabel("plop");
1228 page3->setObjectName("Page3");
1229 page3->setStyleSheet("* {background-color: yellow; min-height: 250px; max-height:500px }");
1230 int index3 = tabWidget.addTab(widget: page3,"very_long_long_long_long_caption");
1231
1232 tabWidget.setStyleSheet("QTabBar::tab { min-width:100px; max-width:130px; }");
1233
1234 centerOnScreen(w: &tabWidget);
1235 tabWidget.show();
1236 QVERIFY(QTest::qWaitForWindowActive(&tabWidget));
1237 //i allow 4px additional border from the native style (hence the -2, <=2)
1238 QVERIFY(qAbs(page2->maximumSize().width() - 500 - 2) <= 2);
1239 QVERIFY(qAbs(page2->minimumSize().width() - 250 - 2) <= 2);
1240 QVERIFY(qAbs(page3->maximumSize().height() - 500 - 2) <= 2);
1241 QVERIFY(qAbs(page3->minimumSize().height() - 250 - 2) <= 2);
1242 QVERIFY(qAbs(page3->minimumSize().height() - 250 - 2) <= 2);
1243 QTabBar *bar = tabWidget.findChild<QTabBar*>();
1244 QVERIFY(bar);
1245#ifdef Q_OS_MAC
1246 QEXPECT_FAIL("", "QTBUG-23686", Continue);
1247#endif
1248 QVERIFY(qAbs(bar->tabRect(index1).width() - 100 - 2) <= 2);
1249#ifdef Q_OS_MAC
1250 QEXPECT_FAIL("", "QTBUG-23686", Continue);
1251#endif
1252 QVERIFY(qAbs(bar->tabRect(index3).width() - 130 - 2) <= 2);
1253}
1254
1255void tst_QStyleSheetStyle::task206238_twice()
1256{
1257 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1258 QSKIP("Wayland: This fails. Figure out why.");
1259
1260 const QColor red(Qt::red);
1261 QMainWindow w;
1262 w.resize(m_testSize);
1263 w.setWindowTitle(QTest::currentTestFunction());
1264 QTabWidget* tw = new QTabWidget;
1265 tw->addTab(widget: new QLabel("foo"), "test");
1266 w.setCentralWidget(tw);
1267 w.setStyleSheet("background: red;");
1268 centerOnScreen(w: &w);
1269 w.show();
1270 QVERIFY(QTest::qWaitForWindowActive(&w));
1271 QCOMPARE(BACKGROUND(w) , red);
1272 QCOMPARE(BACKGROUND(*tw), red);
1273 w.setStyleSheet("background: red;");
1274 QTest::qWait(ms: 20);
1275 QCOMPARE(BACKGROUND(w) , red);
1276 QCOMPARE(BACKGROUND(*tw), red);
1277}
1278
1279void tst_QStyleSheetStyle::transparent()
1280{
1281 QWidget w;
1282 QPushButton *p1=new QPushButton(&w);
1283 QPushButton *p2=new QPushButton(&w);
1284 QPushButton *p3=new QPushButton(&w);
1285 p1->setStyleSheet("background:transparent");
1286 p2->setStyleSheet("background-color:transparent");
1287 p3->setStyleSheet("background:rgba(0,0,0,0)");
1288 QCOMPARE(BACKGROUND(*p1) , QColor(0,0,0,0));
1289 QCOMPARE(BACKGROUND(*p2) , QColor(0,0,0,0));
1290 QCOMPARE(BACKGROUND(*p3) , QColor(0,0,0,0));
1291}
1292
1293
1294
1295class ProxyStyle : public QStyle
1296{
1297 Q_OBJECT
1298
1299 public:
1300 ProxyStyle(QStyle *s)
1301 {
1302 style = s;
1303 }
1304
1305 void drawControl(ControlElement ce, const QStyleOption *opt,
1306 QPainter *painter, const QWidget *widget = nullptr) const override;
1307
1308 void drawPrimitive(QStyle::PrimitiveElement pe,
1309 const QStyleOption* opt,
1310 QPainter *p,
1311 const QWidget *w) const override
1312 {
1313 style->drawPrimitive(pe, opt, p, w);
1314 }
1315
1316 QRect subElementRect(QStyle::SubElement se,
1317 const QStyleOption *opt,
1318 const QWidget *w) const override
1319 {
1320 Q_UNUSED(se);
1321 Q_UNUSED(opt);
1322 Q_UNUSED(w);
1323 return QRect(0, 0, 3, 3);
1324 }
1325
1326 void drawComplexControl(QStyle::ComplexControl cc,
1327 const QStyleOptionComplex *opt,
1328 QPainter *p,
1329 const QWidget *w) const override
1330 {
1331 style->drawComplexControl(cc, opt, p, widget: w);
1332 }
1333
1334
1335 SubControl hitTestComplexControl(QStyle::ComplexControl cc,
1336 const QStyleOptionComplex *opt,
1337 const QPoint &pt,
1338 const QWidget *w) const override
1339 {
1340 return style->hitTestComplexControl(cc, opt, pt, widget: w);
1341 }
1342
1343 QRect subControlRect(QStyle::ComplexControl cc,
1344 const QStyleOptionComplex *opt,
1345 QStyle::SubControl sc,
1346 const QWidget *w) const override
1347 {
1348 return style->subControlRect(cc, opt, sc, widget: w);
1349 }
1350
1351 int pixelMetric(QStyle::PixelMetric pm,
1352 const QStyleOption *opt,
1353 const QWidget *w) const override
1354 {
1355 return style->pixelMetric(metric: pm, option: opt, widget: w);
1356 }
1357
1358
1359 QSize sizeFromContents(QStyle::ContentsType ct,
1360 const QStyleOption *opt,
1361 const QSize &size,
1362 const QWidget *w) const override
1363 {
1364 return style->sizeFromContents(ct, opt, contentsSize: size, w);
1365 }
1366
1367 int styleHint(QStyle::StyleHint sh,
1368 const QStyleOption *opt,
1369 const QWidget *w,
1370 QStyleHintReturn *shr) const override
1371 {
1372 return style->styleHint(stylehint: sh, opt, widget: w, returnData: shr);
1373 }
1374
1375 QPixmap standardPixmap(QStyle::StandardPixmap spix,
1376 const QStyleOption *opt,
1377 const QWidget *w) const override
1378 {
1379 return style->standardPixmap(standardPixmap: spix, opt, widget: w);
1380 }
1381
1382 QPixmap generatedIconPixmap(QIcon::Mode mode,
1383 const QPixmap &pix,
1384 const QStyleOption *opt) const override
1385 {
1386 return style->generatedIconPixmap(iconMode: mode, pixmap: pix, opt);
1387 }
1388
1389 int layoutSpacing(QSizePolicy::ControlType c1,
1390 QSizePolicy::ControlType c2,
1391 Qt::Orientation ori,
1392 const QStyleOption *opt,
1393 const QWidget *w) const override
1394 {
1395 return style->layoutSpacing(control1: c1, control2: c2, orientation: ori, option: opt, widget: w);
1396 }
1397
1398 QIcon standardIcon(StandardPixmap si,
1399 const QStyleOption *opt,
1400 const QWidget *w) const override
1401 {
1402 return style->standardIcon(standardIcon: si, option: opt, widget: w);
1403 }
1404
1405 private:
1406 QStyle *style;
1407};
1408
1409void ProxyStyle::drawControl(ControlElement ce, const QStyleOption *opt,
1410 QPainter *painter, const QWidget *widget) const
1411{
1412 if(ce == CE_PushButton)
1413 {
1414 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt))
1415 {
1416 QRect r = btn->rect;
1417 painter->fillRect(r, c: Qt::green);
1418
1419 if(btn->state & QStyle::State_HasFocus)
1420 painter->fillRect(r: r.adjusted(xp1: 5, yp1: 5, xp2: -5, yp2: -5), c: Qt::yellow);
1421
1422
1423 painter->drawText(r, flags: Qt::AlignCenter, text: btn->text);
1424 }
1425 }
1426 else
1427 {
1428 style->drawControl(element: ce, opt, p: painter, w: widget);
1429 }
1430}
1431
1432void tst_QStyleSheetStyle::proxyStyle()
1433{
1434 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1435 QSKIP("Wayland: This fails. Figure out why.");
1436
1437 //Should not crash; task 158984
1438
1439 ProxyStyle *proxy = new ProxyStyle(QApplication::style());
1440 QString styleSheet("QPushButton {background-color: red; }");
1441
1442 QWidget *w = new QWidget;
1443 w->setMinimumWidth(m_testSize.width());
1444 centerOnScreen(w);
1445 QVBoxLayout *layout = new QVBoxLayout(w);
1446
1447 QPushButton *pb1 = new QPushButton(QApplication::style()->objectName(), w);
1448 layout->addWidget(pb1);
1449
1450 QPushButton *pb2 = new QPushButton("ProxyStyle", w);
1451 pb2->setStyle(proxy);
1452 layout->addWidget(pb2);
1453
1454 QPushButton *pb3 = new QPushButton("StyleSheet", w);
1455 pb3->setStyleSheet(styleSheet);
1456 layout->addWidget(pb3);
1457
1458 QPushButton *pb4 = new QPushButton("StyleSheet then ProxyStyle ", w);
1459 pb4->setStyleSheet(styleSheet);
1460
1461 // We are creating our Proxy based on current style...
1462 // In this case it would be the QStyleSheetStyle that is deleted
1463 // later on. We need to get access to the "real" QStyle to be able to
1464 // draw correctly.
1465 ProxyStyle *newProxy = new ProxyStyle(QApplication::style());
1466 pb4->setStyle(newProxy);
1467
1468 layout->addWidget(pb4);
1469
1470 QPushButton *pb5 = new QPushButton("ProxyStyle then StyleSheet ", w);
1471 pb5->setStyle(proxy);
1472 pb5->setStyleSheet(styleSheet);
1473 layout->addWidget(pb5);
1474
1475 w->show();
1476 QVERIFY(QTest::qWaitForWindowActive(w));
1477
1478 // Test for QTBUG-7198 - style sheet overrides custom element size
1479 QStyleOptionViewItem opt;
1480 opt.initFrom(w);
1481 opt.features |= QStyleOptionViewItem::HasCheckIndicator;
1482 QVERIFY(pb5->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator,
1483 &opt, pb5).width() == 3);
1484 delete w;
1485 delete proxy;
1486 delete newProxy;
1487}
1488
1489void tst_QStyleSheetStyle::dialogButtonBox()
1490{
1491 QDialogButtonBox box;
1492 box.addButton(button: QDialogButtonBox::Ok);
1493 box.addButton(button: QDialogButtonBox::Cancel);
1494 box.setStyleSheet("/** */ ");
1495 box.setStyleSheet(QString()); //should not crash
1496}
1497
1498void tst_QStyleSheetStyle::emptyStyleSheet()
1499{
1500 //empty stylesheet should not change anything
1501 qApp->setStyleSheet(QString());
1502 QWidget w;
1503 w.setWindowTitle(QTest::currentTestFunction());
1504 QHBoxLayout layout(&w);
1505 w.setLayout(&layout);
1506 layout.addWidget(new QPushButton("push", &w));
1507 layout.addWidget(new QToolButton(&w));
1508 QLabel label("toto", &w);
1509 label.setFrameShape(QLabel::Panel);
1510 label.setFrameShadow(QLabel::Sunken);
1511 layout.addWidget(&label); //task 231137
1512 layout.addWidget(new QTableWidget(200,200, &w));
1513 layout.addWidget(new QProgressBar(&w));
1514 layout.addWidget(new QLineEdit(&w));
1515 layout.addWidget(new QSpinBox(&w));
1516 layout.addWidget(new QComboBox(&w));
1517 layout.addWidget(new QDateEdit(&w));
1518 layout.addWidget(new QGroupBox("some text", &w));
1519
1520 centerOnScreen(w: &w);
1521 w.show();
1522 QVERIFY(QTest::qWaitForWindowExposed(&w));
1523 //workaround the fact that the label sizehint is one pixel different the first time.
1524 label.setIndent(0); //force to recompute the sizeHint:
1525 w.setFocus();
1526 QTest::qWait(ms: 100);
1527
1528 QImage img1(w.size(), QImage::Format_ARGB32);
1529 w.render(target: &img1);
1530
1531 w.setStyleSheet("/* */");
1532 QTest::qWait(ms: 100);
1533
1534 QImage img2(w.size(), QImage::Format_ARGB32);
1535 w.render(target: &img2);
1536
1537 if(img1 != img2) {
1538 img1.save(fileName: "emptyStyleSheet_img1.png");
1539 img2.save(fileName: "emptyStyleSheet_img2.png");
1540 }
1541
1542 QEXPECT_FAIL("", "QTBUG-21468", Abort);
1543 QCOMPARE(img1,img2);
1544}
1545
1546class ApplicationStyleSetter
1547{
1548public:
1549 explicit inline ApplicationStyleSetter(QStyle *s) : m_oldStyleName(QApplication::style()->objectName())
1550 { QApplication::setStyle(s); }
1551 inline ~ApplicationStyleSetter()
1552 { QApplication::setStyle(QStyleFactory::create(m_oldStyleName)); }
1553
1554private:
1555 const QString m_oldStyleName;
1556};
1557
1558void tst_QStyleSheetStyle::toolTip()
1559{
1560 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1561 QSKIP("Wayland: This fails. Figure out why.");
1562
1563 qApp->setStyleSheet(QString());
1564 QWidget w;
1565 w.resize(m_testSize);
1566 w.setWindowTitle(QTest::currentTestFunction());
1567 // Use "Fusion" to prevent the Vista style from clobbering the tooltip palette in polish().
1568 QStyle *fusionStyle = QStyleFactory::create(QLatin1String("Fusion"));
1569 QVERIFY(fusionStyle);
1570 ApplicationStyleSetter as(fusionStyle);
1571 QHBoxLayout layout(&w);
1572 w.setLayout(&layout);
1573
1574 QWidget *wid1 = new QGroupBox(&w);
1575 layout.addWidget(wid1);
1576 wid1->setStyleSheet("QToolTip { background: #ae2; } #wid3 > QToolTip { background: #0b8; } ");
1577 QVBoxLayout *layout1 = new QVBoxLayout(wid1);
1578 wid1->setLayout(layout1);
1579 wid1->setToolTip("this is wid1");
1580 wid1->setObjectName("wid1");
1581
1582 QWidget *wid2 = new QPushButton("wid2", wid1);
1583 layout1->addWidget(wid2);
1584 wid2->setStyleSheet("QToolTip { background: #f81; } ");
1585 wid2->setToolTip("this is wid2");
1586 wid2->setObjectName("wid2");
1587
1588 QWidget *wid3 = new QPushButton("wid3", wid1);
1589 layout1->addWidget(wid3);
1590 wid3->setToolTip("this is wid3");
1591 wid3->setObjectName("wid3");
1592
1593 QWidget *wid4 = new QPushButton("wid4", &w);
1594 layout.addWidget(wid4);
1595 wid4->setToolTip("this is wid4");
1596 wid4->setObjectName("wid4");
1597
1598 centerOnScreen(w: &w);
1599 w.show();
1600 QApplication::setActiveWindow(&w);
1601 QVERIFY(QTest::qWaitForWindowActive(&w));
1602
1603 const QColor normalToolTip = QToolTip::palette().color(cg: QPalette::Inactive, cr: QPalette::ToolTipBase);
1604 // Tooltip on the widget without stylesheet, then to other widget,
1605 // including one without stylesheet (the tooltip will be reused,
1606 // but its color must change)
1607 const QWidgetList widgets{wid4, wid1, wid2, wid3, wid4};
1608 const QVector<QColor> colors{normalToolTip, QColor("#ae2"), QColor("#f81"),
1609 QColor("#0b8"), normalToolTip};
1610
1611 QWidgetList topLevels;
1612 for (int i = 0; i < widgets.count() ; ++i) {
1613 QWidget *wid = widgets.at(i);
1614 QColor col = colors.at(i);
1615
1616 QToolTip::showText( pos: QPoint(0,0) , text: "This is " + wid->objectName(), w: wid);
1617
1618 topLevels = QApplication::topLevelWidgets();
1619 QWidget *tooltip = nullptr;
1620 for (QWidget *widget : qAsConst(t&: topLevels)) {
1621 if (widget->inherits(classname: "QTipLabel")) {
1622 tooltip = widget;
1623 break;
1624 }
1625 }
1626 QVERIFY(tooltip);
1627 QTRY_VERIFY(tooltip->isVisible()); // Wait until Roll-Effect is finished (Windows Vista)
1628 QCOMPARE(tooltip->palette().color(tooltip->backgroundRole()), col);
1629 }
1630
1631 QToolTip::showText( pos: QPoint(0,0) , text: "This is " + wid3->objectName(), w: wid3);
1632 QTest::qWait(ms: 100);
1633 delete wid3; //should not crash;
1634 QTest::qWait(ms: 10);
1635 topLevels = QApplication::topLevelWidgets();
1636 for (QWidget *widget : qAsConst(t&: topLevels))
1637 widget->update(); //should not crash either
1638}
1639
1640void tst_QStyleSheetStyle::embeddedFonts()
1641{
1642 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1643 QSKIP("Wayland: This fails. Figure out why.");
1644
1645 //task 235622 and 210551
1646 QSpinBox spin;
1647 spin.setWindowTitle(QTest::currentTestFunction());
1648 spin.setMinimumWidth(m_testSize.width());
1649 spin.move(m_availableGeometry.topLeft() + QPoint(20, 20));
1650 spin.show();
1651 spin.setStyleSheet("QSpinBox { font-size: 32px; }");
1652 QTest::qWait(ms: 20);
1653 QLineEdit *embedded = spin.findChild<QLineEdit *>();
1654 QVERIFY(embedded);
1655 QCOMPARE(spin.font().pixelSize(), 32);
1656 QCOMPARE(embedded->font().pixelSize(), 32);
1657
1658#ifndef QT_NO_CONTEXTMENU
1659 QMenu *menu = embedded->createStandardContextMenu();
1660 menu->show();
1661 QTest::qWait(ms: 20);
1662 QVERIFY(menu);
1663 QVERIFY(menu->font().pixelSize() != 32);
1664 QCOMPARE(menu->font().pixelSize(), qApp->font(menu).pixelSize());
1665#endif // QT_NO_CONTEXTMENU
1666
1667 //task 242556
1668 QComboBox box;
1669 box.setMinimumWidth(160);
1670 box.move(m_availableGeometry.topLeft() + QPoint(20, 120));
1671 box.setEditable(true);
1672 box.addItems(texts: QStringList() << "First" << "Second" << "Third");
1673 box.setStyleSheet("QComboBox { font-size: 32px; }");
1674 box.show();
1675 QVERIFY(QTest::qWaitForWindowActive(&box));
1676 embedded = box.findChild<QLineEdit *>();
1677 QVERIFY(embedded);
1678 QCOMPARE(box.font().pixelSize(), 32);
1679 QCOMPARE(embedded->font().pixelSize(), 32);
1680}
1681
1682void tst_QStyleSheetStyle::opaquePaintEvent_data()
1683{
1684 QTest::addColumn<QString>(name: "stylesheet");
1685 QTest::addColumn<bool>(name: "transparent");
1686 QTest::addColumn<bool>(name: "styled");
1687
1688 QTest::newRow(dataTag: "none") << QString::fromLatin1(str: "/* */") << false << false;
1689 QTest::newRow(dataTag: "background black ") << QString::fromLatin1(str: "background: black;") << false << true;
1690 QTest::newRow(dataTag: "background qrgba") << QString::fromLatin1(str: "background: rgba(125,0,0,125);") << true << true;
1691 QTest::newRow(dataTag: "background transparent") << QString::fromLatin1(str: "background: transparent;") << true << true;
1692 QTest::newRow(dataTag: "border native") << QString::fromLatin1(str: "border: native;") << false << false;
1693 QTest::newRow(dataTag: "border solid") << QString::fromLatin1(str: "border: 2px solid black;") << true << true;
1694 QTest::newRow(dataTag: "border transparent") << QString::fromLatin1(str: "background: black; border: 2px solid rgba(125,0,0,125);") << true << true;
1695 QTest::newRow(dataTag: "margin") << QString::fromLatin1(str: "margin: 25px;") << true << true;
1696 QTest::newRow(dataTag: "focus") << QString::fromLatin1(str: "*:focus { background: rgba(125,0,0,125) }") << true << true;
1697}
1698
1699void tst_QStyleSheetStyle::opaquePaintEvent()
1700{
1701 QFETCH(QString, stylesheet);
1702 QFETCH(bool, transparent);
1703 QFETCH(bool, styled);
1704
1705 QWidget tl;
1706 QWidget cl(&tl);
1707 cl.setAttribute(Qt::WA_OpaquePaintEvent, on: true);
1708 cl.setAutoFillBackground(true);
1709 cl.setStyleSheet(stylesheet);
1710 cl.ensurePolished();
1711 QCOMPARE(cl.testAttribute(Qt::WA_OpaquePaintEvent), !transparent);
1712 QCOMPARE(cl.testAttribute(Qt::WA_StyledBackground), styled);
1713 QCOMPARE(cl.autoFillBackground(), !styled );
1714}
1715
1716void tst_QStyleSheetStyle::complexWidgetFocus()
1717{
1718 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1719 QSKIP("Wayland: This fails. Figure out why.");
1720
1721 // This test is a simplified version of the focusColors() test above.
1722
1723 // Tests if colors can be changed by altering the focus of the widget.
1724 // To avoid messy pixel-by-pixel comparison, we assume that the goal
1725 // is reached if at least ten pixels of the right color can be found in
1726 // the image.
1727 // For this reason, we use unusual and extremely ugly colors! :-)
1728
1729 QDialog frame;
1730 frame.setWindowTitle(QTest::currentTestFunction());
1731 frame.setStyleSheet("*:focus { background: black; color: black } "
1732 "QSpinBox::up-arrow:focus, QSpinBox::down-arrow:focus { width: 7px; height: 7px; background: #ff0084 } "
1733 "QComboBox::down-arrow:focus { width: 7px; height: 7px; background: #ff0084 }"
1734 "QSlider::handle:horizontal:focus { width: 7px; height: 7px; background: #ff0084 } ");
1735
1736 const QWidgetList widgets{new QSpinBox, new QComboBox, new QSlider(Qt::Horizontal)};
1737
1738 QLayout* layout = new QGridLayout;
1739 layout->addWidget(w: new QLineEdit); // Avoids initial focus.
1740 for (QWidget *widget : widgets)
1741 layout->addWidget(w: widget);
1742 frame.setLayout(layout);
1743
1744 centerOnScreen(w: &frame);
1745 frame.show();
1746 QApplication::setActiveWindow(&frame);
1747 QVERIFY(QTest::qWaitForWindowActive(&frame));
1748 for (QWidget *widget : widgets) {
1749 widget->setFocus();
1750 QApplication::processEvents();
1751
1752 QImage image(frame.width(), frame.height(), QImage::Format_ARGB32);
1753 frame.render(target: &image);
1754 if (image.depth() < 24)
1755 QSKIP("Test doesn't support color depth < 24");
1756
1757 QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)),
1758 (QString::fromLatin1(widget->metaObject()->className())
1759 + " did not contain text color #ff0084, using style "
1760 + QString::fromLatin1(QApplication::style()->metaObject()->className()))
1761 .toLocal8Bit().constData());
1762 }
1763}
1764
1765void tst_QStyleSheetStyle::task188195_baseBackground()
1766{
1767 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1768 QSKIP("Wayland: This fails. Figure out why.");
1769
1770 QTreeView tree;
1771 tree.setWindowTitle(QTest::currentTestFunction());
1772 tree.setStyleSheet( "QTreeView:disabled { background-color:#ab1251; }" );
1773 tree.setGeometry(QRect(m_availableGeometry.topLeft() + QPoint(20, 100), m_testSize));
1774 tree.show();
1775 QVERIFY(QTest::qWaitForWindowActive(&tree));
1776 QImage image(tree.width(), tree.height(), QImage::Format_ARGB32);
1777
1778 tree.render(target: &image);
1779 QVERIFY(testForColors(image, tree.palette().base().color()));
1780 QVERIFY(!testForColors(image, QColor(0xab, 0x12, 0x51)));
1781
1782 tree.setEnabled(false);
1783 tree.render(target: &image);
1784 QVERIFY(testForColors(image, QColor(0xab, 0x12, 0x51)));
1785
1786 tree.setEnabled(true);
1787 tree.render(target: &image);
1788 QVERIFY(testForColors(image, tree.palette().base().color()));
1789 QVERIFY(!testForColors(image, QColor(0xab, 0x12, 0x51)));
1790
1791 QTableWidget table(12, 12);
1792 table.setItem(row: 0, column: 0, item: new QTableWidgetItem());
1793 table.setStyleSheet( "QTableView {background-color: #ff0000}" );
1794 // This needs to be large so that >50% (excluding header rows/columns) are red.
1795 table.setGeometry(QRect(m_availableGeometry.topLeft() + QPoint(300, 100), m_testSize * 2));
1796 table.show();
1797 QVERIFY(QTest::qWaitForWindowActive(&table));
1798 image = QImage(table.width(), table.height(), QImage::Format_ARGB32);
1799 table.render(target: &image);
1800 QVERIFY(testForColors(image, Qt::red, true));
1801}
1802
1803void tst_QStyleSheetStyle::task232085_spinBoxLineEditBg()
1804{
1805 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1806 QSKIP("Wayland: This fails. Figure out why.");
1807
1808 // This test is a simplified version of the focusColors() test above.
1809
1810 // Tests if colors can be changed by altering the focus of the widget.
1811 // To avoid messy pixel-by-pixel comparison, we assume that the goal
1812 // is reached if at least ten pixels of the right color can be found in
1813 // the image.
1814 // For this reason, we use unusual and extremely ugly colors! :-)
1815
1816 QSpinBox *spinbox = new QSpinBox;
1817 spinbox->setValue(8888);
1818
1819 QDialog frame;
1820 frame.setWindowTitle(QTest::currentTestFunction());
1821 QLayout* layout = new QGridLayout;
1822
1823 QLineEdit* dummy = new QLineEdit; // Avoids initial focus.
1824
1825 // We only want to test the line edit colors.
1826 spinbox->setStyleSheet("QSpinBox:focus { background: #e8ff66; color: #ff0084 } "
1827 "QSpinBox::up-button, QSpinBox::down-button { background: black; }");
1828
1829 layout->addWidget(w: dummy);
1830 layout->addWidget(w: spinbox);
1831 frame.setLayout(layout);
1832
1833 centerOnScreen(w: &frame);
1834 frame.show();
1835 QApplication::setActiveWindow(&frame);
1836 spinbox->setFocus();
1837 QVERIFY(QTest::qWaitForWindowActive(&frame));
1838
1839 QImage image(frame.width(), frame.height(), QImage::Format_ARGB32);
1840 frame.render(target: &image);
1841 if (image.depth() < 24)
1842 QSKIP("Test doesn't support color depth < 24");
1843
1844 QVERIFY2(testForColors(image, QColor(0xe8, 0xff, 0x66)),
1845 (QString::fromLatin1(spinbox->metaObject()->className())
1846 + " did not contain background color #e8ff66, using style "
1847 + QString::fromLatin1(QApplication::style()->metaObject()->className()))
1848 .toLocal8Bit().constData());
1849 QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)),
1850 (QString::fromLatin1(spinbox->metaObject()->className())
1851 + " did not contain text color #ff0084, using style "
1852 + QString::fromLatin1(QApplication::style()->metaObject()->className()))
1853 .toLocal8Bit().constData());
1854}
1855
1856class ChangeEventWidget : public QWidget
1857{
1858protected:
1859 void changeEvent(QEvent *event) override
1860 {
1861 if(event->type() == QEvent::StyleChange) {
1862 static bool recurse = false;
1863 if (!recurse) {
1864 recurse = true;
1865 QStyle *style = QStyleFactory::create(QLatin1String("Fusion"));
1866 style->setParent(this);
1867 setStyle(style);
1868 recurse = false;
1869 }
1870 }
1871 QWidget::changeEvent(event);
1872 }
1873};
1874
1875void tst_QStyleSheetStyle::changeStyleInChangeEvent()
1876{ //must not crash;
1877 ChangeEventWidget wid;
1878 wid.ensurePolished();
1879 wid.setStyleSheet(" /* */ ");
1880 wid.ensurePolished();
1881 wid.setStyleSheet(" /* ** */ ");
1882 wid.ensurePolished();
1883}
1884
1885void tst_QStyleSheetStyle::QTBUG11658_cachecrash()
1886{
1887 //should not crash
1888 class Widget : public QWidget
1889 {
1890 public:
1891 Widget(int minimumWidth, QWidget *parent = nullptr)
1892 : QWidget(parent)
1893 {
1894 setMinimumWidth(minimumWidth);
1895 QVBoxLayout* pLayout = new QVBoxLayout(this);
1896 QCheckBox* pCheckBox = new QCheckBox(this);
1897 pLayout->addWidget(pCheckBox);
1898 setLayout(pLayout);
1899
1900 QString szStyleSheet = QLatin1String("* { color: red; }");
1901 qApp->setStyleSheet(szStyleSheet);
1902 QApplication::setStyle(QStyleFactory::create(QLatin1String("Windows")));
1903 }
1904 };
1905
1906 Widget *w = new Widget(m_testSize.width());
1907 delete w;
1908 w = new Widget(m_testSize.width());
1909 w->setWindowTitle(QTest::currentTestFunction());
1910 centerOnScreen(w);
1911 w->show();
1912
1913 QVERIFY(QTest::qWaitForWindowExposed(w));
1914 delete w;
1915 qApp->setStyleSheet(QString());
1916}
1917
1918void tst_QStyleSheetStyle::QTBUG15910_crashNullWidget()
1919{
1920 struct Widget : QWidget {
1921 void paintEvent(QPaintEvent *) override
1922 {
1923 QStyleOption opt;
1924 opt.init(w: this);
1925 QPainter p(this);
1926 style()->drawPrimitive(pe: QStyle::PE_Widget, opt: &opt, p: &p, w: nullptr);
1927 style()->drawPrimitive(pe: QStyle::PE_Frame, opt: &opt, p: &p, w: nullptr);
1928 style()->drawControl(element: QStyle::CE_PushButton, opt: &opt, p: &p, w: nullptr);
1929 }
1930 } w;
1931 w.setWindowTitle(QTest::currentTestFunction());
1932 w.setStyleSheet("* { background-color: white; color:black; border 3px solid yellow }");
1933 w.setMinimumWidth(160);
1934 centerOnScreen(w: &w);
1935 w.show();
1936 QVERIFY(QTest::qWaitForWindowExposed(&w));
1937}
1938
1939void tst_QStyleSheetStyle::QTBUG36933_brokenPseudoClassLookup()
1940{
1941 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1942 QSKIP("Wayland: This fails. Figure out why.");
1943
1944 const int rowCount = 10;
1945 const int columnCount = 10;
1946
1947 QTableWidget widget(rowCount, columnCount);
1948 widget.resize(m_testSize);
1949 widget.setWindowTitle(QTest::currentTestFunction());
1950
1951 for (int row = 0; row < rowCount; ++row) {
1952 const QString rowNumber = QLatin1String("row ") + QString::number(row + 1);
1953 for (int column = 0; column < columnCount; ++column) {
1954 const QString t = rowNumber
1955 + QLatin1String(" column ") + QString::number(column + 1);
1956 widget.setItem(row, column, item: new QTableWidgetItem(t));
1957 }
1958
1959 // put no visible text for the vertical headers, but still put some text or they will collapse
1960 widget.setVerticalHeaderItem(row, item: new QTableWidgetItem(QStringLiteral(" ")));
1961 }
1962
1963 // parsing of this stylesheet must not crash, and it must be correctly applied
1964 widget.setStyleSheet(QStringLiteral("QHeaderView::section:vertical { background-color: #FF0000 }"));
1965
1966 centerOnScreen(w: &widget);
1967 widget.show();
1968 QVERIFY(QTest::qWaitForWindowExposed(&widget));
1969
1970 widget.activateWindow();
1971 QApplication::setActiveWindow(&widget);
1972 QVERIFY(QTest::qWaitForWindowActive(&widget));
1973
1974 QHeaderView *verticalHeader = widget.verticalHeader();
1975 QImage image(verticalHeader->size(), QImage::Format_ARGB32);
1976 verticalHeader->render(target: &image);
1977 if (!QApplication::style()->objectName().compare(other: QLatin1String("fusion"), cs: Qt::CaseInsensitive))
1978 QEXPECT_FAIL("", "QTBUG-21468", Abort);
1979 QVERIFY(testForColors(image, QColor(0xFF, 0x00, 0x00)));
1980}
1981
1982void tst_QStyleSheetStyle::styleSheetChangeBeforePolish()
1983{
1984 QWidget widget;
1985 widget.setWindowTitle(QTest::currentTestFunction());
1986 QVBoxLayout *vbox = new QVBoxLayout(&widget);
1987 QFrame *frame = new QFrame(&widget);
1988 frame->setFixedSize(m_testSize);
1989 frame->setStyleSheet("background-color: #FF0000;");
1990 frame->setStyleSheet("background-color: #00FF00;");
1991 vbox->addWidget(frame);
1992 QFrame *frame2 = new QFrame(&widget);
1993 frame2->setFixedSize(m_testSize);
1994 frame2->setStyleSheet("background-color: #FF0000;");
1995 frame2->setStyleSheet("background-color: #00FF00;");
1996 vbox->addWidget(frame);
1997 widget.show();
1998 QVERIFY(QTest::qWaitForWindowExposed(&widget));
1999 QImage image(frame->size(), QImage::Format_ARGB32);
2000 frame->render(target: &image);
2001 QVERIFY(testForColors(image, QColor(0x00, 0xFF, 0x00)));
2002 QImage image2(frame2->size(), QImage::Format_ARGB32);
2003 frame2->render(target: &image2);
2004 QVERIFY(testForColors(image2, QColor(0x00, 0xFF, 0x00)));
2005}
2006
2007void tst_QStyleSheetStyle::widgetStylePropagation_data()
2008{
2009 QTest::addColumn<QString>(name: "applicationStyleSheet");
2010 QTest::addColumn<QString>(name: "parentStyleSheet");
2011 QTest::addColumn<QString>(name: "childStyleSheet");
2012 QTest::addColumn<QFont>(name: "parentFont");
2013 QTest::addColumn<QFont>(name: "childFont");
2014 QTest::addColumn<QPalette>(name: "parentPalette");
2015 QTest::addColumn<QPalette>(name: "childPalette");
2016 QTest::addColumn<int>(name: "parentExpectedSize");
2017 QTest::addColumn<int>(name: "childExpectedSize");
2018 QTest::addColumn<QColor>(name: "parentExpectedColor");
2019 QTest::addColumn<QColor>(name: "childExpectedColor");
2020
2021 QFont noFont;
2022 QFont font45; font45.setPointSize(45);
2023 QFont font32; font32.setPointSize(32);
2024
2025 QPalette noPalette;
2026 QPalette redPalette; redPalette.setColor(acr: QPalette::WindowText, acolor: QColor("red"));
2027 QPalette greenPalette; greenPalette.setColor(acr: QPalette::WindowText, acolor: QColor("green"));
2028
2029 QLabel defaultLabel;
2030
2031 int defaultSize = defaultLabel.font().pointSize();
2032 QColor defaultColor = defaultLabel.palette().color(cr: defaultLabel.foregroundRole());
2033 QColor redColor("red");
2034 QColor greenColor("green");
2035
2036 // Check regular Qt propagation works as expected, with and without a
2037 // non-interfering application stylesheet
2038 QTest::newRow(dataTag: "defaults")
2039 << QString() << QString() << QString()
2040 << noFont << noFont << noPalette << noPalette
2041 << defaultSize << defaultSize << defaultColor << defaultColor;
2042 QTest::newRow(dataTag: "parent font propagation, no application style sheet")
2043 << QString() << QString() << QString()
2044 << font45 << noFont << noPalette << noPalette
2045 << 45 << 45 << defaultColor << defaultColor;
2046 QTest::newRow(dataTag: "parent font propagation, dummy application style sheet")
2047 << "QGroupBox { font-size: 64pt }" << QString() << QString()
2048 << font45 << noFont << noPalette << noPalette
2049 << 45 << 45 << defaultColor << defaultColor;
2050 QTest::newRow(dataTag: "parent color propagation, no application style sheet")
2051 << QString() << QString() << QString()
2052 << noFont << noFont << redPalette << noPalette
2053 << defaultSize << defaultSize << redColor << redColor;
2054 QTest::newRow(dataTag: "parent color propagation, dummy application style sheet")
2055 << "QGroupBox { color: blue }" << QString() << QString()
2056 << noFont << noFont << redPalette << noPalette
2057 << defaultSize << defaultSize << redColor << redColor;
2058
2059 // Parent style sheet propagates to child if child has not explicitly
2060 // set a value
2061 QTest::newRow(dataTag: "parent style sheet color propagation")
2062 << "#parentLabel { color: red }" << QString() << QString()
2063 << noFont << noFont << noPalette << noPalette
2064 << defaultSize << defaultSize << redColor << redColor;
2065 QTest::newRow(dataTag: "parent style sheet font propagation")
2066 << "#parentLabel { font-size: 45pt }" << QString() << QString()
2067 << noFont << noFont << noPalette << noPalette
2068 << 45 << 45 << defaultColor << defaultColor;
2069
2070 // Parent style sheet does not propagate to child if child has explicitly
2071 // set a value
2072 QTest::newRow(dataTag: "parent style sheet color propagation, child explicitly set")
2073 << "#parentLabel { color: red }" << QString() << QString()
2074 << noFont << noFont << noPalette << greenPalette
2075 << defaultSize << defaultSize << redColor << greenColor;
2076 QTest::newRow(dataTag: "parent style sheet font propagation, child explicitly set")
2077 << "#parentLabel { font-size: 45pt }" << QString() << QString()
2078 << noFont << font32 << noPalette << noPalette
2079 << 45 << 32 << defaultColor << defaultColor;
2080
2081 // Parent does not propagate to child when child is target of style sheet
2082 QTest::newRow(dataTag: "parent style sheet font propagation, child application style sheet")
2083 << "#childLabel { font-size: 32pt }" << QString() << QString()
2084 << font45 << noFont << noPalette << noPalette
2085 << 45 << 32 << defaultColor << defaultColor;
2086 QTest::newRow(dataTag: "parent style sheet color propagation, child application style sheet")
2087 << "#childLabel { color: green }" << QString() << QString()
2088 << noFont << noFont << redPalette << noPalette
2089 << defaultSize << defaultSize << redColor << greenColor;
2090}
2091
2092void tst_QStyleSheetStyle::widgetStylePropagation()
2093{
2094 QFETCH(QString, applicationStyleSheet);
2095 QFETCH(QString, parentStyleSheet);
2096 QFETCH(QString, childStyleSheet);
2097
2098 QFETCH(QFont, parentFont);
2099 QFETCH(QFont, childFont);
2100 QFETCH(QPalette, parentPalette);
2101 QFETCH(QPalette, childPalette);
2102
2103 QFETCH(int, parentExpectedSize);
2104 QFETCH(int, childExpectedSize);
2105 QFETCH(QColor, parentExpectedColor);
2106 QFETCH(QColor, childExpectedColor);
2107
2108 QCoreApplication::setAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles, on: true);
2109
2110 qApp->setStyleSheet(applicationStyleSheet);
2111
2112 QLabel parentLabel;
2113 parentLabel.setObjectName("parentLabel");
2114 QLabel childLabel(&parentLabel);
2115 childLabel.setObjectName("childLabel");
2116
2117 if (parentFont.resolve())
2118 parentLabel.setFont(parentFont);
2119 if (childFont.resolve())
2120 childLabel.setFont(childFont);
2121 if (parentPalette.resolve())
2122 parentLabel.setPalette(parentPalette);
2123 if (childPalette.resolve())
2124 childLabel.setPalette(childPalette);
2125 if (!parentStyleSheet.isEmpty())
2126 parentLabel.setStyleSheet(parentStyleSheet);
2127 if (!childStyleSheet.isEmpty())
2128 childLabel.setStyleSheet(childStyleSheet);
2129
2130 parentLabel.ensurePolished();
2131 childLabel.ensurePolished();
2132
2133 QCOMPARE(FONTSIZE(parentLabel), parentExpectedSize);
2134 QCOMPARE(FONTSIZE(childLabel), childExpectedSize);
2135 QCOMPARE(COLOR(parentLabel), parentExpectedColor);
2136 QCOMPARE(COLOR(childLabel), childExpectedColor);
2137}
2138
2139void tst_QStyleSheetStyle::styleSheetTargetAttribute()
2140{
2141 QGroupBox gb;
2142 QLabel lb(&gb);
2143 QPushButton pb(&lb);
2144
2145 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2146 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false);
2147 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false);
2148 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false);
2149
2150 qApp->setStyleSheet("QPushButton { background-color: blue; }");
2151
2152 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2153 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false);
2154 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false);
2155 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true);
2156
2157 qApp->setStyleSheet("QGroupBox { background-color: blue; }");
2158
2159 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2160 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), true);
2161 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false);
2162 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false);
2163
2164 qApp->setStyleSheet("QGroupBox * { background-color: blue; }");
2165
2166 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2167 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false);
2168 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true);
2169 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true);
2170
2171 qApp->setStyleSheet("* { background-color: blue; }");
2172 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2173 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), true);
2174 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true);
2175 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true);
2176
2177 qApp->setStyleSheet("QLabel { font-size: 32pt; }");
2178
2179 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2180 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false);
2181 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true);
2182 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false);
2183
2184 qApp->setStyleSheet(QString());
2185
2186 gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished();
2187 QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false);
2188 QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false);
2189 QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false);
2190}
2191
2192void tst_QStyleSheetStyle::unpolish()
2193{
2194 QWidget w;
2195 QCOMPARE(w.minimumWidth(), 0);
2196 w.setStyleSheet("QWidget { min-width: 100; }");
2197 w.ensurePolished();
2198 QCOMPARE(w.minimumWidth(), 100);
2199 w.setStyleSheet(QString());
2200 QCOMPARE(w.minimumWidth(), 0);
2201}
2202
2203void tst_QStyleSheetStyle::highdpiImages_data()
2204{
2205 QTest::addColumn<qreal>(name: "screenFactor");
2206 QTest::addColumn<QColor>(name: "color");
2207
2208 QTest::newRow(dataTag: "highdpi") << 2.0 << QColor(0x00, 0xFF, 0x00);
2209 QTest::newRow(dataTag: "lowdpi") << 1.0 << QColor(0xFF, 0x00, 0x00);
2210}
2211
2212void tst_QStyleSheetStyle::highdpiImages()
2213{
2214 QFETCH(qreal, screenFactor);
2215 QFETCH(QColor, color);
2216
2217 QWidget w;
2218 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
2219 + QLatin1String(QTest::currentDataTag()));
2220 QScreen *screen = QGuiApplication::primaryScreen();
2221 w.move(screen->availableGeometry().topLeft());
2222 QHighDpiScaling::setScreenFactor(screen, factor: screenFactor);
2223 w.setStyleSheet("QWidget { background-image: url(\":/images/testimage.png\"); }");
2224 w.show();
2225
2226 QVERIFY(QTest::qWaitForWindowExposed(&w));
2227 QImage image(w.size(), QImage::Format_ARGB32);
2228 w.render(target: &image);
2229 QVERIFY(testForColors(image, color));
2230
2231 QHighDpiScaling::setScreenFactor(screen, factor: 1.0);
2232 QHighDpiScaling::updateHighDpiScaling(); // reset to normal
2233}
2234
2235void tst_QStyleSheetStyle::placeholderColor()
2236{
2237 const QColor red(Qt::red);
2238 qApp->setStyleSheet("* { color: red; }");
2239 QLineEdit le1;
2240 QLineEdit le2;
2241 le2.setEnabled(false);
2242 le1.ensurePolished();
2243 QCOMPARE(le1.palette().placeholderText(), red);
2244 le2.ensurePolished();
2245 QCOMPARE(le2.palette().placeholderText(), red);
2246 le2.setEnabled(true);
2247 QCOMPARE(le2.palette().placeholderText(), red);
2248}
2249
2250void tst_QStyleSheetStyle::enumPropertySelector_data()
2251{
2252 QTest::addColumn<QString>(name: "styleSheet");
2253
2254 QTest::addRow(format: "Enum value") << R"(QToolButton[popupMode=MenuButtonPopup] { padding-right: 40px; })";
2255 QTest::addRow(format: "Int value") << R"(QToolButton[popupMode="1"] { padding-right: 40px; })";
2256}
2257
2258void tst_QStyleSheetStyle::enumPropertySelector()
2259{
2260 QFETCH(QString, styleSheet);
2261
2262 QToolButton button;
2263 QMenu menu;
2264 menu.addAction(text: "Action1");
2265 QPixmap pm(50, 50);
2266 pm.fill(fillColor: Qt::red);
2267 button.setIcon(pm);
2268 button.setMenu(&menu);
2269 button.setPopupMode(QToolButton::MenuButtonPopup);
2270
2271 button.show();
2272 const QSize unstyledSizeHint = button.sizeHint();
2273
2274 qApp->setStyleSheet(styleSheet);
2275 const QSize styledSizeHint = button.sizeHint();
2276
2277#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2278 QEXPECT_FAIL("Enum value", "In Qt 5, style sheet selectors have to use integer enum values", Continue);
2279#else
2280 QEXPECT_FAIL("Int value", "In Qt 6, style sheet selectors must use the enum value name", Continue);
2281#endif
2282
2283 QVERIFY(styledSizeHint.width() > unstyledSizeHint.width());
2284}
2285
2286QTEST_MAIN(tst_QStyleSheetStyle)
2287#include "tst_qstylesheetstyle.moc"
2288
2289

source code of qtbase/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp