1/****************************************************************************
2**
3** Copyright (C) 2016 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 "../../../shared/highdpi.h"
30
31#include <qboxlayout.h>
32#include <qapplication.h>
33#include <qbitmap.h>
34#include <qdebug.h>
35#include <qeventloop.h>
36#include <qlabel.h>
37#include <qlayout.h>
38#include <qlineedit.h>
39#include <qlistview.h>
40#include <qmessagebox.h>
41#include <qpainter.h>
42#include <qpoint.h>
43#include <qpushbutton.h>
44#include <qstyle.h>
45#include <qwidget.h>
46#include <qstylefactory.h>
47#include <qdesktopwidget.h>
48#include <private/qwidget_p.h>
49#include <private/qwidgetrepaintmanager_p.h>
50#include <private/qapplication_p.h>
51#include <private/qhighdpiscaling_p.h>
52#include <qcalendarwidget.h>
53#include <qmainwindow.h>
54#include <qdockwidget.h>
55#include <qrandom.h>
56#include <qstylehints.h>
57#include <qtoolbar.h>
58#include <qtoolbutton.h>
59#include <QtCore/qoperatingsystemversion.h>
60#include <QtGui/qpaintengine.h>
61#include <QtGui/qpainterpath.h>
62#include <QtGui/qbackingstore.h>
63#include <QtGui/qguiapplication.h>
64#include <QtGui/qpa/qplatformwindow.h>
65#include <QtGui/qscreen.h>
66#include <qmenubar.h>
67#include <qcompleter.h>
68#include <qtableview.h>
69#include <qtreewidget.h>
70#include <qabstractnativeeventfilter.h>
71#include <qproxystyle.h>
72#include <QtWidgets/QGraphicsView>
73#include <QtWidgets/QGraphicsProxyWidget>
74#include <QtGui/qwindow.h>
75#include <qtimer.h>
76#include <QtWidgets/QDoubleSpinBox>
77
78#if defined(Q_OS_MACOS)
79#include "tst_qwidget_mac_helpers.h" // Abstract the ObjC stuff out so not everyone must run an ObjC++ compile.
80#endif
81
82#include <QtTest/QTest>
83#include <QtTest/private/qtesthelpers_p.h>
84
85using namespace QTestPrivate;
86
87#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
88# include <QtCore/qt_windows.h>
89# include <QtGui/private/qguiapplication_p.h>
90#include <qpa/qplatformnativeinterface.h>
91#include <qpa/qplatformintegration.h>
92
93#include <algorithm>
94
95static HWND winHandleOf(const QWidget *w)
96{
97 static QPlatformNativeInterface *nativeInterface
98 = QGuiApplicationPrivate::platformIntegration()->nativeInterface();
99 if (void *handle = nativeInterface->nativeResourceForWindow("handle", w->window()->windowHandle()))
100 return reinterpret_cast<HWND>(handle);
101 qWarning() << "Cannot obtain native handle for " << w;
102 return nullptr;
103}
104
105# define Q_CHECK_PAINTEVENTS \
106 if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \
107 QSKIP("desktop is not visible, this test would fail");
108
109#else // Q_OS_WIN && !Q_OS_WINRT
110# define Q_CHECK_PAINTEVENTS
111#endif
112
113#ifdef Q_OS_MACOS
114#include <Security/AuthSession.h>
115bool macHasAccessToWindowsServer()
116{
117 SecuritySessionId mySession;
118 SessionAttributeBits sessionInfo;
119 SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo);
120 return (sessionInfo & sessionHasGraphicAccess);
121}
122#endif
123
124#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
125static inline void setWindowsAnimationsEnabled(bool enabled)
126{
127 ANIMATIONINFO animation = { sizeof(ANIMATIONINFO), enabled };
128 SystemParametersInfo(SPI_SETANIMATION, 0, &animation, 0);
129}
130
131static inline bool windowsAnimationsEnabled()
132{
133 ANIMATIONINFO animation = { sizeof(ANIMATIONINFO), 0 };
134 SystemParametersInfo(SPI_GETANIMATION, 0, &animation, 0);
135 return animation.iMinAnimate;
136}
137#else // Q_OS_WIN && !Q_OS_WINRT
138inline void setWindowsAnimationsEnabled(bool) {}
139static inline bool windowsAnimationsEnabled() { return false; }
140#endif // !Q_OS_WIN || Q_OS_WINRT
141
142template <class T>
143static QByteArray msgComparisonFailed(T v1, const char *op, T v2)
144{
145 QString s;
146 QDebug(&s) << v1 << op << v2;
147 return s.toLocal8Bit();
148}
149
150class tst_QWidget : public QObject
151{
152 Q_OBJECT
153
154public:
155 tst_QWidget();
156 virtual ~tst_QWidget();
157
158public slots:
159 void initTestCase();
160 void cleanup();
161private slots:
162 void getSetCheck();
163 void fontPropagation();
164 void fontPropagation2();
165 void fontPropagation3();
166 void fontPropagationDynamic();
167 void palettePropagation();
168 void palettePropagation2();
169 void palettePropagationDynamic();
170 void enabledPropagation();
171 void ignoreKeyEventsWhenDisabled_QTBUG27417();
172 void properTabHandlingWhenDisabled_QTBUG27417();
173#if QT_CONFIG(draganddrop)
174 void acceptDropsPropagation();
175#endif
176 void isEnabledTo();
177 void visible();
178 void visible_setWindowOpacity();
179 void isVisibleTo();
180 void isHidden();
181 void fonts();
182 void mapFromAndTo_data();
183 void mapFromAndTo();
184 void focusChainOnHide();
185 void focusChainOnReparent();
186 void defaultTabOrder();
187 void reverseTabOrder();
188 void tabOrderWithProxy();
189 void tabOrderWithProxyDisabled();
190 void tabOrderWithCompoundWidgets();
191 void tabOrderWithCompoundWidgetsNoFocusPolicy();
192 void tabOrderNoChange();
193 void tabOrderNoChange2();
194 void appFocusWidgetWithFocusProxyLater();
195 void appFocusWidgetWhenLosingFocusProxy();
196 void explicitTabOrderWithComplexWidget();
197 void explicitTabOrderWithSpinBox_QTBUG81097();
198#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
199 void activation();
200#endif
201 void reparent();
202 void windowState();
203 void showMaximized();
204 void showFullScreen();
205 void showMinimized();
206 void showMinimizedKeepsFocus();
207 void icon();
208 void hideWhenFocusWidgetIsChild();
209 void normalGeometry();
210 void setGeometry();
211 void setGeometryHidden();
212 void windowOpacity();
213 void raise();
214 void lower();
215 void stackUnder();
216 void testContentsPropagation();
217 void saveRestoreGeometry();
218 void restoreVersion1Geometry_data();
219 void restoreVersion1Geometry();
220
221 void widgetAt();
222#ifdef Q_OS_MACOS
223 void setMask();
224#endif
225 void optimizedResizeMove();
226 void optimizedResize_topLevel();
227 void resizeEvent();
228 void task110173();
229
230 void testDeletionInEventHandlers();
231
232 void childDeletesItsSibling();
233
234 void setMinimumSize();
235 void setMaximumSize();
236 void setFixedSize();
237
238 void ensureCreated();
239 void createAndDestroy();
240 void winIdChangeEvent();
241 void persistentWinId();
242 void showNativeChild();
243 void transientParent();
244 void qobject_castInDestroyedSlot();
245
246 void showHideEvent_data();
247 void showHideEvent();
248 void showHideEventWhileMinimize();
249 void showHideChildrenWhileMinimize_QTBUG50589();
250
251 void lostUpdatesOnHide();
252
253 void update();
254 void isOpaque();
255
256#ifndef Q_OS_MACOS
257 void scroll();
258 void scrollNativeChildren();
259#endif
260
261 // tests QWidget::setGeometry()
262 void setWindowGeometry_data();
263 void setWindowGeometry();
264
265 // tests QWidget::move() and resize()
266 void windowMoveResize_data();
267 void windowMoveResize();
268
269 void moveChild_data();
270 void moveChild();
271 void showAndMoveChild();
272
273 void subtractOpaqueSiblings();
274
275#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
276 void setGeometry_win();
277#endif
278
279 void setLocale();
280 void propagateLocale();
281 void deleteStyle();
282 void multipleToplevelFocusCheck();
283 void setFocus();
284#ifndef QT_NO_CURSOR
285 void setCursor();
286#endif
287 void setToolTip();
288 void testWindowIconChangeEventPropagation();
289
290 void minAndMaxSizeWithX11BypassWindowManagerHint();
291 void showHideShowX11();
292 void clean_qt_x11_enforce_cursor();
293
294 void childEvents();
295 void render();
296 void renderInvisible();
297 void renderWithPainter();
298 void render_task188133();
299 void render_task211796();
300 void render_task217815();
301 void render_windowOpacity();
302 void render_systemClip();
303 void render_systemClip2_data();
304 void render_systemClip2();
305 void render_systemClip3_data();
306 void render_systemClip3();
307 void render_task252837();
308 void render_worldTransform();
309
310 void setContentsMargins();
311
312 void moveWindowInShowEvent_data();
313 void moveWindowInShowEvent();
314
315 void repaintWhenChildDeleted();
316 void hideOpaqueChildWhileHidden();
317 void updateWhileMinimized();
318 void alienWidgets();
319 void nativeWindowPosition_data();
320 void nativeWindowPosition();
321 void adjustSize();
322 void adjustSize_data();
323 void updateGeometry();
324 void updateGeometry_data();
325 void sendUpdateRequestImmediately();
326 void doubleRepaint();
327 void resizeInPaintEvent();
328 void opaqueChildren();
329
330 void setMaskInResizeEvent();
331 void moveInResizeEvent();
332
333#ifdef QT_BUILD_INTERNAL
334 void immediateRepaintAfterInvalidateBackingStore();
335#endif
336
337 void effectiveWinId();
338 void effectiveWinId2();
339 void customDpi();
340 void customDpiProperty();
341
342 void quitOnCloseAttribute();
343 void moveRect();
344
345#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
346 void gdiPainting();
347 void paintOnScreenPossible();
348#endif
349 void reparentStaticWidget();
350 void QTBUG6883_reparentStaticWidget2();
351
352 void translucentWidget();
353
354 void setClearAndResizeMask();
355 void maskedUpdate();
356#ifndef QT_NO_CURSOR
357 void syntheticEnterLeave();
358 void taskQTBUG_4055_sendSyntheticEnterLeave();
359 void underMouse();
360 void taskQTBUG_27643_enterEvents();
361#endif
362 void windowFlags();
363 void initialPosForDontShowOnScreenWidgets();
364 void updateOnDestroyedSignal();
365 void toplevelLineEditFocus();
366
367 void focusWidget_task254563();
368 void rectOutsideCoordinatesLimit_task144779();
369 void setGraphicsEffect();
370 void render_graphicsEffect_data();
371 void render_graphicsEffect();
372
373#ifdef QT_BUILD_INTERNAL
374 void destroyBackingStore();
375#endif
376
377 void activateWindow();
378
379 void openModal_taskQTBUG_5804();
380
381 void focusProxy();
382 void focusProxyAndInputMethods();
383#ifdef QT_BUILD_INTERNAL
384 void scrollWithoutBackingStore();
385#endif
386
387 void taskQTBUG_7532_tabOrderWithFocusProxy();
388 void movedAndResizedAttributes();
389 void childAt();
390#ifdef Q_OS_MACOS
391 void taskQTBUG_11373();
392#endif
393 void taskQTBUG_17333_ResizeInfiniteRecursion();
394
395 void nativeChildFocus();
396 void grab();
397 void grabMouse();
398 void grabKeyboard();
399
400 void touchEventSynthesizedMouseEvent();
401 void touchUpdateOnNewTouch();
402 void touchEventsForGesturePendingWidgets();
403
404 void styleSheetPropagation();
405
406 void destroyedSignal();
407
408 void keyboardModifiers();
409 void mouseDoubleClickBubbling_QTBUG29680();
410 void largerThanScreen_QTBUG30142();
411
412 void resizeStaticContentsChildWidget_QTBUG35282();
413
414 void qmlSetParentHelper();
415
416 void testForOutsideWSRangeFlag();
417
418 void tabletTracking();
419
420 void closeEvent();
421 void closeWithChildWindow();
422
423 void winIdAfterClose();
424 void receivesLanguageChangeEvent();
425 void deleteWindowInCloseEvent();
426
427 void activateWhileModalHidden();
428
429private:
430 bool ensureScreenSize(int width, int height);
431
432 const QString m_platform;
433 QSize m_testWidgetSize;
434 QPoint m_availableTopLeft;
435 QPoint m_safeCursorPos;
436 const bool m_windowsAnimationsEnabled;
437 QTouchDevice *m_touchScreen;
438 const int m_fuzz;
439};
440
441bool tst_QWidget::ensureScreenSize(int width, int height)
442{
443 const QSize available = QGuiApplication::primaryScreen()->availableGeometry().size();
444 return (available.width() >= width && available.height() >= height);
445}
446
447// Testing get/set functions
448void tst_QWidget::getSetCheck()
449{
450 QWidget obj1;
451 QWidget child1(&obj1);
452 // QStyle * QWidget::style()
453 // void QWidget::setStyle(QStyle *)
454 QScopedPointer<QStyle> var1(QStyleFactory::create(QLatin1String("Windows")));
455 obj1.setStyle(var1.data());
456 QCOMPARE(static_cast<QStyle *>(var1.data()), obj1.style());
457 obj1.setStyle(nullptr);
458 QVERIFY(var1.data() != obj1.style());
459 QVERIFY(obj1.style() != nullptr); // style can never be 0 for a widget
460
461 // int QWidget::minimumWidth()
462 // void QWidget::setMinimumWidth(int)
463 obj1.setMinimumWidth(0);
464 QCOMPARE(obj1.minimumWidth(), 0);
465 obj1.setMinimumWidth(INT_MIN);
466 QCOMPARE(obj1.minimumWidth(), 0); // A widgets width can never be less than 0
467 obj1.setMinimumWidth(INT_MAX);
468
469 child1.setMinimumWidth(0);
470 QCOMPARE(child1.minimumWidth(), 0);
471 child1.setMinimumWidth(INT_MIN);
472 QCOMPARE(child1.minimumWidth(), 0); // A widgets width can never be less than 0
473 child1.setMinimumWidth(INT_MAX);
474 QCOMPARE(child1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium
475
476 // int QWidget::minimumHeight()
477 // void QWidget::setMinimumHeight(int)
478 obj1.setMinimumHeight(0);
479 QCOMPARE(obj1.minimumHeight(), 0);
480 obj1.setMinimumHeight(INT_MIN);
481 QCOMPARE(obj1.minimumHeight(), 0); // A widgets height can never be less than 0
482 obj1.setMinimumHeight(INT_MAX);
483
484 child1.setMinimumHeight(0);
485 QCOMPARE(child1.minimumHeight(), 0);
486 child1.setMinimumHeight(INT_MIN);
487 QCOMPARE(child1.minimumHeight(), 0); // A widgets height can never be less than 0
488 child1.setMinimumHeight(INT_MAX);
489 QCOMPARE(child1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium
490
491 // int QWidget::maximumWidth()
492 // void QWidget::setMaximumWidth(int)
493 obj1.setMaximumWidth(0);
494 QCOMPARE(obj1.maximumWidth(), 0);
495 obj1.setMaximumWidth(INT_MIN);
496 QCOMPARE(obj1.maximumWidth(), 0); // A widgets width can never be less than 0
497 obj1.setMaximumWidth(INT_MAX);
498 QCOMPARE(obj1.maximumWidth(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX
499
500 // int QWidget::maximumHeight()
501 // void QWidget::setMaximumHeight(int)
502 obj1.setMaximumHeight(0);
503 QCOMPARE(obj1.maximumHeight(), 0);
504 obj1.setMaximumHeight(INT_MIN);
505 QCOMPARE(obj1.maximumHeight(), 0); // A widgets height can never be less than 0
506 obj1.setMaximumHeight(INT_MAX);
507 QCOMPARE(obj1.maximumHeight(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX
508
509 // back to normal
510 obj1.setMinimumWidth(0);
511 obj1.setMinimumHeight(0);
512 obj1.setMaximumWidth(QWIDGETSIZE_MAX);
513 obj1.setMaximumHeight(QWIDGETSIZE_MAX);
514
515 // const QPalette & QWidget::palette()
516 // void QWidget::setPalette(const QPalette &)
517 QPalette var6;
518 obj1.setPalette(var6);
519 QCOMPARE(var6, obj1.palette());
520 obj1.setPalette(QPalette());
521 QCOMPARE(QPalette(), obj1.palette());
522
523 // const QFont & QWidget::font()
524 // void QWidget::setFont(const QFont &)
525 QFont var7;
526 obj1.setFont(var7);
527 QCOMPARE(var7, obj1.font());
528 obj1.setFont(QFont());
529 QCOMPARE(QFont(), obj1.font());
530
531 // qreal QWidget::windowOpacity()
532 // void QWidget::setWindowOpacity(qreal)
533 obj1.setWindowOpacity(0.0);
534 QCOMPARE(0.0, obj1.windowOpacity());
535 obj1.setWindowOpacity(1.1);
536 QCOMPARE(1.0, obj1.windowOpacity()); // 1.0 is the fullest opacity possible
537
538 // QWidget * QWidget::focusProxy()
539 // void QWidget::setFocusProxy(QWidget *)
540 {
541 QScopedPointer<QWidget> var9(new QWidget());
542 obj1.setFocusProxy(var9.data());
543 QCOMPARE(var9.data(), obj1.focusProxy());
544 obj1.setFocusProxy(nullptr);
545 QCOMPARE(nullptr, obj1.focusProxy());
546 }
547
548 // const QRect & QWidget::geometry()
549 // void QWidget::setGeometry(const QRect &)
550 QCoreApplication::processEvents();
551 QRect var10(10, 10, 100, 100);
552 obj1.setGeometry(var10);
553 QCoreApplication::processEvents();
554 qDebug() << obj1.geometry();
555 QCOMPARE(var10, obj1.geometry());
556 obj1.setGeometry(QRect(0,0,0,0));
557 qDebug() << obj1.geometry();
558 QCOMPARE(QRect(0,0,0,0), obj1.geometry());
559
560 // QLayout * QWidget::layout()
561 // void QWidget::setLayout(QLayout *)
562 QBoxLayout *var11 = new QBoxLayout(QBoxLayout::LeftToRight);
563 obj1.setLayout(var11);
564 QCOMPARE(static_cast<QLayout *>(var11), obj1.layout());
565 obj1.setLayout(nullptr);
566 QCOMPARE(static_cast<QLayout *>(var11), obj1.layout()); // You cannot set a 0-pointer layout, that keeps the current
567 delete var11; // This will remove the layout from the widget
568 QCOMPARE(nullptr, obj1.layout());
569
570 // bool QWidget::acceptDrops()
571 // void QWidget::setAcceptDrops(bool)
572 obj1.setAcceptDrops(false);
573 QCOMPARE(false, obj1.acceptDrops());
574 obj1.setAcceptDrops(true);
575 QCOMPARE(true, obj1.acceptDrops());
576
577 // bool QWidget::autoFillBackground()
578 // void QWidget::setAutoFillBackground(bool)
579 obj1.setAutoFillBackground(false);
580 QCOMPARE(false, obj1.autoFillBackground());
581 obj1.setAutoFillBackground(true);
582 QCOMPARE(true, obj1.autoFillBackground());
583
584 var1.reset();
585#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
586 obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
587 const HWND handle = reinterpret_cast<HWND>(obj1.winId()); // explicitly create window handle
588 QVERIFY(GetWindowLong(handle, GWL_STYLE) & LONG(WS_POPUP));
589#endif
590}
591
592tst_QWidget::tst_QWidget()
593 : m_platform(QGuiApplication::platformName().toLower())
594 , m_safeCursorPos(0, 0)
595 , m_windowsAnimationsEnabled(windowsAnimationsEnabled())
596 , m_touchScreen(QTest::createTouchDevice())
597 , m_fuzz(int(QGuiApplication::primaryScreen()->devicePixelRatio()))
598{
599 if (m_windowsAnimationsEnabled) // Disable animations which can interfere with screen grabbing in moveChild(), showAndMoveChild()
600 setWindowsAnimationsEnabled(false);
601 QFont font;
602 font.setBold(true);
603 font.setPointSize(42);
604 QApplication::setFont(font, className: "QPropagationTestWidget");
605
606 QPalette palette;
607 palette.setColor(acr: QPalette::ToolTipBase, acolor: QColor(12, 13, 14));
608 palette.setColor(acr: QPalette::Text, acolor: QColor(21, 22, 23));
609 QApplication::setPalette(palette, className: "QPropagationTestWidget");
610}
611
612tst_QWidget::~tst_QWidget()
613{
614 if (m_windowsAnimationsEnabled)
615 setWindowsAnimationsEnabled(m_windowsAnimationsEnabled);
616}
617
618void tst_QWidget::initTestCase()
619{
620 // Size of reference widget, 200 for < 2000, scale up for larger screens
621 // to avoid Windows warnings about minimum size for decorated windows.
622 int width = 200;
623 const QScreen *screen = QGuiApplication::primaryScreen();
624 const QRect availableGeometry = screen->availableGeometry();
625 m_availableTopLeft = availableGeometry.topLeft();
626 // XCB: Determine "safe" cursor position at bottom/right corner of screen.
627 // Pushing the mouse rapidly to the top left corner can trigger KDE / KWin's
628 // "Present all Windows" (Ctrl+F9) feature also programmatically.
629 if (m_platform == QLatin1String("xcb"))
630 m_safeCursorPos = availableGeometry.bottomRight() - QPoint(40, 40);
631 const int screenWidth = screen->geometry().width();
632 if (screenWidth > 2000)
633 width = 100 * ((screenWidth + 500) / 1000);
634 m_testWidgetSize = QSize(width, width);
635}
636
637void tst_QWidget::cleanup()
638{
639 QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty());
640}
641
642void tst_QWidget::fontPropagation()
643{
644 QScopedPointer<QWidget> testWidget(new QWidget);
645 testWidget->resize(m_testWidgetSize);
646 testWidget->setWindowTitle(__FUNCTION__);
647 centerOnScreen(w: testWidget.data());
648 testWidget->show();
649 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
650 QFont font = testWidget->font();
651 QWidget* childWidget = new QWidget( testWidget.data() );
652 childWidget->show();
653 QCOMPARE( font, childWidget->font() );
654
655 font.setBold( true );
656 testWidget->setFont( font );
657 QCOMPARE( font, testWidget->font() );
658 QCOMPARE( font, childWidget->font() );
659
660 QFont newFont = font;
661 newFont.setItalic( true );
662 childWidget->setFont( newFont );
663 QWidget* grandChildWidget = new QWidget( childWidget );
664 QCOMPARE( font, testWidget->font() );
665 QCOMPARE( newFont, grandChildWidget->font() );
666
667 font.setUnderline( true );
668 testWidget->setFont( font );
669
670 // the child and grand child should now have merged bold and
671 // underline
672 newFont.setUnderline( true );
673
674 QCOMPARE( newFont, childWidget->font() );
675 QCOMPARE( newFont, grandChildWidget->font() );
676
677 // make sure font propagation continues working after reparenting
678 font = testWidget->font();
679 font.setPointSize(font.pointSize() + 2);
680 testWidget->setFont(font);
681
682 QWidget *one = new QWidget(testWidget.data());
683 QWidget *two = new QWidget(one);
684 QWidget *three = new QWidget(two);
685 QWidget *four = new QWidget(two);
686
687 four->setParent(three);
688 four->move(QPoint(0,0));
689
690 font.setPointSize(font.pointSize() + 2);
691 testWidget->setFont(font);
692
693 QCOMPARE(testWidget->font(), one->font());
694 QCOMPARE(one->font(), two->font());
695 QCOMPARE(two->font(), three->font());
696 QCOMPARE(three->font(), four->font());
697
698 QVERIFY(testWidget->testAttribute(Qt::WA_SetFont));
699 QVERIFY(! one->testAttribute(Qt::WA_SetFont));
700 QVERIFY(! two->testAttribute(Qt::WA_SetFont));
701 QVERIFY(! three->testAttribute(Qt::WA_SetFont));
702 QVERIFY(! four->testAttribute(Qt::WA_SetFont));
703
704 font.setPointSize(font.pointSize() + 2);
705 one->setFont(font);
706
707 QCOMPARE(one->font(), two->font());
708 QCOMPARE(two->font(), three->font());
709 QCOMPARE(three->font(), four->font());
710
711 QVERIFY(one->testAttribute(Qt::WA_SetFont));
712 QVERIFY(! two->testAttribute(Qt::WA_SetFont));
713 QVERIFY(! three->testAttribute(Qt::WA_SetFont));
714 QVERIFY(! four->testAttribute(Qt::WA_SetFont));
715
716 font.setPointSize(font.pointSize() + 2);
717 two->setFont(font);
718
719 QCOMPARE(two->font(), three->font());
720 QCOMPARE(three->font(), four->font());
721
722 QVERIFY(two->testAttribute(Qt::WA_SetFont));
723 QVERIFY(! three->testAttribute(Qt::WA_SetFont));
724 QVERIFY(! four->testAttribute(Qt::WA_SetFont));
725
726 font.setPointSize(font.pointSize() + 2);
727 three->setFont(font);
728
729 QCOMPARE(three->font(), four->font());
730
731 QVERIFY(three->testAttribute(Qt::WA_SetFont));
732 QVERIFY(! four->testAttribute(Qt::WA_SetFont));
733
734 font.setPointSize(font.pointSize() + 2);
735 four->setFont(font);
736
737 QVERIFY(four->testAttribute(Qt::WA_SetFont));
738}
739
740class QPropagationTestWidget : public QWidget
741{
742 Q_OBJECT
743public:
744 using QWidget::QWidget;
745};
746
747void tst_QWidget::fontPropagation2()
748{
749 // ! Note, the code below is executed in tst_QWidget's constructor.
750 // QFont font;
751 // font.setBold(true);
752 // font.setPointSize(42);
753 // QApplication::setFont(font, "QPropagationTestWidget");
754
755 QScopedPointer<QWidget> root(new QWidget);
756 root->setObjectName(QLatin1String("fontPropagation2"));
757 root->setWindowTitle(root->objectName());
758 root->resize(w: 200, h: 200);
759
760 QWidget *child0 = new QWidget(root.data());
761 QWidget *child1 = new QWidget(child0);
762 QWidget *child2 = new QPropagationTestWidget(child1);
763 QWidget *child3 = new QWidget(child2);
764 QWidget *child4 = new QWidget(child3);
765 QWidget *child5 = new QWidget(child4);
766 root->show();
767
768 // Check that only the application fonts apply.
769 QCOMPARE(root->font(), QApplication::font());
770 QCOMPARE(child0->font(), QApplication::font());
771 QCOMPARE(child1->font(), QApplication::font());
772 QCOMPARE(child2->font().pointSize(), 42);
773 QVERIFY(child2->font().bold());
774 QCOMPARE(child3->font().pointSize(), 42);
775 QVERIFY(child3->font().bold());
776 QCOMPARE(child4->font().pointSize(), 42);
777 QVERIFY(child4->font().bold());
778 QCOMPARE(child5->font().pointSize(), 42);
779 QVERIFY(child5->font().bold());
780
781 // Set child0's font size to 15, and remove bold on child4.
782 QFont font;
783 font.setPointSize(15);
784 child0->setFont(font);
785 QFont unboldFont;
786 unboldFont.setBold(false);
787 child4->setFont(unboldFont);
788
789 // Check that the above settings propagate correctly.
790 QCOMPARE(root->font(), QApplication::font());
791 QCOMPARE(child0->font().pointSize(), 15);
792 QVERIFY(!child0->font().bold());
793 QCOMPARE(child1->font().pointSize(), 15);
794 QVERIFY(!child1->font().bold());
795 QCOMPARE(child2->font().pointSize(), 15);
796 QVERIFY(child2->font().bold());
797 QCOMPARE(child3->font().pointSize(), 15);
798 QVERIFY(child3->font().bold());
799 QCOMPARE(child4->font().pointSize(), 15);
800 QVERIFY(!child4->font().bold());
801 QCOMPARE(child5->font().pointSize(), 15);
802 QVERIFY(!child5->font().bold());
803
804 // Replace the app font for child2. Italic should propagate
805 // but the size should still be ignored. The previous bold
806 // setting is gone.
807 QFont italicSizeFont;
808 italicSizeFont.setItalic(true);
809 italicSizeFont.setPointSize(33);
810 QApplication::setFont(italicSizeFont, className: "QPropagationTestWidget");
811
812 // Check that this propagates correctly.
813 QCOMPARE(root->font(), QApplication::font());
814 QCOMPARE(child0->font().pointSize(), 15);
815 QVERIFY(!child0->font().bold());
816 QVERIFY(!child0->font().italic());
817 QCOMPARE(child1->font().pointSize(), 15);
818 QVERIFY(!child1->font().bold());
819 QVERIFY(!child1->font().italic());
820 QCOMPARE(child2->font().pointSize(), 15);
821 QVERIFY(!child2->font().bold());
822 QVERIFY(child2->font().italic());
823 QCOMPARE(child3->font().pointSize(), 15);
824 QVERIFY(!child3->font().bold());
825 QVERIFY(child3->font().italic());
826 QCOMPARE(child4->font().pointSize(), 15);
827 QVERIFY(!child4->font().bold());
828 QVERIFY(child4->font().italic());
829 QCOMPARE(child5->font().pointSize(), 15);
830 QVERIFY(!child5->font().bold());
831 QVERIFY(child5->font().italic());
832}
833
834void tst_QWidget::fontPropagation3()
835{
836 QWidget parent;
837 QWidget *child = new QWidget(&parent);
838 parent.setFont(QFont("Monospace", 9));
839 QImage image(32, 32, QImage::Format_RGB32);
840 QPainter p(&image);
841 p.setFont(child->font());
842 QCOMPARE(p.font().family(), child->font().family());
843 QCOMPARE(p.font().pointSize(), child->font().pointSize());
844}
845
846/*!
847 This tests that children that are added to a widget with an explicitly
848 defined font inherit that font correctly, merging (and overriding)
849 with the font that might be defined by the platform theme.
850*/
851void tst_QWidget::fontPropagationDynamic()
852{
853 // override side effects from previous tests
854 QFont themedFont;
855 themedFont.setBold(true);
856 themedFont.setPointSize(42);
857 QApplication::setFont(themedFont, className: "QPropagationTestWidget");
858
859 QWidget parent;
860 QWidget firstChild(&parent);
861
862 const QFont defaultFont = parent.font();
863 QFont appFont = defaultFont;
864 appFont.setPointSize(72);
865
866 // sanity check
867 QVERIFY(themedFont != defaultFont);
868 QVERIFY(themedFont != appFont);
869
870 // palette propagates to existing children
871 parent.setFont(appFont);
872 QCOMPARE(firstChild.font().pointSize(), appFont.pointSize());
873
874 // palatte propagates to children added later
875 QWidget secondChild(&parent);
876 QCOMPARE(secondChild.font().pointSize(), appFont.pointSize());
877 QWidget thirdChild;
878 QCOMPARE(thirdChild.font().pointSize(), defaultFont.pointSize());
879 thirdChild.setParent(&parent);
880 QCOMPARE(thirdChild.font().pointSize(), appFont.pointSize());
881
882 // even if the child has an override in QApplication::font
883 QPropagationTestWidget themedChild;
884 themedChild.ensurePolished(); // needed for default font to be set up
885 QCOMPARE(themedChild.font().pointSize(), themedFont.pointSize());
886 QCOMPARE(themedChild.font().bold(), themedFont.bold());
887 themedChild.setParent(&parent);
888 QCOMPARE(themedChild.font().pointSize(), appFont.pointSize());
889 QCOMPARE(themedChild.font().bold(), themedFont.bold());
890
891 // grand children as well
892 QPropagationTestWidget themedGrandChild;
893 themedGrandChild.setParent(&themedChild);
894 QCOMPARE(themedGrandChild.font().pointSize(), appFont.pointSize());
895 QCOMPARE(themedGrandChild.font().bold(), themedFont.bold());
896
897 // child with own font attribute does not inherit from parent
898 QFont childFont = defaultFont;
899 childFont.setPointSize(9);
900 QWidget modifiedChild;
901 modifiedChild.setFont(childFont);
902 modifiedChild.setParent(&parent);
903 QCOMPARE(modifiedChild.font().pointSize(), childFont.pointSize());
904}
905
906void tst_QWidget::palettePropagation()
907{
908 QScopedPointer<QWidget> testWidget(new QWidget);
909 testWidget->resize(m_testWidgetSize);
910 testWidget->setWindowTitle(__FUNCTION__);
911 centerOnScreen(w: testWidget.data());
912 testWidget->show();
913 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
914
915 QPalette palette = testWidget->palette();
916 QWidget* childWidget = new QWidget( testWidget.data() );
917 childWidget->show();
918 QCOMPARE( palette, childWidget->palette() );
919
920 palette.setColor( acr: QPalette::Base, acolor: Qt::red );
921 testWidget->setPalette( palette );
922 QCOMPARE( palette, testWidget->palette() );
923 QCOMPARE( palette, childWidget->palette() );
924
925 QPalette newPalette = palette;
926 newPalette.setColor( acr: QPalette::Highlight, acolor: Qt::green );
927 childWidget->setPalette( newPalette );
928 QWidget* grandChildWidget = new QWidget( childWidget );
929 QCOMPARE( palette, testWidget->palette() );
930 QCOMPARE( newPalette, grandChildWidget->palette() );
931
932 palette.setColor( acr: QPalette::Text, acolor: Qt::blue );
933 testWidget->setPalette( palette );
934
935 // the child and grand child should now have merged green
936 // highlight and blue text
937 newPalette.setColor( acr: QPalette::Text, acolor: Qt::blue);
938
939 QCOMPARE( newPalette, childWidget->palette() );
940 QCOMPARE( newPalette, grandChildWidget->palette() );
941}
942
943void tst_QWidget::palettePropagation2()
944{
945 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
946 QSKIP("Wayland: This fails. Figure out why.");
947
948 // ! Note, the code below is executed in tst_QWidget's constructor.
949 // QPalette palette;
950 // palette.setColor(QPalette::ToolTipBase, QColor(12, 13, 14));
951 // palette.setColor(QPalette::Text, QColor(21, 22, 23));
952 // qApp->setPalette(palette, "QPropagationTestWidget");
953
954 QScopedPointer<QWidget> root(new QWidget);
955 root->setObjectName(QLatin1String("palettePropagation2"));
956 root->setWindowTitle(root->objectName());
957 root->resize(w: 200, h: 200);
958 QWidget *child0 = new QWidget(root.data());
959 QWidget *child1 = new QWidget(child0);
960 QWidget *child2 = new QPropagationTestWidget(child1);
961 QWidget *child3 = new QWidget(child2);
962 QWidget *child4 = new QWidget(child3);
963 QWidget *child5 = new QWidget(child4);
964 root->show();
965 QVERIFY(QTest::qWaitForWindowExposed(root.data()));
966
967 // These colors are unlikely to be imposed on the default palette of
968 // QWidget ;-).
969 QColor sysPalText(21, 22, 23);
970 QColor sysPalToolTipBase(12, 13, 14);
971 QColor overridePalText(42, 43, 44);
972 QColor overridePalToolTipBase(45, 46, 47);
973 QColor sysPalButton(99, 98, 97);
974
975 // Check that only the application fonts apply.
976 QPalette appPal = QApplication::palette();
977 QCOMPARE(root->palette(), appPal);
978 QCOMPARE(child0->palette(), appPal);
979 QCOMPARE(child1->palette(), appPal);
980 QCOMPARE(child2->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
981 QCOMPARE(child2->palette().color(QPalette::Text), sysPalText);
982 QCOMPARE(child2->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
983 QCOMPARE(child3->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
984 QCOMPARE(child3->palette().color(QPalette::Text), sysPalText);
985 QCOMPARE(child3->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
986 QCOMPARE(child4->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
987 QCOMPARE(child4->palette().color(QPalette::Text), sysPalText);
988 QCOMPARE(child4->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
989 QCOMPARE(child5->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
990 QCOMPARE(child5->palette().color(QPalette::Text), sysPalText);
991 QCOMPARE(child5->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
992
993 // Set child0's Text, and set ToolTipBase on child4.
994 QPalette textPalette;
995 textPalette.setColor(acr: QPalette::Text, acolor: overridePalText);
996 child0->setPalette(textPalette);
997 QPalette toolTipPalette;
998 toolTipPalette.setColor(acr: QPalette::ToolTipBase, acolor: overridePalToolTipBase);
999 child4->setPalette(toolTipPalette);
1000
1001 // Check that the above settings propagate correctly.
1002 QCOMPARE(root->palette(), appPal);
1003 QCOMPARE(child0->palette().color(QPalette::Text), overridePalText);
1004 QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1005 QCOMPARE(child0->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1006 QCOMPARE(child1->palette().color(QPalette::Text), overridePalText);
1007 QCOMPARE(child1->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1008 QCOMPARE(child1->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1009 QCOMPARE(child2->palette().color(QPalette::Text), overridePalText);
1010 QCOMPARE(child2->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
1011 QCOMPARE(child2->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1012 QCOMPARE(child3->palette().color(QPalette::Text), overridePalText);
1013 QCOMPARE(child3->palette().color(QPalette::ToolTipBase), sysPalToolTipBase);
1014 QCOMPARE(child3->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1015 QCOMPARE(child4->palette().color(QPalette::Text), overridePalText);
1016 QCOMPARE(child4->palette().color(QPalette::ToolTipBase), overridePalToolTipBase);
1017 QCOMPARE(child4->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1018 QCOMPARE(child5->palette().color(QPalette::Text), overridePalText);
1019 QCOMPARE(child5->palette().color(QPalette::ToolTipBase), overridePalToolTipBase);
1020 QCOMPARE(child5->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1021
1022 // Replace the app palette for child2. Button should propagate but Text
1023 // should still be ignored. The previous ToolTipBase setting is gone.
1024 QPalette buttonPalette;
1025 buttonPalette.setColor(acr: QPalette::ToolTipText, acolor: sysPalButton);
1026 QApplication::setPalette(buttonPalette, className: "QPropagationTestWidget");
1027
1028 // Check that the above settings propagate correctly.
1029 QCOMPARE(root->palette(), appPal);
1030 QCOMPARE(child0->palette().color(QPalette::Text), overridePalText);
1031 QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1032 QCOMPARE(child0->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1033 QCOMPARE(child1->palette().color(QPalette::Text), overridePalText);
1034 QCOMPARE(child1->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1035 QCOMPARE(child1->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText));
1036 QCOMPARE(child2->palette().color(QPalette::Text), overridePalText);
1037 QCOMPARE(child2->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1038 QCOMPARE(child2->palette().color(QPalette::ToolTipText), sysPalButton);
1039 QCOMPARE(child3->palette().color(QPalette::Text), overridePalText);
1040 QCOMPARE(child3->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase));
1041 QCOMPARE(child3->palette().color(QPalette::ToolTipText), sysPalButton);
1042 QCOMPARE(child4->palette().color(QPalette::Text), overridePalText);
1043 QCOMPARE(child4->palette().color(QPalette::ToolTipBase), overridePalToolTipBase);
1044 QCOMPARE(child4->palette().color(QPalette::ToolTipText), sysPalButton);
1045 QCOMPARE(child5->palette().color(QPalette::Text), overridePalText);
1046 QCOMPARE(child5->palette().color(QPalette::ToolTipBase), overridePalToolTipBase);
1047 QCOMPARE(child5->palette().color(QPalette::ToolTipText), sysPalButton);
1048}
1049
1050/*!
1051 This tests that children that are added to a widget with an explicitly
1052 defined palette inherit that palette correctly, merging (and overriding)
1053 with the palette that might be defined by the platform theme.
1054*/
1055void tst_QWidget::palettePropagationDynamic()
1056{
1057 // override side effects from previous tests
1058 QPalette themedPalette;
1059 themedPalette.setColor(acr: QPalette::ToolTipBase, acolor: QColor(12, 13, 14));
1060 themedPalette.setColor(acr: QPalette::Text, acolor: QColor(21, 22, 23));
1061 QApplication::setPalette(themedPalette, className: "QPropagationTestWidget");
1062
1063 QWidget parent;
1064 QWidget firstChild(&parent);
1065
1066 const QPalette defaultPalette = parent.palette();
1067 QPalette appPalette = defaultPalette;
1068 const QColor appColor(1, 2, 3);
1069 appPalette.setColor(acr: QPalette::Text, acolor: appColor);
1070
1071 // sanity check
1072 QVERIFY(themedPalette != defaultPalette);
1073 QVERIFY(themedPalette != appPalette);
1074
1075 // palette propagates to existing children
1076 parent.setPalette(appPalette);
1077 QCOMPARE(firstChild.palette().color(QPalette::Text), appPalette.color(QPalette::Text));
1078
1079 // palatte propagates to children added later
1080 QWidget secondChild(&parent);
1081 QCOMPARE(secondChild.palette().color(QPalette::Text), appPalette.color(QPalette::Text));
1082 QWidget thirdChild;
1083 QCOMPARE(thirdChild.palette().color(QPalette::Text), defaultPalette.color(QPalette::Text));
1084 thirdChild.setParent(&parent);
1085 QCOMPARE(thirdChild.palette().color(QPalette::Text), appPalette.color(QPalette::Text));
1086
1087 // even if the child has an override in QApplication::palette
1088 QPropagationTestWidget themedChild;
1089 themedChild.ensurePolished(); // needed for default palette to be set up
1090 QCOMPARE(themedChild.palette().color(QPalette::Text), themedPalette.color(QPalette::Text));
1091 QCOMPARE(themedChild.palette().color(QPalette::ToolTipBase), themedPalette.color(QPalette::ToolTipBase));
1092 themedChild.setParent(&parent);
1093 QCOMPARE(themedChild.palette().color(QPalette::Text), appPalette.color(QPalette::Text));
1094 QCOMPARE(themedChild.palette().color(QPalette::ToolTipBase), themedPalette.color(QPalette::ToolTipBase));
1095
1096 // grand children as well
1097 QPropagationTestWidget themedGrandChild;
1098 themedGrandChild.setParent(&themedChild);
1099 QCOMPARE(themedGrandChild.palette().color(QPalette::Text), appPalette.color(QPalette::Text));
1100 QCOMPARE(themedGrandChild.palette().color(QPalette::ToolTipBase), themedPalette.color(QPalette::ToolTipBase));
1101
1102 // child with own color does not inherit from parent
1103 QPalette childPalette = defaultPalette;
1104 childPalette.setColor(acr: QPalette::Text, acolor: Qt::red);
1105 QWidget modifiedChild;
1106 modifiedChild.setPalette(childPalette);
1107 modifiedChild.setParent(&parent);
1108 QCOMPARE(modifiedChild.palette().color(QPalette::Text), childPalette.color(QPalette::Text));
1109
1110}
1111
1112void tst_QWidget::enabledPropagation()
1113{
1114 QScopedPointer<QWidget> testWidget(new QWidget);
1115 testWidget->resize(m_testWidgetSize);
1116 testWidget->setWindowTitle(__FUNCTION__);
1117 centerOnScreen(w: testWidget.data());
1118 testWidget->show();
1119 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
1120 QWidget* childWidget = new QWidget( testWidget.data() );
1121 childWidget->show();
1122 QVERIFY( testWidget->isEnabled() );
1123 QVERIFY( childWidget->isEnabled() );
1124
1125 testWidget->setEnabled( false );
1126 QVERIFY( !testWidget->isEnabled() );
1127 QVERIFY( !childWidget->isEnabled() );
1128
1129 testWidget->setDisabled( false );
1130 QVERIFY( testWidget->isEnabled() );
1131 QVERIFY( childWidget->isEnabled() );
1132
1133 QWidget* grandChildWidget = new QWidget( childWidget );
1134 QVERIFY( grandChildWidget->isEnabled() );
1135
1136 testWidget->setDisabled( true );
1137 QVERIFY( !testWidget->isEnabled() );
1138 QVERIFY( !childWidget->isEnabled() );
1139 QVERIFY( !grandChildWidget->isEnabled() );
1140
1141 grandChildWidget->setEnabled( false );
1142 testWidget->setEnabled( true );
1143 QVERIFY( testWidget->isEnabled() );
1144 QVERIFY( childWidget->isEnabled() );
1145 QVERIFY( !grandChildWidget->isEnabled() );
1146
1147 grandChildWidget->setEnabled( true );
1148 testWidget->setEnabled( false );
1149 childWidget->setDisabled( true );
1150 testWidget->setEnabled( true );
1151 QVERIFY( testWidget->isEnabled() );
1152 QVERIFY( !childWidget->isEnabled() );
1153 QVERIFY( !grandChildWidget->isEnabled() );
1154}
1155
1156void tst_QWidget::ignoreKeyEventsWhenDisabled_QTBUG27417()
1157{
1158 QLineEdit lineEdit;
1159 lineEdit.setWindowTitle(__FUNCTION__);
1160 lineEdit.setMinimumWidth(m_testWidgetSize.width());
1161 centerOnScreen(w: &lineEdit);
1162 lineEdit.setDisabled(true);
1163 lineEdit.show();
1164 QTest::keyClick(widget: &lineEdit, key: Qt::Key_A);
1165 QTRY_VERIFY(lineEdit.text().isEmpty());
1166}
1167
1168void tst_QWidget::properTabHandlingWhenDisabled_QTBUG27417()
1169{
1170 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1171 QSKIP("Wayland: This fails. Figure out why.");
1172
1173 QWidget widget;
1174 widget.setWindowTitle(__FUNCTION__);
1175 widget.setMinimumWidth(m_testWidgetSize.width());
1176 centerOnScreen(w: &widget);
1177 QVBoxLayout *layout = new QVBoxLayout();
1178 QLineEdit *lineEdit = new QLineEdit();
1179 layout->addWidget(lineEdit);
1180 QLineEdit *lineEdit2 = new QLineEdit();
1181 layout->addWidget(lineEdit2);
1182 QLineEdit *lineEdit3 = new QLineEdit();
1183 layout->addWidget(lineEdit3);
1184 widget.setLayout(layout);
1185 widget.show();
1186
1187 lineEdit->setFocus();
1188 QTRY_VERIFY(lineEdit->hasFocus());
1189 QTest::keyClick(widget: &widget, key: Qt::Key_Tab);
1190 QTRY_VERIFY(lineEdit2->hasFocus());
1191 QTest::keyClick(widget: &widget, key: Qt::Key_Tab);
1192 QTRY_VERIFY(lineEdit3->hasFocus());
1193
1194 lineEdit2->setDisabled(true);
1195 lineEdit->setFocus();
1196 QTRY_VERIFY(lineEdit->hasFocus());
1197 QTest::keyClick(widget: &widget, key: Qt::Key_Tab);
1198 QTRY_VERIFY(!lineEdit2->hasFocus());
1199 QVERIFY(lineEdit3->hasFocus());
1200}
1201
1202// Drag'n drop disabled in this build.
1203#if QT_CONFIG(draganddrop)
1204void tst_QWidget::acceptDropsPropagation()
1205{
1206 QScopedPointer<QWidget> testWidget(new QWidget);
1207 testWidget->resize(m_testWidgetSize);
1208 testWidget->setWindowTitle(__FUNCTION__);
1209 centerOnScreen(w: testWidget.data());
1210 testWidget->show();
1211 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
1212 QWidget *childWidget = new QWidget(testWidget.data());
1213 childWidget->show();
1214 QVERIFY(!testWidget->acceptDrops());
1215 QVERIFY(!childWidget->acceptDrops());
1216
1217 testWidget->setAcceptDrops(true);
1218 QVERIFY(testWidget->acceptDrops());
1219 QVERIFY(!childWidget->acceptDrops());
1220 QVERIFY(childWidget->testAttribute(Qt::WA_DropSiteRegistered));
1221
1222 testWidget->setAcceptDrops(false);
1223 QVERIFY(!testWidget->acceptDrops());
1224 QVERIFY(!childWidget->acceptDrops());
1225 QVERIFY(!childWidget->testAttribute(Qt::WA_DropSiteRegistered));
1226
1227 QWidget *grandChildWidget = new QWidget(childWidget);
1228 QVERIFY(!grandChildWidget->acceptDrops());
1229 QVERIFY(!grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered));
1230
1231 testWidget->setAcceptDrops(true);
1232 QVERIFY(testWidget->acceptDrops());
1233 QVERIFY(!childWidget->acceptDrops());
1234 QVERIFY(childWidget->testAttribute(Qt::WA_DropSiteRegistered));
1235 QVERIFY(!grandChildWidget->acceptDrops());
1236 QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered));
1237
1238 grandChildWidget->setAcceptDrops(true);
1239 testWidget->setAcceptDrops(false);
1240 QVERIFY(!testWidget->acceptDrops());
1241 QVERIFY(!childWidget->acceptDrops());
1242 QVERIFY(grandChildWidget->acceptDrops());
1243 QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered));
1244
1245 grandChildWidget->setAcceptDrops(false);
1246 QVERIFY(!grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered));
1247 testWidget->setAcceptDrops(true);
1248 childWidget->setAcceptDrops(true);
1249 testWidget->setAcceptDrops(false);
1250 QVERIFY(!testWidget->acceptDrops());
1251 QVERIFY(childWidget->acceptDrops());
1252 QVERIFY(!grandChildWidget->acceptDrops());
1253 QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered));
1254}
1255#endif
1256
1257void tst_QWidget::isEnabledTo()
1258{
1259 QWidget testWidget;
1260 testWidget.resize(m_testWidgetSize);
1261 testWidget.setWindowTitle(__FUNCTION__);
1262 centerOnScreen(w: &testWidget);
1263 testWidget.show();
1264 QWidget* childWidget = new QWidget( &testWidget );
1265 QWidget* grandChildWidget = new QWidget( childWidget );
1266
1267 QVERIFY( childWidget->isEnabledTo( &testWidget ) );
1268 QVERIFY( grandChildWidget->isEnabledTo( &testWidget ) );
1269
1270 childWidget->setEnabled( false );
1271 QVERIFY( !childWidget->isEnabledTo( &testWidget ) );
1272 QVERIFY( grandChildWidget->isEnabledTo( childWidget ) );
1273 QVERIFY( !grandChildWidget->isEnabledTo( &testWidget ) );
1274
1275 QScopedPointer<QMainWindow> childDialog(new QMainWindow(&testWidget));
1276 testWidget.setEnabled(false);
1277 QVERIFY(!childDialog->isEnabled());
1278 QVERIFY(childDialog->isEnabledTo(nullptr));
1279}
1280
1281void tst_QWidget::visible()
1282{
1283 // Ensure that the testWidget is hidden for this test at the
1284 // start
1285 QScopedPointer<QWidget> testWidget(new QWidget);
1286 testWidget->resize(m_testWidgetSize);
1287 testWidget->setWindowTitle(__FUNCTION__);
1288 centerOnScreen(w: testWidget.data());
1289 QVERIFY( !testWidget->isVisible() );
1290 QWidget* childWidget = new QWidget( testWidget.data() );
1291 QVERIFY( !childWidget->isVisible() );
1292
1293 testWidget->show();
1294 QVERIFY(testWidget->screen());
1295 QVERIFY( testWidget->isVisible() );
1296 QVERIFY( childWidget->isVisible() );
1297
1298 QWidget* grandChildWidget = new QWidget( childWidget );
1299 QVERIFY( !grandChildWidget->isVisible() );
1300 grandChildWidget->show();
1301 QVERIFY( grandChildWidget->isVisible() );
1302
1303 grandChildWidget->hide();
1304 testWidget->hide();
1305 testWidget->show();
1306 QVERIFY( !grandChildWidget->isVisible() );
1307 QVERIFY( testWidget->isVisible() );
1308 QVERIFY( childWidget->isVisible() );
1309
1310 grandChildWidget->show();
1311 childWidget->hide();
1312 testWidget->hide();
1313 testWidget->show();
1314 QVERIFY( testWidget->isVisible() );
1315 QVERIFY( !childWidget->isVisible() );
1316 QVERIFY( !grandChildWidget->isVisible() );
1317
1318 grandChildWidget->show();
1319 QVERIFY( !grandChildWidget->isVisible() );
1320}
1321
1322void tst_QWidget::setLocale()
1323{
1324 QWidget w;
1325 QCOMPARE(w.locale(), QLocale());
1326
1327 w.setLocale(QLocale::Italian);
1328 QCOMPARE(w.locale(), QLocale(QLocale::Italian));
1329
1330 QWidget child1(&w);
1331 QCOMPARE(child1.locale(), QLocale(QLocale::Italian));
1332
1333 w.unsetLocale();
1334 QCOMPARE(w.locale(), QLocale());
1335 QCOMPARE(child1.locale(), QLocale());
1336
1337 w.setLocale(QLocale::French);
1338 QCOMPARE(w.locale(), QLocale(QLocale::French));
1339 QCOMPARE(child1.locale(), QLocale(QLocale::French));
1340
1341 child1.setLocale(QLocale::Italian);
1342 QCOMPARE(w.locale(), QLocale(QLocale::French));
1343 QCOMPARE(child1.locale(), QLocale(QLocale::Italian));
1344
1345 child1.unsetLocale();
1346 QCOMPARE(w.locale(), QLocale(QLocale::French));
1347 QCOMPARE(child1.locale(), QLocale(QLocale::French));
1348
1349 QWidget child2;
1350 QCOMPARE(child2.locale(), QLocale());
1351 child2.setParent(&w);
1352 QCOMPARE(child2.locale(), QLocale(QLocale::French));
1353}
1354
1355void tst_QWidget::propagateLocale()
1356{
1357 QWidget parent;
1358 parent.setLocale(QLocale::French);
1359 // Non-window widget; propagates locale:
1360 QWidget *child = new QWidget(&parent);
1361 QVERIFY(!child->isWindow());
1362 QVERIFY(!child->testAttribute(Qt::WA_WindowPropagation));
1363 QCOMPARE(child->locale(), QLocale(QLocale::French));
1364 parent.setLocale(QLocale::Italian);
1365 QCOMPARE(child->locale(), QLocale(QLocale::Italian));
1366 delete child;
1367 // Window: doesn't propagate locale:
1368 child = new QWidget(&parent, Qt::Window);
1369 QVERIFY(child->isWindow());
1370 QVERIFY(!child->testAttribute(Qt::WA_WindowPropagation));
1371 QCOMPARE(child->locale(), QLocale());
1372 parent.setLocale(QLocale::French);
1373 QCOMPARE(child->locale(), QLocale());
1374 // ... unless we tell it to:
1375 child->setAttribute(Qt::WA_WindowPropagation, on: true);
1376 QVERIFY(child->testAttribute(Qt::WA_WindowPropagation));
1377 QCOMPARE(child->locale(), QLocale(QLocale::French));
1378 parent.setLocale(QLocale::Italian);
1379 QCOMPARE(child->locale(), QLocale(QLocale::Italian));
1380}
1381
1382void tst_QWidget::visible_setWindowOpacity()
1383{
1384 QScopedPointer<QWidget> testWidget(new QWidget);
1385 testWidget->resize(m_testWidgetSize);
1386 testWidget->setWindowTitle(__FUNCTION__);
1387 centerOnScreen(w: testWidget.data());
1388 testWidget->winId();
1389
1390 QVERIFY( !testWidget->isVisible() );
1391 testWidget->setWindowOpacity(0.5);
1392#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1393 QVERIFY(!::IsWindowVisible(winHandleOf(testWidget.data())));
1394#endif
1395 testWidget->setWindowOpacity(1.0);
1396}
1397
1398void tst_QWidget::isVisibleTo()
1399{
1400 // Ensure that the testWidget is hidden for this test at the
1401 // start
1402 QWidget testWidget;
1403 testWidget.resize(m_testWidgetSize);
1404 testWidget.setWindowTitle(__FUNCTION__);
1405 centerOnScreen(w: &testWidget);
1406
1407 QWidget* childWidget = new QWidget( &testWidget );
1408 QVERIFY( childWidget->isVisibleTo( &testWidget ) );
1409 childWidget->hide();
1410 QVERIFY( !childWidget->isVisibleTo( &testWidget ) );
1411
1412 QWidget* grandChildWidget = new QWidget( childWidget );
1413 QVERIFY( !grandChildWidget->isVisibleTo( &testWidget ) );
1414 QVERIFY( grandChildWidget->isVisibleTo( childWidget ) );
1415
1416 testWidget.show();
1417 childWidget->show();
1418
1419 QVERIFY( childWidget->isVisibleTo( &testWidget ) );
1420 grandChildWidget->hide();
1421 QVERIFY( !grandChildWidget->isVisibleTo( childWidget ) );
1422 QVERIFY( !grandChildWidget->isVisibleTo( &testWidget ) );
1423}
1424
1425void tst_QWidget::isHidden()
1426{
1427 // Ensure that the testWidget is hidden for this test at the
1428 // start
1429 QScopedPointer<QWidget> testWidget(new QWidget);
1430 testWidget->resize(m_testWidgetSize);
1431 testWidget->setWindowTitle(__FUNCTION__);
1432 centerOnScreen(w: testWidget.data());
1433
1434 QVERIFY( testWidget->isHidden() );
1435 QWidget* childWidget = new QWidget( testWidget.data() );
1436 QVERIFY( !childWidget->isHidden() );
1437
1438 testWidget->show();
1439 QVERIFY( !testWidget->isHidden() );
1440 QVERIFY( !childWidget->isHidden() );
1441
1442 QWidget* grandChildWidget = new QWidget( childWidget );
1443 QVERIFY( grandChildWidget->isHidden() );
1444 grandChildWidget->show();
1445 QVERIFY( !grandChildWidget->isHidden() );
1446
1447 grandChildWidget->hide();
1448 testWidget->hide();
1449 testWidget->show();
1450 QVERIFY( grandChildWidget->isHidden() );
1451 QVERIFY( !testWidget->isHidden() );
1452 QVERIFY( !childWidget->isHidden() );
1453
1454 grandChildWidget->show();
1455 childWidget->hide();
1456 testWidget->hide();
1457 testWidget->show();
1458 QVERIFY( !testWidget->isHidden() );
1459 QVERIFY( childWidget->isHidden() );
1460 QVERIFY( !grandChildWidget->isHidden() );
1461
1462 grandChildWidget->show();
1463 QVERIFY( !grandChildWidget->isHidden() );
1464}
1465
1466void tst_QWidget::fonts()
1467{
1468 QWidget testWidget;
1469 testWidget.resize(m_testWidgetSize);
1470 testWidget.setWindowTitle(__FUNCTION__);
1471 centerOnScreen(w: &testWidget);
1472 testWidget.show();
1473 QVERIFY(QTest::qWaitForWindowExposed(&testWidget));
1474
1475 // Tests setFont(), ownFont() and unsetFont()
1476 QWidget* cleanTestWidget = new QWidget( &testWidget );
1477 QFont originalFont = cleanTestWidget->font();
1478
1479 QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) );
1480 cleanTestWidget->setFont(QFont());
1481 QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) );
1482
1483 QFont newFont( "times", 18 );
1484 cleanTestWidget->setFont( newFont );
1485 newFont = newFont.resolve( testWidget.font() );
1486
1487 QVERIFY( cleanTestWidget->testAttribute(Qt::WA_SetFont) );
1488 QVERIFY2( cleanTestWidget->font() == newFont,
1489 msgComparisonFailed(cleanTestWidget->font(), "==", newFont));
1490
1491 cleanTestWidget->setFont(QFont());
1492 QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) );
1493 QVERIFY2( cleanTestWidget->font() == originalFont,
1494 msgComparisonFailed(cleanTestWidget->font(), "==", originalFont));
1495}
1496
1497void tst_QWidget::mapFromAndTo_data()
1498{
1499 QTest::addColumn<bool>(name: "windowHidden");
1500 QTest::addColumn<bool>(name: "subWindow1Hidden");
1501 QTest::addColumn<bool>(name: "subWindow2Hidden");
1502 QTest::addColumn<bool>(name: "subSubWindowHidden");
1503 QTest::addColumn<bool>(name: "windowMinimized");
1504 QTest::addColumn<bool>(name: "subWindow1Minimized");
1505
1506 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 1") << false << false << false << false << false << false;
1507 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 1") << true << false << false << false << false << false;
1508 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 1") << false << true << false << false << false << false;
1509 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 1") << true << true << false << false << false << false;
1510 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 1") << false << false << true << false << false << false;
1511 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 1") << true << false << true << false << false << false;
1512 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 1") << false << true << true << false << false << false;
1513 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 1") << true << true << true << false << false << false;
1514 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 0") << false << false << false << true << false << false;
1515 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 0") << true << false << false << true << false << false;
1516 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 0") << false << true << false << true << false << false;
1517 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 0") << true << true << false << true << false << false;
1518 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 0") << false << false << true << true << false << false;
1519 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 0") << true << false << true << true << false << false;
1520 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 0") << false << true << true << true << false << false;
1521 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 0") << true << true << true << true << false << false;
1522 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 1 windowMinimized") << false << false << false << false << true << false;
1523 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 1 windowMinimized") << true << false << false << false << true << false;
1524 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 1 windowMinimized") << false << true << false << false << true << false;
1525 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 1 windowMinimized") << true << true << false << false << true << false;
1526 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 1 windowMinimized") << false << false << true << false << true << false;
1527 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 1 windowMinimized") << true << false << true << false << true << false;
1528 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 1 windowMinimized") << false << true << true << false << true << false;
1529 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 1 windowMinimized") << true << true << true << false << true << false;
1530 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 0 windowMinimized") << false << false << false << true << true << false;
1531 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 0 windowMinimized") << true << false << false << true << true << false;
1532 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 0 windowMinimized") << false << true << false << true << true << false;
1533 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 0 windowMinimized") << true << true << false << true << true << false;
1534 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 0 windowMinimized") << false << false << true << true << true << false;
1535 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 0 windowMinimized") << true << false << true << true << true << false;
1536 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 0 windowMinimized") << false << true << true << true << true << false;
1537 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 0 windowMinimized") << true << true << true << true << true << false;
1538 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 1 subWindow1Minimized") << false << false << false << false << false << true;
1539 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 1 subWindow1Minimized") << true << false << false << false << false << true;
1540 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 1 subWindow1Minimized") << false << true << false << false << false << true;
1541 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 1 subWindow1Minimized") << true << true << false << false << false << true;
1542 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 1 subWindow1Minimized") << false << false << true << false << false << true;
1543 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 1 subWindow1Minimized") << true << false << true << false << false << true;
1544 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 1 subWindow1Minimized") << false << true << true << false << false << true;
1545 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 1 subWindow1Minimized") << true << true << true << false << false << true;
1546 QTest::newRow(dataTag: "window 1 sub1 1 sub2 1 subsub 0 subWindow1Minimized") << false << false << false << true << false << true;
1547 QTest::newRow(dataTag: "window 0 sub1 1 sub2 1 subsub 0 subWindow1Minimized") << true << false << false << true << false << true;
1548 QTest::newRow(dataTag: "window 1 sub1 0 sub2 1 subsub 0 subWindow1Minimized") << false << true << false << true << false << true;
1549 QTest::newRow(dataTag: "window 0 sub1 0 sub2 1 subsub 0 subWindow1Minimized") << true << true << false << true << false << true;
1550 QTest::newRow(dataTag: "window 1 sub1 1 sub2 0 subsub 0 subWindow1Minimized") << false << false << true << true << false << true;
1551 QTest::newRow(dataTag: "window 0 sub1 1 sub2 0 subsub 0 subWindow1Minimized") << true << false << true << true << false << true;
1552 QTest::newRow(dataTag: "window 1 sub1 0 sub2 0 subsub 0 subWindow1Minimized") << false << true << true << true << false << true;
1553 QTest::newRow(dataTag: "window 0 sub1 0 sub2 0 subsub 0 subWindow1Minimized") << true << true << true << true << false << true;
1554
1555
1556}
1557
1558void tst_QWidget::mapFromAndTo()
1559{
1560 QFETCH(bool, windowHidden);
1561 QFETCH(bool, subWindow1Hidden);
1562 QFETCH(bool, subWindow2Hidden);
1563 QFETCH(bool, subSubWindowHidden);
1564 QFETCH(bool, windowMinimized);
1565 QFETCH(bool, subWindow1Minimized);
1566
1567 // create a toplevel and two overlapping siblings
1568 QWidget window;
1569 window.setObjectName(QStringLiteral("mapFromAndTo"));
1570 window.setWindowTitle(window.objectName());
1571 window.setWindowFlags(window.windowFlags() | Qt::X11BypassWindowManagerHint);
1572 QWidget *subWindow1 = new QWidget(&window);
1573 QWidget *subWindow2 = new QWidget(&window);
1574 QWidget *subSubWindow = new QWidget(subWindow1);
1575
1576 // set their geometries
1577 window.setGeometry(ax: 100, ay: 100, aw: 100, ah: 100);
1578 subWindow1->setGeometry(ax: 50, ay: 50, aw: 100, ah: 100);
1579 subWindow2->setGeometry(ax: 75, ay: 75, aw: 100, ah: 100);
1580 subSubWindow->setGeometry(ax: 10, ay: 10, aw: 10, ah: 10);
1581
1582#if !defined(Q_OS_QNX) && !defined(Q_OS_WINRT)
1583 //update visibility
1584 if (windowMinimized) {
1585 if (!windowHidden) {
1586 window.showMinimized();
1587 QVERIFY(window.isMinimized());
1588 }
1589 } else {
1590 window.setVisible(!windowHidden);
1591 }
1592 if (subWindow1Minimized) {
1593 subWindow1->hide();
1594 subWindow1->showMinimized();
1595 QVERIFY(subWindow1->isMinimized());
1596 } else {
1597 subWindow1->setVisible(!subWindow1Hidden);
1598 }
1599#else
1600 Q_UNUSED(windowHidden);
1601 Q_UNUSED(subWindow1Hidden);
1602 Q_UNUSED(windowMinimized);
1603 Q_UNUSED(subWindow1Minimized);
1604#endif
1605
1606 subWindow2->setVisible(!subWindow2Hidden);
1607 subSubWindow->setVisible(!subSubWindowHidden);
1608
1609 // window
1610 QCOMPARE(window.mapToGlobal(QPoint(0, 0)), QPoint(100, 100));
1611 QCOMPARE(window.mapToGlobal(QPoint(10, 0)), QPoint(110, 100));
1612 QCOMPARE(window.mapToGlobal(QPoint(0, 10)), QPoint(100, 110));
1613 QCOMPARE(window.mapToGlobal(QPoint(-10, 0)), QPoint(90, 100));
1614 QCOMPARE(window.mapToGlobal(QPoint(0, -10)), QPoint(100, 90));
1615 QCOMPARE(window.mapToGlobal(QPoint(100, 100)), QPoint(200, 200));
1616 QCOMPARE(window.mapToGlobal(QPoint(110, 100)), QPoint(210, 200));
1617 QCOMPARE(window.mapToGlobal(QPoint(100, 110)), QPoint(200, 210));
1618 QCOMPARE(window.mapFromGlobal(QPoint(100, 100)), QPoint(0, 0));
1619 QCOMPARE(window.mapFromGlobal(QPoint(110, 100)), QPoint(10, 0));
1620 QCOMPARE(window.mapFromGlobal(QPoint(100, 110)), QPoint(0, 10));
1621 QCOMPARE(window.mapFromGlobal(QPoint(90, 100)), QPoint(-10, 0));
1622 QCOMPARE(window.mapFromGlobal(QPoint(100, 90)), QPoint(0, -10));
1623 QCOMPARE(window.mapFromGlobal(QPoint(200, 200)), QPoint(100, 100));
1624 QCOMPARE(window.mapFromGlobal(QPoint(210, 200)), QPoint(110, 100));
1625 QCOMPARE(window.mapFromGlobal(QPoint(200, 210)), QPoint(100, 110));
1626 QCOMPARE(window.mapToParent(QPoint(0, 0)), QPoint(100, 100));
1627 QCOMPARE(window.mapToParent(QPoint(10, 0)), QPoint(110, 100));
1628 QCOMPARE(window.mapToParent(QPoint(0, 10)), QPoint(100, 110));
1629 QCOMPARE(window.mapToParent(QPoint(-10, 0)), QPoint(90, 100));
1630 QCOMPARE(window.mapToParent(QPoint(0, -10)), QPoint(100, 90));
1631 QCOMPARE(window.mapToParent(QPoint(100, 100)), QPoint(200, 200));
1632 QCOMPARE(window.mapToParent(QPoint(110, 100)), QPoint(210, 200));
1633 QCOMPARE(window.mapToParent(QPoint(100, 110)), QPoint(200, 210));
1634 QCOMPARE(window.mapFromParent(QPoint(100, 100)), QPoint(0, 0));
1635 QCOMPARE(window.mapFromParent(QPoint(110, 100)), QPoint(10, 0));
1636 QCOMPARE(window.mapFromParent(QPoint(100, 110)), QPoint(0, 10));
1637 QCOMPARE(window.mapFromParent(QPoint(90, 100)), QPoint(-10, 0));
1638 QCOMPARE(window.mapFromParent(QPoint(100, 90)), QPoint(0, -10));
1639 QCOMPARE(window.mapFromParent(QPoint(200, 200)), QPoint(100, 100));
1640 QCOMPARE(window.mapFromParent(QPoint(210, 200)), QPoint(110, 100));
1641 QCOMPARE(window.mapFromParent(QPoint(200, 210)), QPoint(100, 110));
1642
1643 // first subwindow
1644 QCOMPARE(subWindow1->mapToGlobal(QPoint(0, 0)), QPoint(150, 150));
1645 QCOMPARE(subWindow1->mapToGlobal(QPoint(10, 0)), QPoint(160, 150));
1646 QCOMPARE(subWindow1->mapToGlobal(QPoint(0, 10)), QPoint(150, 160));
1647 QCOMPARE(subWindow1->mapToGlobal(QPoint(-10, 0)), QPoint(140, 150));
1648 QCOMPARE(subWindow1->mapToGlobal(QPoint(0, -10)), QPoint(150, 140));
1649 QCOMPARE(subWindow1->mapToGlobal(QPoint(100, 100)), QPoint(250, 250));
1650 QCOMPARE(subWindow1->mapToGlobal(QPoint(110, 100)), QPoint(260, 250));
1651 QCOMPARE(subWindow1->mapToGlobal(QPoint(100, 110)), QPoint(250, 260));
1652 QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 150)), QPoint(0, 0));
1653 QCOMPARE(subWindow1->mapFromGlobal(QPoint(160, 150)), QPoint(10, 0));
1654 QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 160)), QPoint(0, 10));
1655 QCOMPARE(subWindow1->mapFromGlobal(QPoint(140, 150)), QPoint(-10, 0));
1656 QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 140)), QPoint(0, -10));
1657 QCOMPARE(subWindow1->mapFromGlobal(QPoint(250, 250)), QPoint(100, 100));
1658 QCOMPARE(subWindow1->mapFromGlobal(QPoint(260, 250)), QPoint(110, 100));
1659 QCOMPARE(subWindow1->mapFromGlobal(QPoint(250, 260)), QPoint(100, 110));
1660 QCOMPARE(subWindow1->mapToParent(QPoint(0, 0)), QPoint(50, 50));
1661 QCOMPARE(subWindow1->mapToParent(QPoint(10, 0)), QPoint(60, 50));
1662 QCOMPARE(subWindow1->mapToParent(QPoint(0, 10)), QPoint(50, 60));
1663 QCOMPARE(subWindow1->mapToParent(QPoint(-10, 0)), QPoint(40, 50));
1664 QCOMPARE(subWindow1->mapToParent(QPoint(0, -10)), QPoint(50, 40));
1665 QCOMPARE(subWindow1->mapToParent(QPoint(100, 100)), QPoint(150, 150));
1666 QCOMPARE(subWindow1->mapToParent(QPoint(110, 100)), QPoint(160, 150));
1667 QCOMPARE(subWindow1->mapToParent(QPoint(100, 110)), QPoint(150, 160));
1668 QCOMPARE(subWindow1->mapFromParent(QPoint(50, 50)), QPoint(0, 0));
1669 QCOMPARE(subWindow1->mapFromParent(QPoint(60, 50)), QPoint(10, 0));
1670 QCOMPARE(subWindow1->mapFromParent(QPoint(50, 60)), QPoint(0, 10));
1671 QCOMPARE(subWindow1->mapFromParent(QPoint(40, 50)), QPoint(-10, 0));
1672 QCOMPARE(subWindow1->mapFromParent(QPoint(50, 40)), QPoint(0, -10));
1673 QCOMPARE(subWindow1->mapFromParent(QPoint(150, 150)), QPoint(100, 100));
1674 QCOMPARE(subWindow1->mapFromParent(QPoint(160, 150)), QPoint(110, 100));
1675 QCOMPARE(subWindow1->mapFromParent(QPoint(150, 160)), QPoint(100, 110));
1676
1677 // subsubwindow
1678 QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, 0)), QPoint(160, 160));
1679 QCOMPARE(subSubWindow->mapToGlobal(QPoint(10, 0)), QPoint(170, 160));
1680 QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, 10)), QPoint(160, 170));
1681 QCOMPARE(subSubWindow->mapToGlobal(QPoint(-10, 0)), QPoint(150, 160));
1682 QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, -10)), QPoint(160, 150));
1683 QCOMPARE(subSubWindow->mapToGlobal(QPoint(100, 100)), QPoint(260, 260));
1684 QCOMPARE(subSubWindow->mapToGlobal(QPoint(110, 100)), QPoint(270, 260));
1685 QCOMPARE(subSubWindow->mapToGlobal(QPoint(100, 110)), QPoint(260, 270));
1686 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 160)), QPoint(0, 0));
1687 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(170, 160)), QPoint(10, 0));
1688 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 170)), QPoint(0, 10));
1689 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(150, 160)), QPoint(-10, 0));
1690 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 150)), QPoint(0, -10));
1691 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(260, 260)), QPoint(100, 100));
1692 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(270, 260)), QPoint(110, 100));
1693 QCOMPARE(subSubWindow->mapFromGlobal(QPoint(260, 270)), QPoint(100, 110));
1694 QCOMPARE(subSubWindow->mapToParent(QPoint(0, 0)), QPoint(10, 10));
1695 QCOMPARE(subSubWindow->mapToParent(QPoint(10, 0)), QPoint(20, 10));
1696 QCOMPARE(subSubWindow->mapToParent(QPoint(0, 10)), QPoint(10, 20));
1697 QCOMPARE(subSubWindow->mapToParent(QPoint(-10, 0)), QPoint(0, 10));
1698 QCOMPARE(subSubWindow->mapToParent(QPoint(0, -10)), QPoint(10, 0));
1699 QCOMPARE(subSubWindow->mapToParent(QPoint(100, 100)), QPoint(110, 110));
1700 QCOMPARE(subSubWindow->mapToParent(QPoint(110, 100)), QPoint(120, 110));
1701 QCOMPARE(subSubWindow->mapToParent(QPoint(100, 110)), QPoint(110, 120));
1702 QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 10)), QPoint(0, 0));
1703 QCOMPARE(subSubWindow->mapFromParent(QPoint(20, 10)), QPoint(10, 0));
1704 QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 20)), QPoint(0, 10));
1705 QCOMPARE(subSubWindow->mapFromParent(QPoint(0, 10)), QPoint(-10, 0));
1706 QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 0)), QPoint(0, -10));
1707 QCOMPARE(subSubWindow->mapFromParent(QPoint(110, 110)), QPoint(100, 100));
1708 QCOMPARE(subSubWindow->mapFromParent(QPoint(120, 110)), QPoint(110, 100));
1709 QCOMPARE(subSubWindow->mapFromParent(QPoint(110, 120)), QPoint(100, 110));
1710}
1711
1712void tst_QWidget::focusChainOnReparent()
1713{
1714 QWidget window;
1715 QWidget *child1 = new QWidget(&window);
1716 QWidget *child2 = new QWidget(&window);
1717 QWidget *child3 = new QWidget(&window);
1718 QWidget *child21 = new QWidget(child2);
1719 QWidget *child22 = new QWidget(child2);
1720 QWidget *child4 = new QWidget(&window);
1721
1722 QWidget *expectedOriginalChain[8] = {&window, child1, child2, child3, child21, child22, child4, &window};
1723 QWidget *w = &window;
1724 for (auto expectedOriginal : expectedOriginalChain) {
1725 QCOMPARE(w, expectedOriginal);
1726 w = w->nextInFocusChain();
1727 }
1728 for (int i = 7; i >= 0; --i) {
1729 w = w->previousInFocusChain();
1730 QCOMPARE(w, expectedOriginalChain[i]);
1731 }
1732
1733 QWidget window2;
1734 child2->setParent(&window2);
1735
1736 QWidget *expectedNewChain[5] = {&window2, child2, child21, child22, &window2};
1737 w = &window2;
1738 for (auto expectedNew : expectedNewChain) {
1739 QCOMPARE(w, expectedNew);
1740 w = w->nextInFocusChain();
1741 }
1742 for (int i = 4; i >= 0; --i) {
1743 w = w->previousInFocusChain();
1744 QCOMPARE(w, expectedNewChain[i]);
1745 }
1746
1747 QWidget *expectedOldChain[5] = {&window, child1, child3, child4, &window};
1748 w = &window;
1749 for (auto expectedOld : expectedOldChain) {
1750 QCOMPARE(w, expectedOld);
1751 w = w->nextInFocusChain();
1752 }
1753 for (int i = 4; i >= 0; --i) {
1754 w = w->previousInFocusChain();
1755 QCOMPARE(w, expectedOldChain[i]);
1756 }
1757}
1758
1759
1760void tst_QWidget::focusChainOnHide()
1761{
1762 // focus should move to the next widget in the focus chain when we hide it.
1763 QScopedPointer<QWidget> parent(new QWidget());
1764 parent->setObjectName(QLatin1String("focusChainOnHide"));
1765 parent->resize(w: 200, h: 200);
1766 parent->setWindowTitle(parent->objectName());
1767 parent->setFocusPolicy(Qt::StrongFocus);
1768 QWidget *child = new QWidget(parent.data());
1769 child->setObjectName(QLatin1String("child"));
1770 child->setFocusPolicy(Qt::StrongFocus);
1771 QWidget::setTabOrder(child, parent.data());
1772
1773 parent->show();
1774 QApplication::setActiveWindow(parent->window());
1775 child->activateWindow();
1776 child->setFocus();
1777
1778 QTRY_VERIFY(child->hasFocus());
1779 child->hide();
1780
1781 QTRY_VERIFY(parent->hasFocus());
1782 QCOMPARE(parent.data(), QApplication::focusWidget());
1783}
1784
1785class Container : public QWidget
1786{
1787public:
1788 QVBoxLayout* box;
1789
1790 Container()
1791 {
1792 box = new QVBoxLayout(this);
1793 //(new QVBoxLayout(this))->setAutoAdd(true);
1794 }
1795
1796 void tab()
1797 {
1798 focusNextPrevChild(next: true);
1799 }
1800
1801 void backTab()
1802 {
1803 focusNextPrevChild(next: false);
1804 }
1805};
1806
1807class Composite : public QFrame
1808{
1809public:
1810 explicit Composite(QWidget *parent = nullptr, const QString &name = QString())
1811 : QFrame(parent)
1812 {
1813 setObjectName(name);
1814
1815 lineEdit1 = new QLineEdit;
1816 lineEdit2 = new QLineEdit;
1817 lineEdit3 = new QLineEdit;
1818 lineEdit3->setEnabled(false);
1819
1820 QHBoxLayout* hbox = new QHBoxLayout(this);
1821 hbox->addWidget(lineEdit1);
1822 hbox->addWidget(lineEdit2);
1823 hbox->addWidget(lineEdit3);
1824 }
1825
1826public:
1827 QLineEdit *lineEdit1;
1828 QLineEdit *lineEdit2;
1829 QLineEdit *lineEdit3;
1830};
1831
1832void tst_QWidget::defaultTabOrder()
1833{
1834 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1835 QSKIP("Wayland: This fails. Figure out why.");
1836
1837 const int compositeCount = 2;
1838 Container container;
1839 Composite *composite[compositeCount];
1840
1841 QLineEdit *firstEdit = new QLineEdit;
1842 container.box->addWidget(firstEdit);
1843
1844 for (int i = 0; i < compositeCount; i++) {
1845 composite[i] = new Composite();
1846 container.box->addWidget(composite[i]);
1847 }
1848
1849 QLineEdit *lastEdit = new QLineEdit();
1850 container.box->addWidget(lastEdit);
1851
1852 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1853 container.show();
1854 container.activateWindow();
1855 QApplication::setActiveWindow(&container);
1856 QVERIFY(QTest::qWaitForWindowActive(&container));
1857
1858 QTRY_VERIFY(firstEdit->hasFocus());
1859
1860 // Check that focus moves between the line edits when we tab forward
1861 for (int i = 0; i < compositeCount; ++i) {
1862 container.tab();
1863 QVERIFY(composite[i]->lineEdit1->hasFocus());
1864 QVERIFY(!composite[i]->lineEdit2->hasFocus());
1865 container.tab();
1866 QVERIFY(!composite[i]->lineEdit1->hasFocus());
1867 QVERIFY(composite[i]->lineEdit2->hasFocus());
1868 }
1869
1870 container.tab();
1871 QVERIFY(lastEdit->hasFocus());
1872
1873 // Check that focus moves between the line edits in reverse
1874 // order when we tab backwards
1875 for (int i = compositeCount - 1; i >= 0; --i) {
1876 container.backTab();
1877 QVERIFY(!composite[i]->lineEdit1->hasFocus());
1878 QVERIFY(composite[i]->lineEdit2->hasFocus());
1879
1880 container.backTab();
1881 QVERIFY(composite[i]->lineEdit1->hasFocus());
1882 QVERIFY(!composite[i]->lineEdit2->hasFocus());
1883 }
1884
1885 container.backTab();
1886 QVERIFY(firstEdit->hasFocus());
1887}
1888
1889void tst_QWidget::reverseTabOrder()
1890{
1891 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1892 QSKIP("Wayland: This fails. Figure out why.");
1893
1894 const int compositeCount = 2;
1895 Container container;
1896 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1897 Composite* composite[compositeCount];
1898
1899 QLineEdit *firstEdit = new QLineEdit();
1900 container.box->addWidget(firstEdit);
1901
1902 for (int i = 0; i < compositeCount; i++) {
1903 composite[i] = new Composite();
1904 container.box->addWidget(composite[i]);
1905 }
1906
1907 QLineEdit *lastEdit = new QLineEdit();
1908 container.box->addWidget(lastEdit);
1909
1910 // Reverse tab order inside each composite
1911 for (int i = 0; i < compositeCount; ++i)
1912 QWidget::setTabOrder(composite[i]->lineEdit2, composite[i]->lineEdit1);
1913
1914 container.show();
1915 container.activateWindow();
1916 QApplication::setActiveWindow(&container);
1917 QVERIFY(QTest::qWaitForWindowActive(&container));
1918
1919 QTRY_VERIFY(firstEdit->hasFocus());
1920
1921 // Check that focus moves in reverse order when tabbing inside the composites
1922 // (but in the correct order when tabbing between them)
1923 for (int i = 0; i < compositeCount; ++i) {
1924 container.tab();
1925 QVERIFY(!composite[i]->lineEdit1->hasFocus());
1926 QVERIFY(composite[i]->lineEdit2->hasFocus());
1927 container.tab();
1928 QVERIFY(composite[i]->lineEdit1->hasFocus());
1929 QVERIFY(!composite[i]->lineEdit2->hasFocus());
1930 }
1931
1932 container.tab();
1933 QVERIFY(lastEdit->hasFocus());
1934
1935 // Check that focus moves in "normal" order when tabbing backwards inside the
1936 // composites (since backwards of reversed order cancels each other out),
1937 // but in the reverse order when tabbing between them.
1938 for (int i = compositeCount - 1; i >= 0; --i) {
1939 container.backTab();
1940 QVERIFY(composite[i]->lineEdit1->hasFocus());
1941 QVERIFY(!composite[i]->lineEdit2->hasFocus());
1942 container.backTab();
1943 QVERIFY(!composite[i]->lineEdit1->hasFocus());
1944 QVERIFY(composite[i]->lineEdit2->hasFocus());
1945 }
1946
1947 container.backTab();
1948 QVERIFY(firstEdit->hasFocus());
1949}
1950
1951void tst_QWidget::tabOrderWithProxy()
1952{
1953 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1954 QSKIP("Wayland: This fails. Figure out why.");
1955
1956 const int compositeCount = 2;
1957 Container container;
1958 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1959 Composite* composite[compositeCount];
1960
1961 QLineEdit *firstEdit = new QLineEdit();
1962 container.box->addWidget(firstEdit);
1963
1964 for (int i = 0; i < compositeCount; i++) {
1965 composite[i] = new Composite();
1966 container.box->addWidget(composite[i]);
1967
1968 // Set second child as focus proxy
1969 composite[i]->setFocusPolicy(Qt::StrongFocus);
1970 composite[i]->setFocusProxy(composite[i]->lineEdit2);
1971 }
1972
1973 QLineEdit *lastEdit = new QLineEdit();
1974 container.box->addWidget(lastEdit);
1975
1976 container.show();
1977 container.activateWindow();
1978 QApplication::setActiveWindow(&container);
1979 QVERIFY(QTest::qWaitForWindowActive(&container));
1980
1981 QTRY_VERIFY(firstEdit->hasFocus());
1982
1983 // Check that focus moves between the second line edits
1984 // (the focus proxies) when we tab forward
1985 for (int i = 0; i < compositeCount; ++i) {
1986 container.tab();
1987 QVERIFY(!composite[i]->lineEdit1->hasFocus());
1988 QVERIFY(composite[i]->lineEdit2->hasFocus());
1989 }
1990
1991 container.tab();
1992 QVERIFY(lastEdit->hasFocus());
1993
1994 // Check that focus moves between the line edits
1995 // in reverse order when we tab backwards.
1996 // Note that in this case, the focus proxies should not
1997 // be taken into consideration, since they only take
1998 // effect when tabbing forward
1999 for (int i = compositeCount - 1; i >= 0; --i) {
2000 container.backTab();
2001 QVERIFY(!composite[i]->lineEdit1->hasFocus());
2002 QVERIFY(composite[i]->lineEdit2->hasFocus());
2003 container.backTab();
2004 QVERIFY(composite[i]->lineEdit1->hasFocus());
2005 QVERIFY(!composite[i]->lineEdit2->hasFocus());
2006 }
2007
2008 container.backTab();
2009 QVERIFY(firstEdit->hasFocus());
2010}
2011
2012void tst_QWidget::tabOrderWithProxyDisabled()
2013{
2014 Container container;
2015 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2016
2017 QLineEdit lineEdit1;
2018 lineEdit1.setObjectName("lineEdit1");
2019
2020 QWidget containingWidget;
2021 containingWidget.setFocusPolicy(Qt::StrongFocus);
2022 auto *containingLayout = new QVBoxLayout;
2023 QLineEdit lineEdit2;
2024 lineEdit2.setObjectName("lineEdit2");
2025 QLineEdit lineEdit3;
2026 lineEdit3.setObjectName("lineEdit3");
2027 containingLayout->addWidget(&lineEdit2);
2028 containingLayout->addWidget(&lineEdit3);
2029 containingWidget.setLayout(containingLayout);
2030 containingWidget.setFocusProxy(&lineEdit2);
2031 lineEdit2.setEnabled(false);
2032
2033 container.box->addWidget(&lineEdit1);
2034 container.box->addWidget(&containingWidget);
2035
2036 container.show();
2037 container.activateWindow();
2038
2039 QApplication::setActiveWindow(&container);
2040 if (!QTest::qWaitForWindowActive(widget: &container))
2041 QSKIP("Window failed to activate, skipping test");
2042
2043 QVERIFY2(lineEdit1.hasFocus(),
2044 qPrintable(QApplication::focusWidget()->objectName()));
2045 container.tab();
2046 QVERIFY2(!lineEdit2.hasFocus(),
2047 qPrintable(QApplication::focusWidget()->objectName()));
2048 QVERIFY2(lineEdit3.hasFocus(),
2049 qPrintable(QApplication::focusWidget()->objectName()));
2050 container.tab();
2051 QVERIFY2(lineEdit1.hasFocus(),
2052 qPrintable(QApplication::focusWidget()->objectName()));
2053 container.backTab();
2054 QVERIFY2(lineEdit3.hasFocus(),
2055 qPrintable(QApplication::focusWidget()->objectName()));
2056 container.backTab();
2057 QVERIFY2(!lineEdit2.hasFocus(),
2058 qPrintable(QApplication::focusWidget()->objectName()));
2059 QVERIFY2(lineEdit1.hasFocus(),
2060 qPrintable(QApplication::focusWidget()->objectName()));
2061}
2062
2063void tst_QWidget::tabOrderWithCompoundWidgets()
2064{
2065 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2066 QSKIP("Wayland: This fails. Figure out why.");
2067
2068 const int compositeCount = 4;
2069 Container container;
2070 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2071 Composite *composite[compositeCount];
2072
2073 QLineEdit *firstEdit = new QLineEdit();
2074 container.box->addWidget(firstEdit);
2075
2076 for (int i = 0; i < compositeCount; i++) {
2077 composite[i] = new Composite(nullptr, QStringLiteral("Composite: ") + QString::number(i));
2078 container.box->addWidget(composite[i]);
2079
2080 // Let the composite handle focus, and set a child as focus proxy (use the second child, just
2081 // to ensure that we don't just tab to the first child by coinsidence). This will make the
2082 // composite "compound". Also enable the last line edit to have a bit more data to check when
2083 // tabbing forwards.
2084 composite[i]->setFocusPolicy(Qt::StrongFocus);
2085 composite[i]->setFocusProxy(composite[i]->lineEdit2);
2086 composite[i]->lineEdit3->setEnabled(true);
2087 }
2088
2089 QLineEdit *lastEdit = new QLineEdit();
2090 container.box->addWidget(lastEdit);
2091
2092 // Reverse tab order between each composite
2093 // (but not inside them), including first and last line edit.
2094 // The result should not affect local tab order inside each
2095 // composite, only between them.
2096 QWidget::setTabOrder(lastEdit, composite[compositeCount - 1]);
2097 for (int i = compositeCount - 1; i >= 1; --i)
2098 QWidget::setTabOrder(composite[i], composite[i-1]);
2099 QWidget::setTabOrder(composite[0], firstEdit);
2100
2101 container.show();
2102 container.activateWindow();
2103 QApplication::setActiveWindow(&container);
2104 QVERIFY(QTest::qWaitForWindowActive(&container));
2105
2106 lastEdit->setFocus();
2107 QTRY_VERIFY(lastEdit->hasFocus());
2108
2109 // Check that focus moves between the line edits in the normal
2110 // order when tabbing inside each compound, but in the reverse
2111 // order when tabbing between them. Since the composites have
2112 // lineEdit2 as focus proxy, lineEdit2 will be the first with focus
2113 // when the compound gets focus, and lineEdit1 will therefore be skipped.
2114 for (int i = compositeCount - 1; i >= 0; --i) {
2115 container.tab();
2116 Composite *c = composite[i];
2117 QVERIFY(!c->lineEdit1->hasFocus());
2118 QVERIFY(c->lineEdit2->hasFocus());
2119 QVERIFY(!c->lineEdit3->hasFocus());
2120 container.tab();
2121 QVERIFY(!c->lineEdit1->hasFocus());
2122 QVERIFY(!c->lineEdit2->hasFocus());
2123 QVERIFY(c->lineEdit3->hasFocus());
2124 }
2125
2126 container.tab();
2127 QVERIFY(firstEdit->hasFocus());
2128
2129 // Check that focus moves in reverse order when backTab inside the composites, but
2130 // in the 'correct' order when backTab between them (since the composites are in reverse tab
2131 // order from before, which cancels it out). Note that when we backtab into a compound, we start
2132 // at lineEdit3 rather than the focus proxy, since that is the reverse of what happens when we tab
2133 // forward. And this time we will also backtab to lineEdit1, since there is no focus proxy that interferes.
2134 for (int i = 0; i < compositeCount; ++i) {
2135 container.backTab();
2136 Composite *c = composite[i];
2137 QVERIFY(!c->lineEdit1->hasFocus());
2138 QVERIFY(!c->lineEdit2->hasFocus());
2139 QVERIFY(c->lineEdit3->hasFocus());
2140 container.backTab();
2141 QVERIFY(!c->lineEdit1->hasFocus());
2142 QVERIFY(c->lineEdit2->hasFocus());
2143 QVERIFY(!c->lineEdit3->hasFocus());
2144 container.backTab();
2145 QVERIFY(c->lineEdit1->hasFocus());
2146 QVERIFY(!c->lineEdit2->hasFocus());
2147 QVERIFY(!c->lineEdit3->hasFocus());
2148 }
2149
2150 container.backTab();
2151 QVERIFY(lastEdit->hasFocus());
2152}
2153
2154static QVector<QWidget*> getFocusChain(QWidget *start, bool bForward)
2155{
2156 QVector<QWidget*> ret;
2157 QWidget *cur = start;
2158 do {
2159 ret += cur;
2160 auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(widget: cur));
2161 cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev;
2162 } while (cur != start);
2163 return ret;
2164}
2165
2166//#define DEBUG_FOCUS_CHAIN
2167static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr)
2168{
2169#ifdef DEBUG_FOCUS_CHAIN
2170 qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc;
2171 QWidget *cur = start;
2172 do {
2173 qDebug() << cur;
2174 auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur));
2175 cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev;
2176 } while (cur != start);
2177#else
2178 Q_UNUSED(start)
2179 Q_UNUSED(bForward)
2180 Q_UNUSED(desc)
2181#endif
2182}
2183
2184void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy()
2185{
2186 Container container;
2187 container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2188 QSpinBox spinbox1;
2189 spinbox1.setObjectName("spinbox1");
2190 QSpinBox spinbox2;
2191 spinbox2.setObjectName("spinbox2");
2192 QSpinBox spinbox3;
2193 spinbox3.setObjectName("spinbox3");
2194
2195 spinbox1.setFocusPolicy(Qt::StrongFocus);
2196 spinbox2.setFocusPolicy(Qt::NoFocus);
2197 spinbox3.setFocusPolicy(Qt::StrongFocus);
2198 container.box->addWidget(&spinbox1);
2199 container.box->addWidget(&spinbox2);
2200 container.box->addWidget(&spinbox3);
2201
2202 container.show();
2203 container.activateWindow();
2204
2205 QApplication::setActiveWindow(&container);
2206 if (!QTest::qWaitForWindowActive(widget: &container))
2207 QSKIP("Window failed to activate, skipping test");
2208
2209 QVERIFY2(spinbox1.hasFocus(),
2210 qPrintable(QApplication::focusWidget()->objectName()));
2211 container.tab();
2212 QVERIFY2(!spinbox2.hasFocus(),
2213 qPrintable(QApplication::focusWidget()->objectName()));
2214 QVERIFY2(spinbox3.hasFocus(),
2215 qPrintable(QApplication::focusWidget()->objectName()));
2216 container.tab();
2217 QVERIFY2(spinbox1.hasFocus(),
2218 qPrintable(QApplication::focusWidget()->objectName()));
2219 container.backTab();
2220 QVERIFY2(spinbox3.hasFocus(),
2221 qPrintable(QApplication::focusWidget()->objectName()));
2222 container.backTab();
2223 QVERIFY2(!spinbox2.hasFocus(),
2224 qPrintable(QApplication::focusWidget()->objectName()));
2225 QVERIFY2(spinbox1.hasFocus(),
2226 qPrintable(QApplication::focusWidget()->objectName()));
2227}
2228
2229void tst_QWidget::tabOrderNoChange()
2230{
2231 QWidget w;
2232 auto *verticalLayout = new QVBoxLayout(&w);
2233 auto *tabWidget = new QTabWidget(&w);
2234 auto *tv = new QTreeView(tabWidget);
2235 tabWidget->addTab(widget: tv, QStringLiteral("Tab 1"));
2236 verticalLayout->addWidget(tabWidget);
2237
2238 const auto focusChainForward = getFocusChain(start: &w, bForward: true);
2239 const auto focusChainBackward = getFocusChain(start: &w, bForward: false);
2240 dumpFocusChain(start: &w, bForward: true);
2241 QWidget::setTabOrder(tabWidget, tv);
2242 dumpFocusChain(start: &w, bForward: true);
2243 QCOMPARE(focusChainForward, getFocusChain(&w, true));
2244 QCOMPARE(focusChainBackward, getFocusChain(&w, false));
2245}
2246
2247void tst_QWidget::tabOrderNoChange2()
2248{
2249 QWidget w;
2250 auto *verticalLayout = new QVBoxLayout(&w);
2251 auto *tabWidget = new QTabWidget(&w);
2252 tabWidget->setObjectName("tabWidget");
2253 verticalLayout->addWidget(tabWidget);
2254
2255 auto *tab1 = new QWidget(tabWidget);
2256 tab1->setObjectName("tab1");
2257 auto *vLay1 = new QVBoxLayout(tab1);
2258 auto *le1 = new QLineEdit(tab1);
2259 le1->setObjectName("le1");
2260 auto *le2 = new QLineEdit(tab1);
2261 le2->setObjectName("le2");
2262 vLay1->addWidget(le1);
2263 vLay1->addWidget(le2);
2264 tabWidget->addTab(widget: tab1, QStringLiteral("Tab 1"));
2265
2266 auto *tab2 = new QWidget(tabWidget);
2267 tab2->setObjectName("tab2");
2268 auto *vLay2 = new QVBoxLayout(tab2);
2269 auto *le3 = new QLineEdit(tab2);
2270 le3->setObjectName("le3");
2271 auto *le4 = new QLineEdit(tab2);
2272 le4->setObjectName("le4");
2273 vLay2->addWidget(le3);
2274 vLay2->addWidget(le4);
2275 tabWidget->addTab(widget: tab2, QStringLiteral("Tab 2"));
2276
2277 const auto focusChainForward = getFocusChain(start: &w, bForward: true);
2278 const auto focusChainBackward = getFocusChain(start: &w, bForward: false);
2279 dumpFocusChain(start: &w, bForward: true);
2280 dumpFocusChain(start: &w, bForward: false);
2281 // this will screw up the focus chain order without visible changes,
2282 // so don't call it here for the simplicity of the test
2283 //QWidget::setTabOrder(tabWidget, le1);
2284
2285 QWidget::setTabOrder(le1, le2);
2286 dumpFocusChain(start: &w, bForward: true, desc: "QWidget::setTabOrder(le1, le2)");
2287 QWidget::setTabOrder(le2, le3);
2288 dumpFocusChain(start: &w, bForward: true, desc: "QWidget::setTabOrder(le2, le3)");
2289 QWidget::setTabOrder(le3, le4);
2290 dumpFocusChain(start: &w, bForward: true, desc: "QWidget::setTabOrder(le3, le4)");
2291 QWidget::setTabOrder(le4, tabWidget);
2292 dumpFocusChain(start: &w, bForward: true, desc: "QWidget::setTabOrder(le4, tabWidget)");
2293 dumpFocusChain(start: &w, bForward: false);
2294
2295 QCOMPARE(focusChainForward, getFocusChain(&w, true));
2296 QCOMPARE(focusChainBackward, getFocusChain(&w, false));
2297}
2298
2299void tst_QWidget::appFocusWidgetWithFocusProxyLater()
2300{
2301 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2302 QSKIP("Wayland: This fails. Figure out why.");
2303
2304 // Given a lineedit without a focus proxy
2305 QWidget window;
2306 window.setWindowTitle(QTest::currentTestFunction());
2307 QLineEdit *lineEditFocusProxy = new QLineEdit(&window);
2308 QLineEdit *lineEdit = new QLineEdit(&window);
2309 lineEdit->setFocus();
2310 window.show();
2311 QApplication::setActiveWindow(&window);
2312 QVERIFY(QTest::qWaitForWindowActive(&window));
2313 QCOMPARE(QApplication::focusWidget(), lineEdit);
2314
2315 // When setting a focus proxy for the focus widget (like QWebEngineView does)
2316 lineEdit->setFocusProxy(lineEditFocusProxy);
2317
2318 // Then the focus widget should be updated
2319 QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
2320
2321 // So that deleting the lineEdit and later the window, doesn't crash
2322 delete lineEdit;
2323 QCOMPARE(QApplication::focusWidget(), nullptr);
2324}
2325
2326void tst_QWidget::appFocusWidgetWhenLosingFocusProxy()
2327{
2328 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
2329 QSKIP("Wayland: This fails. Figure out why.");
2330
2331 // Given a lineedit with a focus proxy
2332 QWidget window;
2333 window.setWindowTitle(QTest::currentTestFunction());
2334 QLineEdit *lineEditFocusProxy = new QLineEdit(&window);
2335 QLineEdit *lineEdit = new QLineEdit(&window);
2336 lineEdit->setFocusProxy(lineEditFocusProxy);
2337 lineEdit->setFocus();
2338 window.show();
2339 QApplication::setActiveWindow(&window);
2340 QVERIFY(QTest::qWaitForWindowActive(&window));
2341 QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
2342 QVERIFY(lineEdit->hasFocus());
2343 QVERIFY(lineEditFocusProxy->hasFocus());
2344
2345 // When unsetting the focus proxy
2346 lineEdit->setFocusProxy(nullptr);
2347
2348 // then the focus widget should not change
2349 QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
2350 QVERIFY(!lineEdit->hasFocus());
2351 QVERIFY(lineEditFocusProxy->hasFocus());
2352}
2353
2354void tst_QWidget::explicitTabOrderWithComplexWidget()
2355{
2356 // Check that handling tab/backtab with a widget comprimised of other widgets
2357 // handles tabbing correctly
2358 Container window;
2359 auto lineEditOne = new QLineEdit;
2360 window.box->addWidget(lineEditOne);
2361 auto lineEditTwo = new QLineEdit;
2362 window.box->addWidget(lineEditTwo);
2363 QWidget::setTabOrder(lineEditOne, lineEditTwo);
2364 lineEditOne->setFocus();
2365 window.show();
2366 QApplication::setActiveWindow(&window);
2367 QVERIFY(QTest::qWaitForWindowActive(&window));
2368 QTRY_COMPARE(QApplication::focusWidget(), lineEditOne);
2369
2370 window.tab();
2371 QTRY_COMPARE(QApplication::focusWidget(), lineEditTwo);
2372 window.tab();
2373 QTRY_COMPARE(QApplication::focusWidget(), lineEditOne);
2374 window.backTab();
2375 QTRY_COMPARE(QApplication::focusWidget(), lineEditTwo);
2376 window.backTab();
2377 QTRY_COMPARE(QApplication::focusWidget(), lineEditOne);
2378}
2379
2380void tst_QWidget::explicitTabOrderWithSpinBox_QTBUG81097()
2381{
2382 // Check the special case of QAbstractSpinBox-like widgets, that have a
2383 // child widget with a focusPolicy() set to its parent.
2384 Container window;
2385 auto spinBoxOne = new QDoubleSpinBox;
2386 auto spinBoxTwo = new QDoubleSpinBox;
2387 auto lineEdit = new QLineEdit;
2388 window.box->addWidget(spinBoxOne);
2389 window.box->addWidget(spinBoxTwo);
2390 window.box->addWidget(lineEdit);
2391 QWidget::setTabOrder(spinBoxOne, spinBoxTwo);
2392 QWidget::setTabOrder(spinBoxTwo, lineEdit);
2393 spinBoxOne->setFocus();
2394 window.show();
2395 QApplication::setActiveWindow(&window);
2396 QVERIFY(QTest::qWaitForWindowActive(&window));
2397 QTRY_COMPARE(QApplication::focusWidget(), spinBoxOne);
2398
2399 window.tab();
2400 QTRY_COMPARE(QApplication::focusWidget(), spinBoxTwo);
2401 window.tab();
2402 QTRY_COMPARE(QApplication::focusWidget(), lineEdit);
2403 window.backTab();
2404 QTRY_COMPARE(QApplication::focusWidget(), spinBoxTwo);
2405 window.backTab();
2406 QTRY_COMPARE(QApplication::focusWidget(), spinBoxOne);
2407 window.backTab();
2408 QTRY_COMPARE(QApplication::focusWidget(), lineEdit);
2409}
2410
2411#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
2412void tst_QWidget::activation()
2413{
2414 Q_CHECK_PAINTEVENTS
2415
2416 QWidget widget1;
2417 widget1.setObjectName("activation-Widget1");
2418 widget1.setWindowTitle(widget1.objectName());
2419
2420 QWidget widget2;
2421 widget1.setObjectName("activation-Widget2");
2422 widget1.setWindowTitle(widget2.objectName());
2423
2424 widget1.show();
2425 widget2.show();
2426
2427 QTRY_COMPARE(QApplication::activeWindow(), &widget2);
2428 widget2.showMinimized();
2429
2430 QTRY_COMPARE(QApplication::activeWindow(), &widget1);
2431 widget2.showMaximized();
2432 QTRY_COMPARE(QApplication::activeWindow(), &widget2);
2433 widget2.showMinimized();
2434 QTRY_COMPARE(QApplication::activeWindow(), &widget1);
2435 widget2.showNormal();
2436 QTRY_COMPARE(QApplication::activeWindow(), &widget2);
2437 widget2.hide();
2438 QTRY_COMPARE(QApplication::activeWindow(), &widget1);
2439}
2440#endif // Q_OS_WIN
2441
2442void tst_QWidget::windowState()
2443{
2444#ifdef Q_OS_MACOS
2445 QSKIP("QTBUG-52974");
2446#endif
2447
2448 if (m_platform == QStringLiteral("xcb"))
2449 QSKIP("X11: Many window managers do not support window state properly, which causes this test to fail.");
2450 if (m_platform == QStringLiteral("wayland"))
2451 QSKIP("Wayland: This fails. Figure out why.");
2452
2453 QPoint pos;
2454 QSize size = m_testWidgetSize;
2455 if (QGuiApplicationPrivate::platformIntegration()->defaultWindowState(Qt::Widget)
2456 == Qt::WindowFullScreen
2457 || m_platform == QStringLiteral("winrt")) {
2458 size = QGuiApplication::primaryScreen()->size();
2459 } else {
2460 pos = QPoint(10, 10);
2461 }
2462
2463 QWidget widget1;
2464 widget1.move(pos);
2465 widget1.resize(size);
2466 QCOMPARE(widget1.pos(), pos);
2467 QCOMPARE(widget1.size(), size);
2468 QTest::qWait(ms: 100);
2469 widget1.setObjectName(QStringLiteral("windowState-Widget1"));
2470 widget1.setWindowTitle(widget1.objectName());
2471 QCOMPARE(widget1.pos(), pos);
2472 QCOMPARE(widget1.size(), size);
2473
2474#define VERIFY_STATE(s) \
2475 QCOMPARE(int(widget1.windowState() & stateMask), int(s)); \
2476 QCOMPARE(int(widget1.windowHandle()->windowStates() & stateMask), int(s))
2477
2478 const auto stateMask = Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen;
2479
2480 widget1.setWindowState(Qt::WindowMaximized);
2481 QTest::qWait(ms: 100);
2482 VERIFY_STATE(Qt::WindowMaximized);
2483 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized);
2484
2485 widget1.setVisible(true);
2486 QTest::qWait(ms: 100);
2487 VERIFY_STATE(Qt::WindowMaximized);
2488 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized);
2489
2490 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
2491 QTest::qWait(ms: 100);
2492 QVERIFY(!(widget1.windowState() & Qt::WindowMaximized));
2493 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
2494 qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
2495 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
2496
2497 widget1.setWindowState(Qt::WindowMinimized);
2498 QTest::qWait(ms: 100);
2499 VERIFY_STATE(Qt::WindowMinimized);
2500 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized);
2501
2502 widget1.setWindowState(widget1.windowState() | Qt::WindowMaximized);
2503 QTest::qWait(ms: 100);
2504 VERIFY_STATE((Qt::WindowMinimized|Qt::WindowMaximized));
2505 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized);
2506
2507 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized);
2508 QTest::qWait(ms: 100);
2509 VERIFY_STATE(Qt::WindowMaximized);
2510 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized);
2511
2512 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
2513 QTest::qWait(ms: 100);
2514 QVERIFY(!(widget1.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized)));
2515 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
2516 qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
2517 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
2518
2519 widget1.setWindowState(Qt::WindowFullScreen);
2520 QTest::qWait(ms: 100);
2521 VERIFY_STATE(Qt::WindowFullScreen);
2522 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen);
2523
2524 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized);
2525 QTest::qWait(ms: 100);
2526 VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMinimized));
2527 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized);
2528
2529 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized);
2530 QTest::qWait(ms: 100);
2531 VERIFY_STATE(Qt::WindowFullScreen);
2532 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen);
2533
2534 widget1.setWindowState(Qt::WindowNoState);
2535 QTest::qWait(ms: 100);
2536 VERIFY_STATE(Qt::WindowNoState);
2537 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
2538 qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
2539 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
2540
2541 widget1.setWindowState(Qt::WindowFullScreen);
2542 QTest::qWait(ms: 100);
2543 VERIFY_STATE(Qt::WindowFullScreen);
2544 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen);
2545
2546 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
2547 QTest::qWait(ms: 100);
2548 VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized));
2549 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen);
2550
2551 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized);
2552 QTest::qWait(ms: 100);
2553 VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized|Qt::WindowMinimized));
2554 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized);
2555
2556 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized);
2557 QTest::qWait(ms: 100);
2558 VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized));
2559 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen);
2560
2561 widget1.setWindowState(widget1.windowState() ^ Qt::WindowFullScreen);
2562 QTest::qWait(ms: 100);
2563 VERIFY_STATE(Qt::WindowMaximized);
2564 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized);
2565
2566 widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
2567 QTest::qWait(ms: 100);
2568 QVERIFY(!(widget1.windowState() & stateMask));
2569 QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
2570
2571 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
2572 qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
2573 QTRY_COMPARE(widget1.size(), size);
2574}
2575
2576void tst_QWidget::showMaximized()
2577{
2578 QWidget plain;
2579 QHBoxLayout *layout;
2580 layout = new QHBoxLayout;
2581 QWidget layouted;
2582 QLineEdit le;
2583 QLineEdit le2;
2584 QLineEdit le3;
2585
2586 layout->addWidget(&le);
2587 layout->addWidget(&le2);
2588 layout->addWidget(&le3);
2589
2590 layouted.setLayout(layout);
2591
2592 plain.showMaximized();
2593 QVERIFY(plain.windowState() & Qt::WindowMaximized);
2594
2595 plain.showNormal();
2596 QVERIFY(!(plain.windowState() & Qt::WindowMaximized));
2597
2598 layouted.showMaximized();
2599 QVERIFY(layouted.windowState() & Qt::WindowMaximized);
2600
2601 layouted.showNormal();
2602 QVERIFY(!(layouted.windowState() & Qt::WindowMaximized));
2603
2604 // ### fixme: embedded may choose a different size to fit on the screen.
2605 if (layouted.size() != layouted.sizeHint())
2606 QEXPECT_FAIL("", "QTBUG-22326", Continue);
2607 QCOMPARE(layouted.size(), layouted.sizeHint());
2608
2609 layouted.showMaximized();
2610 QVERIFY(layouted.isMaximized());
2611 QVERIFY(layouted.isVisible());
2612
2613 layouted.hide();
2614 QVERIFY(layouted.isMaximized());
2615 QVERIFY(!layouted.isVisible());
2616
2617 layouted.showMaximized();
2618 QVERIFY(layouted.isMaximized());
2619 QVERIFY(layouted.isVisible());
2620
2621 layouted.showMinimized();
2622 QVERIFY(layouted.isMinimized());
2623 QVERIFY(layouted.isMaximized());
2624
2625 layouted.showMaximized();
2626 QVERIFY(!layouted.isMinimized());
2627 QVERIFY(layouted.isMaximized());
2628 QVERIFY(layouted.isVisible());
2629
2630 layouted.showMinimized();
2631 QVERIFY(layouted.isMinimized());
2632 QVERIFY(layouted.isMaximized());
2633
2634 layouted.showMaximized();
2635 QVERIFY(!layouted.isMinimized());
2636 QVERIFY(layouted.isMaximized());
2637 QVERIFY(layouted.isVisible());
2638
2639 {
2640 QWidget frame;
2641 QWidget widget(&frame);
2642 widget.showMaximized();
2643 QVERIFY(widget.isMaximized());
2644 }
2645
2646 {
2647 QWidget widget;
2648 setFrameless(&widget);
2649 widget.setGeometry(ax: 0, ay: 0, aw: 10, ah: 10);
2650 widget.showMaximized();
2651 QTRY_VERIFY(widget.size().width() > 20 && widget.size().height() > 20);
2652 }
2653}
2654
2655void tst_QWidget::showFullScreen()
2656{
2657#ifdef Q_OS_MACOS
2658 QSKIP("QTBUG-52974");
2659#endif
2660
2661 if (m_platform == QStringLiteral("winrt"))
2662 QSKIP("WinRT: This fails. QTBUG-68297");
2663 QWidget plain;
2664 QHBoxLayout *layout;
2665 QWidget layouted;
2666 QLineEdit le;
2667 QLineEdit le2;
2668 QLineEdit le3;
2669 layout = new QHBoxLayout;
2670
2671 layout->addWidget(&le);
2672 layout->addWidget(&le2);
2673 layout->addWidget(&le3);
2674
2675 layouted.setLayout(layout);
2676
2677 plain.showFullScreen();
2678 QVERIFY(plain.windowState() & Qt::WindowFullScreen);
2679 QVERIFY(plain.windowHandle());
2680 QVERIFY(plain.windowHandle()->screen());
2681 const QRect expectedFullScreenGeometry = plain.windowHandle()->screen()->geometry();
2682 QTRY_COMPARE(plain.geometry(), expectedFullScreenGeometry);
2683
2684 plain.showNormal();
2685 QVERIFY(!(plain.windowState() & Qt::WindowFullScreen));
2686
2687 layouted.showFullScreen();
2688 QVERIFY(layouted.windowState() & Qt::WindowFullScreen);
2689 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2690
2691 layouted.showNormal();
2692 QVERIFY(!(layouted.windowState() & Qt::WindowFullScreen));
2693
2694 // ### fixme: embedded may choose a different size to fit on the screen.
2695 if (layouted.size() != layouted.sizeHint())
2696 QEXPECT_FAIL("", "QTBUG-22326", Continue);
2697 QCOMPARE(layouted.size(), layouted.sizeHint());
2698
2699 layouted.showFullScreen();
2700 QVERIFY(layouted.isFullScreen());
2701 QVERIFY(layouted.isVisible());
2702 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2703
2704 layouted.hide();
2705 QVERIFY(layouted.isFullScreen());
2706 QVERIFY(!layouted.isVisible());
2707
2708 layouted.showFullScreen();
2709 QVERIFY(layouted.isFullScreen());
2710 QVERIFY(layouted.isVisible());
2711 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2712
2713 layouted.showMinimized();
2714 QVERIFY(layouted.isMinimized());
2715 QVERIFY(layouted.isFullScreen());
2716
2717 layouted.showFullScreen();
2718 QVERIFY(!layouted.isMinimized());
2719 QVERIFY(layouted.isFullScreen());
2720 QVERIFY(layouted.isVisible());
2721 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2722
2723 layouted.showMinimized();
2724 QVERIFY(layouted.isMinimized());
2725 QVERIFY(layouted.isFullScreen());
2726
2727 layouted.showFullScreen();
2728 QVERIFY(!layouted.isMinimized());
2729 QVERIFY(layouted.isFullScreen());
2730 QVERIFY(layouted.isVisible());
2731 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2732
2733 {
2734 QWidget frame;
2735 QWidget widget(&frame);
2736 widget.showFullScreen();
2737 QVERIFY(widget.isFullScreen());
2738 QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry);
2739 }
2740}
2741
2742class ResizeWidget : public QWidget {
2743public:
2744 explicit ResizeWidget(QWidget *p = nullptr) : QWidget(p)
2745 {
2746 setObjectName(QLatin1String("ResizeWidget"));
2747 setWindowTitle(objectName());
2748 }
2749protected:
2750 void resizeEvent(QResizeEvent *e) override
2751 {
2752 QCOMPARE(size(), e->size());
2753 ++m_resizeEventCount;
2754 }
2755
2756public:
2757 int m_resizeEventCount = 0;
2758};
2759
2760void tst_QWidget::resizeEvent()
2761{
2762 {
2763 QWidget wParent;
2764 wParent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2765 wParent.resize(m_testWidgetSize);
2766 ResizeWidget wChild(&wParent);
2767 wParent.show();
2768 QVERIFY(QTest::qWaitForWindowExposed(&wParent));
2769 QCOMPARE (wChild.m_resizeEventCount, 1); // initial resize event before paint
2770 wParent.hide();
2771 QSize safeSize(640,480);
2772 if (wChild.size() == safeSize)
2773 safeSize.setWidth(639);
2774 wChild.resize(safeSize);
2775 QCOMPARE (wChild.m_resizeEventCount, 1);
2776 wParent.show();
2777 QCOMPARE (wChild.m_resizeEventCount, 2);
2778 }
2779
2780 {
2781 ResizeWidget wTopLevel;
2782 wTopLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2783 wTopLevel.resize(m_testWidgetSize);
2784 wTopLevel.show();
2785 QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel));
2786 if (m_platform == QStringLiteral("winrt"))
2787 QEXPECT_FAIL("", "WinRT does not support resize", Abort);
2788 QCOMPARE (wTopLevel.m_resizeEventCount, 1); // initial resize event before paint for toplevels
2789 wTopLevel.hide();
2790 QSize safeSize(640,480);
2791 if (wTopLevel.size() == safeSize)
2792 safeSize.setWidth(639);
2793 wTopLevel.resize(safeSize);
2794 QCOMPARE (wTopLevel.m_resizeEventCount, 1);
2795 wTopLevel.show();
2796 QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel));
2797 QCOMPARE (wTopLevel.m_resizeEventCount, 2);
2798 }
2799}
2800
2801void tst_QWidget::showMinimized()
2802{
2803 if (m_platform == QStringLiteral("wayland")) {
2804 QSKIP("Wayland: Neither xdg_shell, wl_shell or ivi_application support "
2805 "letting a client know whether it's minimized. So on these shells "
2806 "Qt Wayland will always report that it's unmimized.");
2807 }
2808
2809 QWidget plain;
2810 plain.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2811 plain.move(ax: 100, ay: 100);
2812 plain.resize(w: 200, h: 200);
2813 QPoint pos = plain.pos();
2814
2815 plain.showMinimized();
2816 QVERIFY(plain.isMinimized());
2817 QVERIFY(plain.isVisible());
2818#ifdef Q_OS_WINRT
2819 QEXPECT_FAIL("", "Winrt does not support move and resize", Abort);
2820#endif
2821 QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
2822 qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
2823
2824 plain.showNormal();
2825 QVERIFY(!plain.isMinimized());
2826 QVERIFY(plain.isVisible());
2827 QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
2828 qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
2829
2830 plain.showMinimized();
2831 QVERIFY(plain.isMinimized());
2832 QVERIFY(plain.isVisible());
2833 QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
2834 qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
2835
2836 plain.hide();
2837 QVERIFY(plain.isMinimized());
2838 QVERIFY(!plain.isVisible());
2839
2840 plain.showMinimized();
2841 QVERIFY(plain.isMinimized());
2842 QVERIFY(plain.isVisible());
2843
2844 plain.setGeometry(ax: 200, ay: 200, aw: 300, ah: 300);
2845 plain.showNormal();
2846 QCOMPARE(plain.geometry(), QRect(200, 200, 300, 300));
2847
2848 {
2849 QWidget frame;
2850 QWidget widget(&frame);
2851 widget.showMinimized();
2852 QVERIFY(widget.isMinimized());
2853 }
2854}
2855
2856void tst_QWidget::showMinimizedKeepsFocus()
2857{
2858 if (m_platform == QStringLiteral("xcb"))
2859 QSKIP("QTBUG-26424");
2860 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
2861 QSKIP("Window activation is not supported.");
2862 if (m_platform == QStringLiteral("offscreen"))
2863 QSKIP("Platform offscreen does not support showMinimized()");
2864
2865 //here we test that minimizing a widget and restoring it doesn't change the focus inside of it
2866 {
2867 QWidget window;
2868 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2869 window.resize(w: 200, h: 200);
2870 QWidget child1(&window), child2(&window);
2871 child1.setFocusPolicy(Qt::StrongFocus);
2872 child2.setFocusPolicy(Qt::StrongFocus);
2873 window.show();
2874 QApplication::setActiveWindow(&window);
2875 QVERIFY(QTest::qWaitForWindowActive(&window));
2876 child2.setFocus();
2877
2878 QTRY_COMPARE(window.focusWidget(), &child2);
2879 QTRY_COMPARE(QApplication::focusWidget(), &child2);
2880
2881 window.showMinimized();
2882 QTRY_VERIFY(window.isMinimized());
2883 QTRY_COMPARE(window.focusWidget(), &child2);
2884
2885 window.showNormal();
2886 QTest::qWait(ms: 10);
2887 QTRY_COMPARE(window.focusWidget(), &child2);
2888 }
2889
2890 //testing deletion of the focusWidget
2891 {
2892 QWidget window;
2893 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2894 window.resize(w: 200, h: 200);
2895 QWidget *child = new QWidget(&window);
2896 child->setFocusPolicy(Qt::StrongFocus);
2897 window.show();
2898 QApplication::setActiveWindow(&window);
2899 QVERIFY(QTest::qWaitForWindowActive(&window));
2900 child->setFocus();
2901 QTRY_COMPARE(window.focusWidget(), child);
2902 QTRY_COMPARE(QApplication::focusWidget(), child);
2903
2904 delete child;
2905 QCOMPARE(window.focusWidget(), nullptr);
2906 QCOMPARE(QApplication::focusWidget(), nullptr);
2907 }
2908
2909 //testing reparenting the focus widget
2910 {
2911 QWidget window;
2912 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2913 window.resize(w: 200, h: 200);
2914 QWidget *child = new QWidget(&window);
2915 child->setFocusPolicy(Qt::StrongFocus);
2916 window.show();
2917 QApplication::setActiveWindow(&window);
2918 QVERIFY(QTest::qWaitForWindowActive(&window));
2919 child->setFocus();
2920 QTRY_COMPARE(window.focusWidget(), child);
2921 QTRY_COMPARE(QApplication::focusWidget(), child);
2922
2923 child->setParent(nullptr);
2924 QScopedPointer<QWidget> childGuard(child);
2925 QCOMPARE(window.focusWidget(), nullptr);
2926 QCOMPARE(QApplication::focusWidget(), nullptr);
2927 }
2928
2929 //testing setEnabled(false)
2930 {
2931 QWidget window;
2932 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2933 window.resize(w: 200, h: 200);
2934 QWidget *child = new QWidget(&window);
2935 child->setFocusPolicy(Qt::StrongFocus);
2936 window.show();
2937 QApplication::setActiveWindow(&window);
2938 QVERIFY(QTest::qWaitForWindowActive(&window));
2939 child->setFocus();
2940 QTRY_COMPARE(window.focusWidget(), child);
2941 QTRY_COMPARE(QApplication::focusWidget(), child);
2942
2943 child->setEnabled(false);
2944 QCOMPARE(window.focusWidget(), nullptr);
2945 QCOMPARE(QApplication::focusWidget(), nullptr);
2946 }
2947
2948 //testing clearFocus
2949 {
2950 QWidget window;
2951 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2952 window.resize(w: 200, h: 200);
2953 QWidget *firstchild = new QWidget(&window);
2954 firstchild->setFocusPolicy(Qt::StrongFocus);
2955 QWidget *child = new QWidget(&window);
2956 child->setFocusPolicy(Qt::StrongFocus);
2957 window.show();
2958 QApplication::setActiveWindow(&window);
2959 QVERIFY(QTest::qWaitForWindowActive(&window));
2960 child->setFocus();
2961 QTRY_COMPARE(window.focusWidget(), child);
2962 QTRY_COMPARE(QApplication::focusWidget(), child);
2963
2964 child->clearFocus();
2965 QCOMPARE(window.focusWidget(), nullptr);
2966 QCOMPARE(QApplication::focusWidget(), nullptr);
2967
2968 window.showMinimized();
2969 QTest::qWait(ms: 30);
2970 QTRY_VERIFY(window.isMinimized());
2971 QCOMPARE(window.focusWidget(), nullptr);
2972 QTRY_COMPARE(QApplication::focusWidget(), nullptr);
2973
2974 window.showNormal();
2975 QApplication::setActiveWindow(&window);
2976 QVERIFY(QTest::qWaitForWindowActive(&window));
2977#ifdef Q_OS_MACOS
2978 if (!macHasAccessToWindowsServer())
2979 QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue);
2980#elif defined(Q_OS_WINRT)
2981 QEXPECT_FAIL("", "Winrt fails here - QTBUG-68297", Continue);
2982#endif
2983 QTRY_COMPARE(window.focusWidget(), firstchild);
2984#ifdef Q_OS_MACOS
2985 if (!macHasAccessToWindowsServer())
2986 QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue);
2987#elif defined(Q_OS_WINRT)
2988 QEXPECT_FAIL("", "Winrt fails here - QTBUG-68297", Continue);
2989#endif
2990 QTRY_COMPARE(QApplication::focusWidget(), firstchild);
2991 }
2992}
2993
2994
2995void tst_QWidget::reparent()
2996{
2997 QWidget parent;
2998 parent.setWindowTitle(QStringLiteral("Toplevel ") + __FUNCTION__);
2999 const QPoint parentPosition = m_availableTopLeft + QPoint(300, 300);
3000 parent.setGeometry(QRect(parentPosition, m_testWidgetSize));
3001
3002 QWidget child;
3003 child.setObjectName("child");
3004 child.setGeometry(ax: 10, ay: 10, aw: 180, ah: 130);
3005 QPalette pal1;
3006 pal1.setColor(acr: child.backgroundRole(), acolor: Qt::white);
3007 child.setPalette(pal1);
3008
3009 QWidget childTLW(&child, Qt::Window);
3010 childTLW.setObjectName(QStringLiteral("childTLW ") + __FUNCTION__);
3011 childTLW.setWindowTitle(childTLW.objectName());
3012 childTLW.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize));
3013 QPalette pal2;
3014 pal2.setColor(acr: childTLW.backgroundRole(), acolor: Qt::yellow);
3015 childTLW.setPalette(pal2);
3016
3017 parent.show();
3018 childTLW.show();
3019#ifdef Q_OS_WINRT
3020 QEXPECT_FAIL("", "WinRT does not support more than 1 top level widget", Abort);
3021#endif
3022 QVERIFY(QTest::qWaitForWindowExposed(&parent));
3023
3024 parent.move(parentPosition);
3025
3026 QPoint childPos = parent.mapToGlobal(child.pos());
3027 QPoint tlwPos = childTLW.pos();
3028
3029 child.setParent(parent: nullptr, f: child.windowFlags() & ~Qt::WindowType_Mask);
3030 child.setGeometry(ax: childPos.x(), ay: childPos.y(), aw: child.width(), ah: child.height());
3031 child.show();
3032
3033#if 0 // QTBUG-26424
3034 if (m_platform == QStringLiteral("xcb"))
3035 QEXPECT_FAIL("", "On X11, the window manager will apply NorthWestGravity rules to 'child', which"
3036 " means the top-left corner of the window frame will be placed at 'childPos'"
3037 " causing this test to fail.", Continue);
3038#endif
3039
3040 QCOMPARE(child.geometry().topLeft(), childPos);
3041 QTRY_COMPARE(childTLW.pos(), tlwPos);
3042}
3043
3044// Qt/Embedded does it differently.
3045void tst_QWidget::icon()
3046{
3047#ifdef Q_OS_MACOS
3048 QSKIP("QTBUG-52974");
3049#endif
3050
3051 QPixmap p(20,20);
3052 p.fill(fillColor: Qt::red);
3053 QScopedPointer<QWidget> testWidget(new QWidget);
3054 testWidget->resize(m_testWidgetSize);
3055 testWidget->setWindowTitle(__FUNCTION__);
3056 centerOnScreen(w: testWidget.data());
3057 testWidget->show();
3058 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
3059 testWidget->setWindowIcon(p);
3060
3061 QVERIFY(!testWidget->windowIcon().isNull());
3062 testWidget->show();
3063 QVERIFY(!testWidget->windowIcon().isNull());
3064 testWidget->showFullScreen();
3065 QVERIFY(!testWidget->windowIcon().isNull());
3066 testWidget->showNormal();
3067 QVERIFY(!testWidget->windowIcon().isNull());
3068}
3069
3070void tst_QWidget::hideWhenFocusWidgetIsChild()
3071{
3072 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
3073 QSKIP("Window activation is not supported.");
3074
3075 QScopedPointer<QWidget> testWidget(new QWidget);
3076 testWidget->setWindowTitle(__FUNCTION__);
3077 testWidget->resize(m_testWidgetSize);
3078 centerOnScreen(w: testWidget.data());
3079 QWidget *parentWidget(new QWidget(testWidget.data()));
3080 parentWidget->setObjectName("parentWidget");
3081 parentWidget->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
3082 QLineEdit *edit = new QLineEdit(parentWidget);
3083 edit->setObjectName("edit1");
3084 QLineEdit *edit3 = new QLineEdit(parentWidget);
3085 edit3->setObjectName("edit3");
3086 edit3->move(ax: 0,ay: 50);
3087 QLineEdit *edit2 = new QLineEdit(testWidget.data());
3088 edit2->setObjectName("edit2");
3089 edit2->move(ax: 110, ay: 100);
3090 edit->setFocus();
3091 testWidget->show();
3092 testWidget->activateWindow();
3093 QVERIFY(QTest::qWaitForWindowActive(testWidget.data()));
3094
3095 QString actualFocusWidget, expectedFocusWidget;
3096 if (!QApplication::focusWidget() && m_platform == QStringLiteral("xcb"))
3097 QSKIP("X11: Your window manager is too broken for this test");
3098
3099 QVERIFY(QApplication::focusWidget());
3100 actualFocusWidget = QString::asprintf(format: "%p %s %s", QApplication::focusWidget(), QApplication::focusWidget()->objectName().toLatin1().constData(), QApplication::focusWidget()->metaObject()->className());
3101 expectedFocusWidget = QString::asprintf(format: "%p %s %s", edit, edit->objectName().toLatin1().constData(), edit->metaObject()->className());
3102 QCOMPARE(actualFocusWidget, expectedFocusWidget);
3103
3104 parentWidget->hide();
3105 QCoreApplication::processEvents();
3106 actualFocusWidget = QString::asprintf(format: "%p %s %s", QApplication::focusWidget(), QApplication::focusWidget()->objectName().toLatin1().constData(), QApplication::focusWidget()->metaObject()->className());
3107 expectedFocusWidget = QString::asprintf(format: "%p %s %s", edit2, edit2->objectName().toLatin1().constData(), edit2->metaObject()->className());
3108 QCOMPARE(actualFocusWidget, expectedFocusWidget);
3109}
3110
3111void tst_QWidget::normalGeometry()
3112{
3113#ifdef Q_OS_MACOS
3114 QSKIP("QTBUG-52974");
3115#endif
3116
3117 if (m_platform == QStringLiteral("wayland"))
3118 QSKIP("Wayland: This fails. Figure out why.");
3119 else if (m_platform == QStringLiteral("winrt"))
3120 QSKIP("WinRT: This fails. Figure out why - QTBUG-68297.");
3121 QWidget parent;
3122 parent.setWindowTitle("NormalGeometry parent");
3123 QWidget *child = new QWidget(&parent);
3124
3125 QCOMPARE(parent.normalGeometry(), parent.geometry());
3126 QCOMPARE(child->normalGeometry(), QRect());
3127
3128 const QRect testGeom = QRect(m_availableTopLeft + QPoint(100 ,100), m_testWidgetSize);
3129 parent.setGeometry(testGeom);
3130 parent.showNormal();
3131 QVERIFY(QTest::qWaitForWindowExposed(&parent));
3132 QApplication::processEvents();
3133
3134 QRect geom = parent.geometry();
3135 // ### the window manager places the top-left corner at
3136 // ### 100,100... making geom something like 102,124 (offset by
3137 // ### the frame/frame)... this indicates a rather large different
3138 // ### between how X11 and Windows works
3139 // QCOMPARE(geom, QRect(100, 100, 200, 200));
3140 QCOMPARE(parent.normalGeometry(), geom);
3141
3142 parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized);
3143 QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized);
3144 QTRY_VERIFY(parent.geometry() != geom);
3145 QTRY_COMPARE(parent.normalGeometry(), geom);
3146
3147 parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized);
3148 QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized));
3149 QTRY_COMPARE(parent.geometry(), geom);
3150 QTRY_COMPARE(parent.normalGeometry(), geom);
3151
3152 parent.showMaximized();
3153 QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized);
3154 QTRY_VERIFY(parent.geometry() != geom);
3155 QCOMPARE(parent.normalGeometry(), geom);
3156
3157 parent.showNormal();
3158 QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized));
3159 QTRY_COMPARE(parent.geometry(), geom);
3160 QCOMPARE(parent.normalGeometry(), geom);
3161
3162 parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized);
3163 QTest::qWait(ms: 10);
3164 parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized);
3165 QTest::qWait(ms: 10);
3166 if (m_platform == QStringLiteral("xcb"))
3167 QSKIP("QTBUG-26424");
3168 QTRY_VERIFY(parent.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized));
3169 // ### when minimized and maximized at the same time, the geometry
3170 // ### does *NOT* have to be the normal geometry, it could be the
3171 // ### maximized geometry.
3172 // QCOMPARE(parent.geometry(), geom);
3173 QTRY_COMPARE(parent.normalGeometry(), geom);
3174
3175 parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized);
3176 QTest::qWait(ms: 10);
3177 QTRY_VERIFY(!(parent.windowState() & Qt::WindowMinimized));
3178 QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized);
3179 QTRY_VERIFY(parent.geometry() != geom);
3180 QTRY_COMPARE(parent.normalGeometry(), geom);
3181
3182 parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized);
3183 QTest::qWait(ms: 10);
3184 QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized));
3185 QTRY_COMPARE(parent.geometry(), geom);
3186 QTRY_COMPARE(parent.normalGeometry(), geom);
3187
3188 parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen);
3189 QTest::qWait(ms: 10);
3190 QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen);
3191 QTRY_VERIFY(parent.geometry() != geom);
3192 QTRY_COMPARE(parent.normalGeometry(), geom);
3193
3194 parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen);
3195 QTest::qWait(ms: 10);
3196 QVERIFY(!(parent.windowState() & Qt::WindowFullScreen));
3197 QTRY_COMPARE(parent.geometry(), geom);
3198 QTRY_COMPARE(parent.normalGeometry(), geom);
3199
3200 parent.showFullScreen();
3201 QTest::qWait(ms: 10);
3202 QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen);
3203 QTRY_VERIFY(parent.geometry() != geom);
3204 QTRY_COMPARE(parent.normalGeometry(), geom);
3205
3206 parent.showNormal();
3207 QTest::qWait(ms: 10);
3208 QTRY_VERIFY(!(parent.windowState() & Qt::WindowFullScreen));
3209 QTRY_COMPARE(parent.geometry(), geom);
3210 QTRY_COMPARE(parent.normalGeometry(), geom);
3211
3212 parent.showNormal();
3213 parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized);
3214 parent.setWindowState(Qt::WindowMinimized | Qt:: WindowFullScreen | Qt::WindowMaximized);
3215 parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized);
3216 QTest::qWait(ms: 10);
3217 QTRY_COMPARE(parent.normalGeometry(), geom);
3218}
3219
3220void tst_QWidget::setGeometry()
3221{
3222 QWidget tlw;
3223 tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3224 QWidget child(&tlw);
3225
3226 const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
3227 const QSize initialSize = 2 * m_testWidgetSize;
3228 QRect tr(topLeft + QPoint(100,100), initialSize);
3229 QRect cr(50,50,50,50);
3230 tlw.setGeometry(tr);
3231 child.setGeometry(cr);
3232 tlw.showNormal();
3233 if (m_platform == QStringLiteral("winrt"))
3234 QEXPECT_FAIL("", "WinRT does not support setGeometry", Abort);
3235 QTRY_COMPARE(tlw.geometry().size(), tr.size());
3236 QCOMPARE(child.geometry(), cr);
3237
3238 tlw.setParent(parent: nullptr, f: Qt::Window|Qt::FramelessWindowHint);
3239 tr = QRect(topLeft, initialSize / 2);
3240 tlw.setGeometry(tr);
3241 QCOMPARE(tlw.geometry(), tr);
3242 tlw.showNormal();
3243 QTest::qWait(ms: 50);
3244 if (tlw.frameGeometry() != tlw.geometry())
3245 QSKIP("Your window manager is too broken for this test");
3246 if (m_platform == QStringLiteral("xcb"))
3247 QSKIP("QTBUG-26424");
3248 QCOMPARE(tlw.geometry(), tr);
3249}
3250
3251void tst_QWidget::setGeometryHidden()
3252{
3253 if (QGuiApplication::styleHints()->showIsMaximized())
3254 QSKIP("Platform does not support QWidget::setGeometry() - skipping");
3255
3256 QWidget tlw;
3257 tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3258 QWidget child(&tlw);
3259
3260 const QRect tr(m_availableTopLeft + QPoint(100, 100), 2 * m_testWidgetSize);
3261 const QRect cr(QPoint(50, 50), m_testWidgetSize);
3262 tlw.setGeometry(tr);
3263 child.setGeometry(cr);
3264 tlw.showNormal();
3265
3266 tlw.hide();
3267 QTRY_VERIFY(tlw.isHidden());
3268 tlw.setGeometry(cr);
3269 QVERIFY(tlw.testAttribute(Qt::WA_PendingMoveEvent));
3270 QVERIFY(tlw.testAttribute(Qt::WA_PendingResizeEvent));
3271 QImage img(tlw.size(), QImage::Format_ARGB32); // just needed to call QWidget::render()
3272 tlw.render(target: &img);
3273 QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent));
3274 QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent));
3275 tlw.setGeometry(cr);
3276 QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent));
3277 QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent));
3278 tlw.resize(cr.size());
3279 QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent));
3280 QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent));
3281}
3282
3283void tst_QWidget::windowOpacity()
3284{
3285 QWidget widget;
3286 QWidget child(&widget);
3287
3288 // Initial value should be 1.0
3289 QCOMPARE(widget.windowOpacity(), 1.0);
3290 // children should always return 1.0
3291 QCOMPARE(child.windowOpacity(), 1.0);
3292
3293 widget.setWindowOpacity(0.0);
3294 QCOMPARE(widget.windowOpacity(), 0.0);
3295 child.setWindowOpacity(0.0);
3296 QCOMPARE(child.windowOpacity(), 1.0);
3297
3298 widget.setWindowOpacity(1.0);
3299 QCOMPARE(widget.windowOpacity(), 1.0);
3300 child.setWindowOpacity(1.0);
3301 QCOMPARE(child.windowOpacity(), 1.0);
3302
3303 widget.setWindowOpacity(2.0);
3304 QCOMPARE(widget.windowOpacity(), 1.0);
3305 child.setWindowOpacity(2.0);
3306 QCOMPARE(child.windowOpacity(), 1.0);
3307
3308 widget.setWindowOpacity(-1.0);
3309 QCOMPARE(widget.windowOpacity(), 0.0);
3310 child.setWindowOpacity(-1.0);
3311 QCOMPARE(child.windowOpacity(), 1.0);
3312}
3313
3314class UpdateWidget : public QWidget
3315{
3316public:
3317 explicit UpdateWidget(QWidget *parent = nullptr) : QWidget(parent)
3318 {
3319 setObjectName(QLatin1String("UpdateWidget"));
3320 reset();
3321 }
3322
3323 void paintEvent(QPaintEvent *e) override
3324 {
3325 paintedRegion += e->region();
3326 ++numPaintEvents;
3327 if (resizeInPaintEvent) {
3328 resizeInPaintEvent = false;
3329 resize(size() + QSize(2, 2));
3330 }
3331 }
3332
3333 bool event(QEvent *event) override
3334 {
3335 switch (event->type()) {
3336 case QEvent::ZOrderChange:
3337 ++numZOrderChangeEvents;
3338 break;
3339 case QEvent::UpdateRequest:
3340 ++numUpdateRequestEvents;
3341 break;
3342 case QEvent::ActivationChange:
3343 case QEvent::FocusIn:
3344 case QEvent::FocusOut:
3345 case QEvent::WindowActivate:
3346 case QEvent::WindowDeactivate:
3347 if (!updateOnActivationChangeAndFocusIn)
3348 return true; // Filter out to avoid update() calls in QWidget.
3349 break;
3350 default:
3351 break;
3352 }
3353 return QWidget::event(event);
3354 }
3355
3356 void reset()
3357 {
3358 numPaintEvents = numZOrderChangeEvents = numUpdateRequestEvents = 0;
3359 updateOnActivationChangeAndFocusIn = resizeInPaintEvent = false;
3360 paintedRegion = QRegion();
3361 }
3362
3363 int numPaintEvents;
3364 int numZOrderChangeEvents;
3365 int numUpdateRequestEvents;
3366 bool updateOnActivationChangeAndFocusIn;
3367 bool resizeInPaintEvent;
3368 QRegion paintedRegion;
3369};
3370
3371void tst_QWidget::lostUpdatesOnHide()
3372{
3373#ifndef Q_OS_MACOS
3374 UpdateWidget widget;
3375 widget.setAttribute(Qt::WA_DontShowOnScreen);
3376 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3377 widget.show();
3378 widget.hide();
3379 QTest::qWait(ms: 50);
3380 widget.show();
3381 QTest::qWait(ms: 50);
3382
3383 QCOMPARE(widget.numPaintEvents, 1);
3384#endif
3385}
3386
3387void tst_QWidget::raise()
3388{
3389 QScopedPointer<QWidget> parentPtr(new QWidget);
3390 parentPtr->resize(w: 200, h: 200);
3391 parentPtr->setObjectName(QLatin1String("raise"));
3392 parentPtr->setWindowTitle(parentPtr->objectName());
3393 QList<UpdateWidget *> allChildren;
3394
3395 UpdateWidget *child1 = new UpdateWidget(parentPtr.data());
3396 child1->setAutoFillBackground(true);
3397 allChildren.append(t: child1);
3398
3399 UpdateWidget *child2 = new UpdateWidget(parentPtr.data());
3400 child2->setAutoFillBackground(true);
3401 allChildren.append(t: child2);
3402
3403 UpdateWidget *child3 = new UpdateWidget(parentPtr.data());
3404 child3->setAutoFillBackground(true);
3405 allChildren.append(t: child3);
3406
3407 UpdateWidget *child4 = new UpdateWidget(parentPtr.data());
3408 child4->setAutoFillBackground(true);
3409 allChildren.append(t: child4);
3410
3411 parentPtr->show();
3412 QVERIFY(QTest::qWaitForWindowExposed(parentPtr.data()));
3413
3414#ifdef Q_OS_MACOS
3415 if (child1->internalWinId()) {
3416 QSKIP("Cocoa has no Z-Order for views, we hack it, but it results in paint events.");
3417 }
3418#endif
3419
3420 QObjectList list1{child1, child2, child3, child4};
3421 QCOMPARE(parentPtr->children(), list1);
3422 QCOMPARE(allChildren.count(), list1.count());
3423
3424 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3425 int expectedPaintEvents = child == child4 ? 1 : 0;
3426 if (expectedPaintEvents == 0) {
3427 QCOMPARE(child->numPaintEvents, 0);
3428 } else {
3429 // show() issues multiple paint events on some window managers
3430 QTRY_VERIFY(child->numPaintEvents >= expectedPaintEvents);
3431 }
3432 QCOMPARE(child->numZOrderChangeEvents, 0);
3433 child->reset();
3434 }
3435
3436 for (int i = 0; i < 5; ++i)
3437 child2->raise();
3438 QTest::qWait(ms: 50);
3439
3440 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3441 int expectedPaintEvents = child == child2 ? 1 : 0;
3442 int expectedZOrderChangeEvents = child == child2 ? 1 : 0;
3443 QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
3444 QCOMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
3445 child->reset();
3446 }
3447
3448 QList<QObject *> list2;
3449 list2 << child1 << child3 << child4 << child2;
3450 QCOMPARE(parentPtr->children(), list2);
3451
3452 // Creates a widget on top of all the children and checks that raising one of
3453 // the children underneath doesn't trigger a repaint on the covering widget.
3454 QWidget topLevel;
3455 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3456 QWidget *parent = parentPtr.take();
3457 parent->setParent(&topLevel);
3458 topLevel.show();
3459
3460 UpdateWidget *onTop = new UpdateWidget(&topLevel);
3461 onTop->reset();
3462 onTop->resize(topLevel.size());
3463 onTop->setAutoFillBackground(true);
3464 onTop->show();
3465 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
3466 QTRY_VERIFY(onTop->numPaintEvents > 0);
3467 onTop->reset();
3468
3469 // Reset all the children.
3470 for (UpdateWidget *child : qAsConst(t&: allChildren))
3471 child->reset();
3472
3473 for (int i = 0; i < 5; ++i)
3474 child3->raise();
3475 QTest::qWait(ms: 50);
3476
3477 QCOMPARE(onTop->numPaintEvents, 0);
3478 QCOMPARE(onTop->numZOrderChangeEvents, 0);
3479
3480 QObjectList list3{child1, child4, child2, child3};
3481 QCOMPARE(parent->children(), list3);
3482
3483 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3484 int expectedPaintEvents = 0;
3485 int expectedZOrderChangeEvents = child == child3 ? 1 : 0;
3486 QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
3487 QCOMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
3488 child->reset();
3489 }
3490}
3491
3492void tst_QWidget::lower()
3493{
3494 QScopedPointer<QWidget> parent(new QWidget);
3495 parent->setObjectName(QLatin1String("lower"));
3496 parent->setWindowTitle(parent->objectName());
3497 parent->resize(w: 200, h: 200);
3498 QList<UpdateWidget *> allChildren;
3499
3500 UpdateWidget *child1 = new UpdateWidget(parent.data());
3501 child1->setAutoFillBackground(true);
3502 allChildren.append(t: child1);
3503
3504 UpdateWidget *child2 = new UpdateWidget(parent.data());
3505 child2->setAutoFillBackground(true);
3506 allChildren.append(t: child2);
3507
3508 UpdateWidget *child3 = new UpdateWidget(parent.data());
3509 child3->setAutoFillBackground(true);
3510 allChildren.append(t: child3);
3511
3512 UpdateWidget *child4 = new UpdateWidget(parent.data());
3513 child4->setAutoFillBackground(true);
3514 allChildren.append(t: child4);
3515
3516 parent->show();
3517 QVERIFY(QTest::qWaitForWindowExposed(parent.data()));
3518
3519 QObjectList list1{child1, child2, child3, child4};
3520 QCOMPARE(parent->children(), list1);
3521 QCOMPARE(allChildren.count(), list1.count());
3522
3523 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3524 int expectedPaintEvents = child == child4 ? 1 : 0;
3525 if (expectedPaintEvents == 0) {
3526 QCOMPARE(child->numPaintEvents, 0);
3527 } else {
3528 // show() issues multiple paint events on some window managers
3529 QTRY_VERIFY(child->numPaintEvents >= expectedPaintEvents);
3530 }
3531 QCOMPARE(child->numZOrderChangeEvents, 0);
3532 child->reset();
3533 }
3534
3535 for (int i = 0; i < 5; ++i)
3536 child4->lower();
3537
3538 QTest::qWait(ms: 100);
3539
3540 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3541 int expectedPaintEvents = child == child3 ? 1 : 0;
3542 int expectedZOrderChangeEvents = child == child4 ? 1 : 0;
3543 QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
3544 QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
3545 child->reset();
3546 }
3547
3548 QList<QObject *> list2;
3549 list2 << child4 << child1 << child2 << child3;
3550 QCOMPARE(parent->children(), list2);
3551}
3552
3553void tst_QWidget::stackUnder()
3554{
3555#ifdef Q_OS_MACOS
3556 QSKIP("QTBUG-52974: Cocoa has no Z-Order for views, we hack it, but it results in paint events.");
3557#endif
3558
3559 QScopedPointer<QWidget> parent(new QWidget);
3560 parent->setObjectName(QLatin1String("stackUnder"));
3561 parent->setWindowTitle(parent->objectName());
3562 parent->resize(w: 200, h: 200);
3563 QList<UpdateWidget *> allChildren;
3564
3565 UpdateWidget *child1 = new UpdateWidget(parent.data());
3566 child1->setAutoFillBackground(true);
3567 allChildren.append(t: child1);
3568
3569 UpdateWidget *child2 = new UpdateWidget(parent.data());
3570 child2->setAutoFillBackground(true);
3571 allChildren.append(t: child2);
3572
3573 UpdateWidget *child3 = new UpdateWidget(parent.data());
3574 child3->setAutoFillBackground(true);
3575 allChildren.append(t: child3);
3576
3577 UpdateWidget *child4 = new UpdateWidget(parent.data());
3578 child4->setAutoFillBackground(true);
3579 allChildren.append(t: child4);
3580
3581 parent->show();
3582 QVERIFY(QTest::qWaitForWindowExposed(parent.data()));
3583 QObjectList list1{child1, child2, child3, child4};
3584 QCOMPARE(parent->children(), list1);
3585
3586 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3587 int expectedPaintEvents = child == child4 ? 1 : 0;
3588#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
3589 if (expectedPaintEvents == 1 && child->numPaintEvents == 2)
3590 QEXPECT_FAIL(0, "Mac and Windows issues double repaints for Z-Order change", Continue);
3591#endif
3592 QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
3593 QCOMPARE(child->numZOrderChangeEvents, 0);
3594 child->reset();
3595 }
3596
3597 for (int i = 0; i < 5; ++i)
3598 child4->stackUnder(child2);
3599 QTest::qWait(ms: 10);
3600
3601 QObjectList list2{child1, child4, child2, child3};
3602 QCOMPARE(parent->children(), list2);
3603
3604 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3605 int expectedPaintEvents = child == child3 ? 1 : 0;
3606 int expectedZOrderChangeEvents = child == child4 ? 1 : 0;
3607 QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
3608 QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
3609 child->reset();
3610 }
3611
3612 for (int i = 0; i < 5; ++i)
3613 child1->stackUnder(child3);
3614 QTest::qWait(ms: 10);
3615
3616 QObjectList list3{child4, child2, child1, child3};
3617 QCOMPARE(parent->children(), list3);
3618
3619 for (UpdateWidget *child : qAsConst(t&: allChildren)) {
3620 int expectedZOrderChangeEvents = child == child1 ? 1 : 0;
3621 if (child == child3) {
3622#ifndef Q_OS_MACOS
3623 QEXPECT_FAIL(0, "See QTBUG-493", Continue);
3624#endif
3625 QCOMPARE(child->numPaintEvents, 0);
3626 } else {
3627 QCOMPARE(child->numPaintEvents, 0);
3628 }
3629 QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
3630 child->reset();
3631 }
3632}
3633
3634void drawPolygon(QPaintDevice *dev, int w, int h)
3635{
3636 QPainter p(dev);
3637 p.fillRect(x: 0, y: 0, w, h, c: Qt::white);
3638
3639 QPolygon a;
3640 a << QPoint(0, 0) << QPoint(w/2, h/2) << QPoint(w, 0)
3641 << QPoint(w/2, h) << QPoint(0, 0);
3642
3643 p.setPen(QPen(Qt::black, 1));
3644 p.setBrush(Qt::DiagCrossPattern);
3645 p.drawPolygon(polygon: a);
3646}
3647
3648class ContentsPropagationWidget : public QWidget
3649{
3650 Q_OBJECT
3651public:
3652 explicit ContentsPropagationWidget(QWidget *parent = nullptr) : QWidget(parent)
3653 {
3654 setObjectName(QLatin1String("ContentsPropagationWidget"));
3655 setWindowTitle(objectName());
3656 QWidget *child = this;
3657 for (int i = 0; i < 32; ++i) {
3658 child = new QWidget(child);
3659 child->setGeometry(ax: i, ay: i, aw: 400 - i * 2, ah: 400 - i * 2);
3660 }
3661 }
3662
3663 void setContentsPropagation(bool enable)
3664 {
3665 for (QObject *child : children())
3666 qobject_cast<QWidget *>(o: child)->setAutoFillBackground(!enable);
3667 }
3668
3669protected:
3670 void paintEvent(QPaintEvent *) override
3671 {
3672 int w = width(), h = height();
3673 drawPolygon(dev: this, w, h);
3674 }
3675
3676 QSize sizeHint() const override { return {500, 500}; }
3677};
3678
3679// Scale to remove devicePixelRatio should scaling be active.
3680static QPixmap grabFromWidget(QWidget *w, const QRect &rect)
3681{
3682 QPixmap pixmap = w->grab(rectangle: rect);
3683 const qreal devicePixelRatio = pixmap.devicePixelRatioF();
3684 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
3685 pixmap = pixmap.scaled(s: (QSizeF(pixmap.size()) / devicePixelRatio).toSize(),
3686 aspectMode: Qt::KeepAspectRatio, mode: Qt::SmoothTransformation);
3687 pixmap.setDevicePixelRatio(1);
3688 }
3689 return pixmap;
3690}
3691
3692void tst_QWidget::testContentsPropagation()
3693{
3694 if (!qFuzzyCompare(qApp->devicePixelRatio(), p2: qreal(1)))
3695 QSKIP("This test does not work with scaling.");
3696 ContentsPropagationWidget widget;
3697 widget.setFixedSize(w: 500, h: 500);
3698 widget.setContentsPropagation(false);
3699 QPixmap widgetSnapshot = widget.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1)));
3700
3701 QPixmap correct(500, 500);
3702 drawPolygon(dev: &correct, w: 500, h: 500);
3703 //correct.save("correct.png", "PNG");
3704
3705 //widgetSnapshot.save("snap1.png", "PNG");
3706 QVERIFY(widgetSnapshot.toImage() != correct.toImage());
3707
3708 widget.setContentsPropagation(true);
3709 widgetSnapshot = widgetSnapshot = widget.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1)));
3710 //widgetSnapshot.save("snap2.png", "PNG");
3711
3712 QCOMPARE(widgetSnapshot, correct);
3713}
3714
3715/*
3716 Test that saving and restoring window geometry with
3717 saveGeometry() and restoreGeometry() works.
3718*/
3719
3720void tst_QWidget::saveRestoreGeometry()
3721{
3722#ifdef Q_OS_MACOS
3723 QSKIP("QTBUG-52974");
3724#endif
3725
3726 if (m_platform == QStringLiteral("wayland"))
3727 QSKIP("Wayland: This fails. Figure out why.");
3728 const QPoint position = m_availableTopLeft + QPoint(100, 100);
3729 const QSize size = m_testWidgetSize;
3730
3731 QByteArray savedGeometry;
3732
3733 {
3734 QWidget widget;
3735 widget.move(position);
3736 widget.resize(size);
3737 widget.showNormal();
3738 QVERIFY(QTest::qWaitForWindowExposed(&widget));
3739 QApplication::processEvents();
3740
3741 if (m_platform == QStringLiteral("winrt"))
3742 QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
3743 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
3744 qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
3745 QCOMPARE(widget.size(), size);
3746 savedGeometry = widget.saveGeometry();
3747 }
3748
3749 {
3750 QWidget widget;
3751 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
3752
3753 const QByteArray empty;
3754 const QByteArray one("a");
3755 const QByteArray two("ab");
3756 const QByteArray three("abc");
3757 const QByteArray four("abca");
3758 const QByteArray garbage("abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc");
3759
3760 QVERIFY(!widget.restoreGeometry(empty));
3761 QVERIFY(!widget.restoreGeometry(one));
3762 QVERIFY(!widget.restoreGeometry(two));
3763 QVERIFY(!widget.restoreGeometry(three));
3764 QVERIFY(!widget.restoreGeometry(four));
3765 QVERIFY(!widget.restoreGeometry(garbage));
3766
3767 QVERIFY(widget.restoreGeometry(savedGeometry));
3768 widget.showNormal();
3769 QVERIFY(QTest::qWaitForWindowExposed(&widget));
3770 QApplication::processEvents();
3771
3772 QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
3773 qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
3774 QCOMPARE(widget.size(), size);
3775 widget.show();
3776 QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
3777 qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
3778 QCOMPARE(widget.size(), size);
3779 }
3780
3781 {
3782 QWidget widget;
3783 widget.move(position);
3784 widget.resize(size);
3785 widget.showNormal();
3786 QVERIFY(QTest::qWaitForWindowExposed(&widget));
3787 QTRY_COMPARE(widget.geometry().size(), size);
3788
3789 QRect geom;
3790
3791 //Restore from Full screen
3792 savedGeometry = widget.saveGeometry();
3793 geom = widget.geometry();
3794 widget.setWindowState(widget.windowState() | Qt::WindowFullScreen);
3795 QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen));
3796 QTest::qWait(ms: 500);
3797 QVERIFY(widget.restoreGeometry(savedGeometry));
3798 QTest::qWait(ms: 120);
3799 QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen));
3800 QTRY_COMPARE(widget.geometry(), geom);
3801
3802 //Restore to full screen
3803 widget.setWindowState(widget.windowState() | Qt::WindowFullScreen);
3804 QTest::qWait(ms: 120);
3805 QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen));
3806 QTest::qWait(ms: 500);
3807 savedGeometry = widget.saveGeometry();
3808 geom = widget.geometry();
3809 widget.setWindowState(widget.windowState() ^ Qt::WindowFullScreen);
3810 QTest::qWait(ms: 120);
3811 QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen));
3812 QTest::qWait(ms: 400);
3813 QVERIFY(widget.restoreGeometry(savedGeometry));
3814 QTest::qWait(ms: 120);
3815 QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen));
3816 QTRY_COMPARE(widget.geometry(), geom);
3817 QVERIFY((widget.windowState() & Qt::WindowFullScreen));
3818 widget.setWindowState(widget.windowState() ^ Qt::WindowFullScreen);
3819 QTest::qWait(ms: 120);
3820 QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen));
3821 QTest::qWait(ms: 120);
3822
3823 //Restore from Maximised
3824 widget.move(position);
3825 widget.resize(size);
3826 QTest::qWait(ms: 10);
3827 QTRY_COMPARE(widget.size(), size);
3828 QTest::qWait(ms: 500);
3829 savedGeometry = widget.saveGeometry();
3830 geom = widget.geometry();
3831 widget.setWindowState(widget.windowState() | Qt::WindowMaximized);
3832 QTest::qWait(ms: 120);
3833 QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized));
3834 QTRY_VERIFY(widget.geometry() != geom);
3835 QTest::qWait(ms: 500);
3836 QVERIFY(widget.restoreGeometry(savedGeometry));
3837 QTest::qWait(ms: 120);
3838 QTRY_COMPARE(widget.geometry(), geom);
3839
3840 QVERIFY(!(widget.windowState() & Qt::WindowMaximized));
3841
3842 //Restore to maximised
3843 widget.setWindowState(widget.windowState() | Qt::WindowMaximized);
3844 QTest::qWait(ms: 120);
3845 QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized));
3846 QTest::qWait(ms: 500);
3847 geom = widget.geometry();
3848 savedGeometry = widget.saveGeometry();
3849 widget.setWindowState(widget.windowState() ^ Qt::WindowMaximized);
3850 QTest::qWait(ms: 120);
3851 QTRY_VERIFY(!(widget.windowState() & Qt::WindowMaximized));
3852 QTest::qWait(ms: 500);
3853 QVERIFY(widget.restoreGeometry(savedGeometry));
3854 QTest::qWait(ms: 120);
3855 QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized));
3856 QTRY_COMPARE(widget.geometry(), geom);
3857 }
3858}
3859
3860void tst_QWidget::restoreVersion1Geometry_data()
3861{
3862 if (m_platform == QStringLiteral("wayland"))
3863 QSKIP("Wayland: This fails. Figure out why.");
3864 QTest::addColumn<QString>(name: "fileName");
3865 QTest::addColumn<Qt::WindowState>(name: "expectedWindowState");
3866 QTest::addColumn<QPoint>(name: "expectedPosition");
3867 QTest::addColumn<QSize>(name: "expectedSize");
3868 QTest::addColumn<QRect>(name: "expectedNormalGeometry");
3869 const QPoint position(100, 100);
3870 const QSize size(200, 200);
3871 const QRect normalGeometry(102, 124, 200, 200);
3872
3873 QTest::newRow(dataTag: "geometry.dat") << ":geometry.dat" << Qt::WindowNoState << position << size << normalGeometry;
3874 QTest::newRow(dataTag: "geometry-maximized.dat") << ":geometry-maximized.dat" << Qt::WindowMaximized << position << size << normalGeometry;
3875 QTest::newRow(dataTag: "geometry-fullscreen.dat") << ":geometry-fullscreen.dat" << Qt::WindowFullScreen << position << size << normalGeometry;
3876}
3877
3878/*
3879 Test that the current version of restoreGeometry() can restore geometry
3880 saved width saveGeometry() version 1.0.
3881*/
3882void tst_QWidget::restoreVersion1Geometry()
3883{
3884 QFETCH(QString, fileName);
3885 QFETCH(Qt::WindowState, expectedWindowState);
3886 QFETCH(QPoint, expectedPosition);
3887 Q_UNUSED(expectedPosition);
3888 QFETCH(QSize, expectedSize);
3889 QFETCH(QRect, expectedNormalGeometry);
3890
3891 if (m_platform == QLatin1String("windows") && QGuiApplication::primaryScreen()->geometry().width() > 2000)
3892 QSKIP("Skipping due to minimum decorated window size on Windows");
3893
3894 // WindowActive is uninteresting for this test
3895 const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized;
3896
3897 QFile f(fileName);
3898 QVERIFY(f.exists());
3899 f.open(flags: QIODevice::ReadOnly);
3900 const QByteArray savedGeometry = f.readAll();
3901 QCOMPARE(savedGeometry.count(), 46);
3902 f.close();
3903
3904 QWidget widget;
3905 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
3906 + QLatin1String(QTest::currentDataTag()));
3907
3908 QVERIFY(widget.restoreGeometry(savedGeometry));
3909
3910 QCOMPARE(widget.windowState() & WindowStateMask, expectedWindowState);
3911 if (expectedWindowState == Qt::WindowNoState) {
3912 QTRY_COMPARE(widget.geometry(), expectedNormalGeometry);
3913 QCOMPARE(widget.size(), expectedSize);
3914 }
3915
3916 widget.showNormal();
3917 QVERIFY(QTest::qWaitForWindowExposed(&widget));
3918 QTest::qWait(ms: 100);
3919
3920 if (m_platform == QStringLiteral("winrt"))
3921 QEXPECT_FAIL("", "WinRT does not support restoreGeometry", Abort);
3922
3923 if (expectedWindowState == Qt::WindowNoState) {
3924 QTRY_COMPARE(widget.size(), expectedSize);
3925 QCOMPARE(widget.geometry(), expectedNormalGeometry);
3926 }
3927
3928 widget.showNormal();
3929 QTest::qWait(ms: 10);
3930
3931 QTRY_COMPARE(widget.geometry(), expectedNormalGeometry);
3932 if (expectedWindowState == Qt::WindowNoState)
3933 QCOMPARE(widget.size(), expectedSize);
3934
3935#if 0
3936 // Code for saving a new geometry*.dat files
3937 {
3938 QWidget widgetToSave;
3939 widgetToSave.move(expectedPosition);
3940 widgetToSave.resize(expectedSize);
3941 widgetToSave.show();
3942 QVERIFY(QTest::qWaitForWindowExposed(&widget));
3943 QTest::qWait(500); // stabilize
3944 widgetToSave.setWindowState(Qt::WindowStates(expectedWindowState));
3945 QTest::qWait(500); // stabilize
3946
3947 QByteArray geometryToSave = widgetToSave.saveGeometry();
3948
3949 // Code for saving a new geometry.dat file.
3950 f.setFileName(fileName.mid(1));
3951 QVERIFY(f.open(QIODevice::WriteOnly)); // did you forget to 'p4 edit *.dat'? :)
3952 f.write(geometryToSave);
3953 f.close();
3954 }
3955#endif
3956}
3957
3958void tst_QWidget::widgetAt()
3959{
3960#ifdef Q_OS_MACOS
3961 QSKIP("QTBUG-52974");
3962#endif
3963
3964 if (m_platform == QStringLiteral("wayland"))
3965 QSKIP("Wayland: This fails. Figure out why.");
3966 if (m_platform == QStringLiteral("offscreen"))
3967 QSKIP("Platform offscreen does not support lower()/raise() or WindowMasks");
3968 if (m_platform == QStringLiteral("winrt"))
3969 QSKIP("WinRT does not support more than 1 top level widget");
3970
3971 Q_CHECK_PAINTEVENTS
3972
3973 const QPoint referencePos = m_availableTopLeft + QPoint(100, 100);
3974 QScopedPointer<QWidget> w1(new QWidget(nullptr, Qt::X11BypassWindowManagerHint));
3975 w1->setGeometry(QRect(referencePos, QSize(m_testWidgetSize.width(), 150)));
3976 w1->setObjectName(QLatin1String("w1"));
3977 w1->setWindowTitle(w1->objectName());
3978 QScopedPointer<QWidget> w2(new QWidget(nullptr, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint));
3979 w2->setGeometry(QRect(referencePos + QPoint(50, 50), QSize(m_testWidgetSize.width(), 100)));
3980 w2->setObjectName(QLatin1String("w2"));
3981 w2->setWindowTitle(w2->objectName());
3982 w1->showNormal();
3983 QVERIFY(QTest::qWaitForWindowExposed(w1.data()));
3984 const QPoint testPos = referencePos + QPoint(100, 100);
3985 QWidget *wr;
3986 QTRY_VERIFY((wr = QApplication::widgetAt((testPos))));
3987 QCOMPARE(wr->objectName(), QString("w1"));
3988
3989 w2->showNormal();
3990 QVERIFY(QTest::qWaitForWindowExposed(w2.data()));
3991 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)));
3992 QCOMPARE(wr->objectName(), QString("w2"));
3993
3994 w2->lower();
3995 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w1"));
3996 w2->raise();
3997
3998 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w2"));
3999
4000 QWidget *w3 = new QWidget(w2.data());
4001 w3->setGeometry(ax: 10,ay: 10,aw: 50,ah: 50);
4002 w3->setObjectName("w3");
4003 w3->showNormal();
4004 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w3"));
4005
4006 w3->setAttribute(Qt::WA_TransparentForMouseEvents);
4007 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w2"));
4008
4009 if (!QGuiApplicationPrivate::platformIntegration()
4010 ->hasCapability(cap: QPlatformIntegration::WindowMasks)) {
4011 QSKIP("Platform does not support WindowMasks");
4012 }
4013
4014 QRegion rgn = QRect(QPoint(0,0), w2->size());
4015 QPoint point = w2->mapFromGlobal(testPos);
4016 rgn -= QRect(point, QSize(1,1));
4017 w2->setMask(rgn);
4018
4019 QTRY_VERIFY((wr = QApplication::widgetAt(testPos)));
4020 QTRY_COMPARE(wr->objectName(), w1->objectName());
4021 QTRY_VERIFY((wr = QApplication::widgetAt(testPos + QPoint(1, 1))));
4022 QTRY_COMPARE(wr->objectName(), w2->objectName());
4023
4024 QBitmap bitmap(w2->size());
4025 QPainter p(&bitmap);
4026 p.fillRect(r: bitmap.rect(), c: Qt::color1);
4027 p.setPen(Qt::color0);
4028 p.drawPoint(p: w2->mapFromGlobal(testPos));
4029 p.end();
4030 w2->setMask(bitmap);
4031 QTRY_COMPARE(QApplication::widgetAt(testPos), w1.data());
4032 QTRY_VERIFY(QApplication::widgetAt(testPos + QPoint(1, 1)) == w2.data());
4033}
4034
4035void tst_QWidget::task110173()
4036{
4037 QWidget w;
4038 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4039
4040 QPushButton *pb1 = new QPushButton("click", &w);
4041 pb1->setFocusPolicy(Qt::ClickFocus);
4042 pb1->move(ax: 100, ay: 100);
4043
4044 QPushButton *pb2 = new QPushButton("push", &w);
4045 pb2->setFocusPolicy(Qt::ClickFocus);
4046 pb2->move(ax: 300, ay: 300);
4047
4048 QTest::keyClick( widget: &w, key: Qt::Key_Tab );
4049 w.show();
4050 QVERIFY(QTest::qWaitForWindowExposed(&w));
4051}
4052
4053class Widget : public QWidget
4054{
4055public:
4056 Widget() { setFocusPolicy(Qt::StrongFocus); }
4057 void actionEvent(QActionEvent *) override { if (deleteThis) delete this; }
4058 void changeEvent(QEvent *) override { if (deleteThis) delete this; }
4059 void closeEvent(QCloseEvent *) override { if (deleteThis) delete this; }
4060 void hideEvent(QHideEvent *) override { if (deleteThis) delete this; }
4061 void focusOutEvent(QFocusEvent *) override { if (deleteThis) delete this; }
4062 void keyPressEvent(QKeyEvent *) override { if (deleteThis) delete this; }
4063 void keyReleaseEvent(QKeyEvent *) override { if (deleteThis) delete this; }
4064 void mouseDoubleClickEvent(QMouseEvent *) override { if (deleteThis) delete this; }
4065 void mousePressEvent(QMouseEvent *) override { if (deleteThis) delete this; }
4066 void mouseReleaseEvent(QMouseEvent *) override { if (deleteThis) delete this; }
4067 void mouseMoveEvent(QMouseEvent *) override { if (deleteThis) delete this; }
4068
4069 bool deleteThis = false;
4070};
4071
4072void tst_QWidget::testDeletionInEventHandlers()
4073{
4074 // closeEvent
4075 QPointer<Widget> w = new Widget;
4076 w->deleteThis = true;
4077 w->close();
4078 QVERIFY(w.isNull());
4079 delete w;
4080
4081 // focusOut (crashes)
4082 //w = new Widget;
4083 //w->show();
4084 //w->setFocus();
4085 //QCOMPARE(qApp->focusWidget(), w);
4086 //w->deleteThis = true;
4087 //w->clearFocus();
4088 //QVERIFY(w.isNull());
4089
4090 // key press
4091 w = new Widget;
4092 w->show();
4093 w->deleteThis = true;
4094 QTest::keyPress(widget: w, key: Qt::Key_A);
4095 QVERIFY(w.isNull());
4096 delete w;
4097
4098 // key release
4099 w = new Widget;
4100 w->show();
4101 w->deleteThis = true;
4102 QTest::keyRelease(widget: w, key: Qt::Key_A);
4103 QVERIFY(w.isNull());
4104 delete w;
4105
4106 // mouse press
4107 w = new Widget;
4108 w->show();
4109 w->deleteThis = true;
4110 QTest::mousePress(widget: w, button: Qt::LeftButton);
4111 QVERIFY(w.isNull());
4112 delete w;
4113
4114 // mouse release
4115 w = new Widget;
4116 w->show();
4117 w->deleteThis = true;
4118 QMouseEvent me(QEvent::MouseButtonRelease, QPoint(1, 1), Qt::LeftButton, Qt::LeftButton, Qt::KeyboardModifiers());
4119 qApp->notify(w, &me);
4120 QVERIFY(w.isNull());
4121 delete w;
4122
4123 // mouse double click
4124 w = new Widget;
4125 w->show();
4126 w->deleteThis = true;
4127 QTest::mouseDClick(widget: w, button: Qt::LeftButton);
4128 QVERIFY(w.isNull());
4129 delete w;
4130
4131 // hide event (crashes)
4132 //w = new Widget;
4133 //w->show();
4134 //w->deleteThis = true;
4135 //w->hide();
4136 //QVERIFY(w.isNull());
4137
4138 // action event
4139 w = new Widget;
4140 w->deleteThis = true;
4141 w->addAction(action: new QAction(w));
4142 QVERIFY(w.isNull());
4143 delete w;
4144
4145 // change event
4146 w = new Widget;
4147 w->show();
4148 w->deleteThis = true;
4149 w->setMouseTracking(true);
4150 QVERIFY(w.isNull());
4151 delete w;
4152
4153 w = new Widget;
4154 w->setMouseTracking(true);
4155 w->show();
4156 w->deleteThis = true;
4157 me = QMouseEvent(QEvent::MouseMove, QPoint(0, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
4158 QApplication::sendEvent(receiver: w, event: &me);
4159 QVERIFY(w.isNull());
4160 delete w;
4161}
4162
4163#ifdef Q_OS_MACOS
4164class MaskedPainter : public QWidget
4165{
4166public:
4167 QRect mask;
4168
4169 MaskedPainter()
4170 : mask(20, 20, 50, 50)
4171 {
4172 setMask(mask);
4173 }
4174
4175 void paintEvent(QPaintEvent *)
4176 {
4177 QPainter p(this);
4178 p.fillRect(mask, QColor(Qt::red));
4179 }
4180};
4181
4182/*
4183 Verifies that the entire area inside the mask is painted red.
4184*/
4185bool verifyWidgetMask(QWidget *widget, QRect mask)
4186{
4187 const QImage image = widget->grab(QRect(QPoint(0, 0), widget->size())).toImage();
4188
4189 const QImage masked = image.copy(mask);
4190 QImage red(masked);
4191 red.fill(QColor(Qt::red).rgb());
4192
4193 return (masked == red);
4194}
4195
4196void tst_QWidget::setMask()
4197{
4198 {
4199 MaskedPainter w;
4200 w.resize(200, 200);
4201 w.show();
4202 QTest::qWait(100);
4203 QVERIFY(verifyWidgetMask(&w, w.mask));
4204 }
4205 {
4206 MaskedPainter w;
4207 w.resize(200, 200);
4208 w.setWindowFlags(w.windowFlags() | Qt::FramelessWindowHint);
4209 w.show();
4210 QTest::qWait(100);
4211 QRect mask = w.mask;
4212
4213 QVERIFY(verifyWidgetMask(&w, mask));
4214 }
4215}
4216#endif
4217
4218class StaticWidget : public QWidget
4219{
4220Q_OBJECT
4221public:
4222 bool partial = false;
4223 bool gotPaintEvent = false;
4224 QRegion paintedRegion;
4225
4226 explicit StaticWidget(QWidget *parent = nullptr) : QWidget(parent)
4227 {
4228 setAttribute(Qt::WA_StaticContents);
4229 setAttribute(Qt::WA_OpaquePaintEvent);
4230 setPalette(Qt::red); // Make sure we have an opaque palette.
4231 setAutoFillBackground(true);
4232 }
4233
4234 void paintEvent(QPaintEvent *e) override
4235 {
4236 paintedRegion += e->region();
4237 gotPaintEvent = true;
4238// qDebug() << "paint" << e->region();
4239 // Look for a full update, set partial to false if found.
4240 for (QRect r : e->region()) {
4241 partial = (r != rect());
4242 if (!partial)
4243 break;
4244 }
4245 }
4246};
4247
4248/*
4249 Test that widget resizes and moves can be done with minimal repaints when WA_StaticContents
4250 and WA_OpaquePaintEvent is set. Test is mac-only for now.
4251*/
4252void tst_QWidget::optimizedResizeMove()
4253{
4254 if (m_platform == QStringLiteral("wayland"))
4255 QSKIP("Wayland: This fails. Figure out why.");
4256 QWidget parent;
4257 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4258 parent.resize(w: 400, h: 400);
4259
4260 StaticWidget staticWidget(&parent);
4261 staticWidget.gotPaintEvent = false;
4262 staticWidget.move(ax: 150, ay: 150);
4263 staticWidget.resize(w: 150, h: 150);
4264 parent.show();
4265 QVERIFY(QTest::qWaitForWindowExposed(&parent));
4266 QTRY_VERIFY(staticWidget.gotPaintEvent);
4267
4268 staticWidget.gotPaintEvent = false;
4269 staticWidget.move(staticWidget.pos() + QPoint(10, 10));
4270 QTest::qWait(ms: 20);
4271 if (m_platform == QStringLiteral("winrt"))
4272 QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
4273 QCOMPARE(staticWidget.gotPaintEvent, false);
4274
4275 staticWidget.gotPaintEvent = false;
4276 staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
4277 QTest::qWait(ms: 20);
4278 QCOMPARE(staticWidget.gotPaintEvent, false);
4279
4280 staticWidget.gotPaintEvent = false;
4281 staticWidget.move(staticWidget.pos() + QPoint(-10, 10));
4282 QTest::qWait(ms: 20);
4283 QCOMPARE(staticWidget.gotPaintEvent, false);
4284
4285 staticWidget.gotPaintEvent = false;
4286 staticWidget.resize(staticWidget.size() + QSize(10, 10));
4287 QTRY_VERIFY(staticWidget.gotPaintEvent);
4288 QCOMPARE(staticWidget.partial, true);
4289
4290 staticWidget.gotPaintEvent = false;
4291 staticWidget.resize(staticWidget.size() + QSize(-10, -10));
4292 QTest::qWait(ms: 20);
4293 QCOMPARE(staticWidget.gotPaintEvent, false);
4294
4295 staticWidget.gotPaintEvent = false;
4296 staticWidget.resize(staticWidget.size() + QSize(10, -10));
4297 QTRY_VERIFY(staticWidget.gotPaintEvent);
4298 QCOMPARE(staticWidget.partial, true);
4299
4300 staticWidget.gotPaintEvent = false;
4301 staticWidget.move(staticWidget.pos() + QPoint(10, 10));
4302 staticWidget.resize(staticWidget.size() + QSize(-10, -10));
4303 QTest::qWait(ms: 20);
4304 QCOMPARE(staticWidget.gotPaintEvent, false);
4305
4306 staticWidget.gotPaintEvent = false;
4307 staticWidget.move(staticWidget.pos() + QPoint(10, 10));
4308 staticWidget.resize(staticWidget.size() + QSize(10, 10));
4309 QTRY_VERIFY(staticWidget.gotPaintEvent);
4310 QCOMPARE(staticWidget.partial, true);
4311
4312 staticWidget.gotPaintEvent = false;
4313 staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
4314 staticWidget.resize(staticWidget.size() + QSize(-10, -10));
4315 QTest::qWait(ms: 20);
4316 QCOMPARE(staticWidget.gotPaintEvent, false);
4317
4318 staticWidget.setAttribute(Qt::WA_StaticContents, on: false);
4319 staticWidget.gotPaintEvent = false;
4320 staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
4321 staticWidget.resize(staticWidget.size() + QSize(-10, -10));
4322 QTRY_VERIFY(staticWidget.gotPaintEvent);
4323 QCOMPARE(staticWidget.partial, false);
4324 staticWidget.setAttribute(Qt::WA_StaticContents, on: true);
4325
4326 staticWidget.setAttribute(Qt::WA_StaticContents, on: false);
4327 staticWidget.gotPaintEvent = false;
4328 staticWidget.move(staticWidget.pos() + QPoint(10, 10));
4329 QTest::qWait(ms: 20);
4330 QCOMPARE(staticWidget.gotPaintEvent, false);
4331 staticWidget.setAttribute(Qt::WA_StaticContents, on: true);
4332}
4333
4334void tst_QWidget::optimizedResize_topLevel()
4335{
4336 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
4337 QSKIP("Wayland: This fails. Figure out why.");
4338
4339 if (QHighDpiScaling::isActive())
4340 QSKIP("Skip due to rounding errors in the regions.");
4341 StaticWidget topLevel;
4342 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4343 topLevel.gotPaintEvent = false;
4344 topLevel.show();
4345 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
4346 QTRY_VERIFY(topLevel.gotPaintEvent);
4347
4348 topLevel.gotPaintEvent = false;
4349 topLevel.partial = false;
4350 topLevel.paintedRegion = QRegion();
4351
4352#if !defined(Q_OS_WIN32)
4353 topLevel.resize(topLevel.size() + QSize(10, 10));
4354#else
4355 // Static contents does not work when programmatically resizing
4356 // top-levels with QWidget::resize. We do some funky stuff in
4357 // setGeometry_sys. However, resizing it with the mouse or with
4358 // a native function call works (it basically has to go through
4359 // WM_RESIZE in QApplication). This is a corner case, though.
4360 // See task 243708
4361 RECT rect;
4362 GetWindowRect(winHandleOf(&topLevel), &rect);
4363 MoveWindow(winHandleOf(&topLevel), rect.left, rect.top,
4364 rect.right - rect.left + 10, rect.bottom - rect.top + 10,
4365 true);
4366 QTest::qWait(100);
4367#endif
4368
4369 // Expected update region: New rect - old rect.
4370 QRegion expectedUpdateRegion(topLevel.rect());
4371 expectedUpdateRegion -= QRect(QPoint(), topLevel.size() - QSize(10, 10));
4372
4373 QTRY_VERIFY(topLevel.gotPaintEvent);
4374 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("offscreen"))
4375 QSKIP("QTBUG-26424");
4376 else if (m_platform == QStringLiteral("winrt"))
4377 QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
4378 QCOMPARE(topLevel.partial, true);
4379 QCOMPARE(topLevel.paintedRegion, expectedUpdateRegion);
4380}
4381
4382class SiblingDeleter : public QWidget
4383{
4384public:
4385 inline SiblingDeleter(QWidget *sibling, QWidget *parent)
4386 : QWidget(parent), sibling(sibling) {}
4387 inline ~SiblingDeleter() { delete sibling; }
4388
4389private:
4390 QPointer<QWidget> sibling;
4391};
4392
4393
4394void tst_QWidget::childDeletesItsSibling()
4395{
4396 auto commonParent = new QWidget(nullptr);
4397 QPointer<QWidget> child(new QWidget(nullptr));
4398 QPointer<QWidget> siblingDeleter = new SiblingDeleter(child, commonParent);
4399 child->setParent(commonParent);
4400 delete commonParent; // don't crash
4401 QVERIFY(!child);
4402 QVERIFY(!siblingDeleter);
4403
4404}
4405
4406void tst_QWidget::setMinimumSize()
4407{
4408 QWidget w;
4409 QSize defaultSize = w.size();
4410
4411 w.setMinimumSize(defaultSize + QSize(100, 100));
4412 QCOMPARE(w.size(), defaultSize + QSize(100, 100));
4413 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4414
4415 w.setMinimumSize(defaultSize + QSize(50, 50));
4416 QCOMPARE(w.size(), defaultSize + QSize(100, 100));
4417 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4418
4419 w.setMinimumSize(defaultSize + QSize(200, 200));
4420 QCOMPARE(w.size(), defaultSize + QSize(200, 200));
4421 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4422
4423 QSize nonDefaultSize = defaultSize + QSize(5,5);
4424 w.setMinimumSize(nonDefaultSize);
4425 w.showNormal();
4426 QVERIFY(QTest::qWaitForWindowExposed(&w));
4427 QVERIFY2(w.height() >= nonDefaultSize.height(),
4428 msgComparisonFailed(w.height(), ">=", nonDefaultSize.height()));
4429 QVERIFY2(w.width() >= nonDefaultSize.width(),
4430 msgComparisonFailed(w.width(), ">=", nonDefaultSize.width()));
4431}
4432
4433void tst_QWidget::setMaximumSize()
4434{
4435 QWidget w;
4436 QSize defaultSize = w.size();
4437
4438 w.setMinimumSize(defaultSize + QSize(100, 100));
4439 QCOMPARE(w.size(), defaultSize + QSize(100, 100));
4440 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4441 w.setMinimumSize(defaultSize);
4442
4443 w.setMaximumSize(defaultSize + QSize(200, 200));
4444 QCOMPARE(w.size(), defaultSize + QSize(100, 100));
4445 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4446
4447 w.setMaximumSize(defaultSize + QSize(50, 50));
4448 QCOMPARE(w.size(), defaultSize + QSize(50, 50));
4449 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4450}
4451
4452void tst_QWidget::setFixedSize()
4453{
4454 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
4455 QSKIP("Wayland: This fails. Figure out why.");
4456
4457 QWidget w;
4458 QSize defaultSize = w.size();
4459
4460 w.setFixedSize(defaultSize + QSize(100, 100));
4461 QCOMPARE(w.size(), defaultSize + QSize(100, 100));
4462 QVERIFY(w.testAttribute(Qt::WA_Resized));
4463
4464 w.setFixedSize(defaultSize + QSize(200, 200));
4465
4466 QCOMPARE(w.minimumSize(), defaultSize + QSize(200,200));
4467 QCOMPARE(w.maximumSize(), defaultSize + QSize(200,200));
4468 QCOMPARE(w.size(), defaultSize + QSize(200, 200));
4469 QVERIFY(w.testAttribute(Qt::WA_Resized));
4470
4471 w.setFixedSize(defaultSize + QSize(50, 50));
4472 QCOMPARE(w.size(), defaultSize + QSize(50, 50));
4473 QVERIFY(w.testAttribute(Qt::WA_Resized));
4474
4475 w.setAttribute(Qt::WA_Resized, on: false);
4476 w.setFixedSize(defaultSize + QSize(50, 50));
4477 QVERIFY(!w.testAttribute(Qt::WA_Resized));
4478
4479 w.setFixedSize(defaultSize + QSize(150, 150));
4480 w.showNormal();
4481 QVERIFY(QTest::qWaitForWindowActive(&w));
4482 if (m_platform == QStringLiteral("xcb"))
4483 QSKIP("QTBUG-26424");
4484 else if (m_platform == QStringLiteral("winrt"))
4485 QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
4486 QCOMPARE(w.size(), defaultSize + QSize(150,150));
4487}
4488
4489void tst_QWidget::ensureCreated()
4490{
4491 {
4492 QWidget widget;
4493 WId widgetWinId = widget.winId();
4494 Q_UNUSED(widgetWinId);
4495 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
4496 }
4497
4498 {
4499 QWidget window;
4500
4501 QDialog dialog(&window);
4502 dialog.setWindowModality(Qt::NonModal);
4503
4504 WId dialogWinId = dialog.winId();
4505 Q_UNUSED(dialogWinId);
4506 QVERIFY(dialog.testAttribute(Qt::WA_WState_Created));
4507 QVERIFY(window.testAttribute(Qt::WA_WState_Created));
4508 }
4509
4510 {
4511 QWidget window;
4512
4513 QDialog dialog(&window);
4514 dialog.setWindowModality(Qt::WindowModal);
4515
4516 WId dialogWinId = dialog.winId();
4517 Q_UNUSED(dialogWinId);
4518 QVERIFY(dialog.testAttribute(Qt::WA_WState_Created));
4519 QVERIFY(window.testAttribute(Qt::WA_WState_Created));
4520 }
4521
4522 {
4523 QWidget window;
4524
4525 QDialog dialog(&window);
4526 dialog.setWindowModality(Qt::ApplicationModal);
4527
4528 WId dialogWinId = dialog.winId();
4529 Q_UNUSED(dialogWinId);
4530 QVERIFY(dialog.testAttribute(Qt::WA_WState_Created));
4531 QVERIFY(window.testAttribute(Qt::WA_WState_Created));
4532 }
4533}
4534
4535class WinIdChangeWidget : public QWidget
4536{
4537public:
4538 using QWidget::QWidget;
4539protected:
4540 bool event(QEvent *e) override
4541 {
4542 if (e->type() == QEvent::WinIdChange) {
4543 m_winIdList.append(t: internalWinId());
4544 return true;
4545 }
4546 return QWidget::event(event: e);
4547 }
4548public:
4549 QList<WId> m_winIdList;
4550 int winIdChangeEventCount() const { return m_winIdList.count(); }
4551};
4552
4553class CreateDestroyWidget : public WinIdChangeWidget
4554{
4555public:
4556 void create() { QWidget::create(); }
4557 void destroy() { QWidget::destroy(); }
4558};
4559
4560void tst_QWidget::createAndDestroy()
4561{
4562 CreateDestroyWidget widget;
4563
4564 // Create and destroy via QWidget
4565 widget.create();
4566 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
4567 QCOMPARE(widget.winIdChangeEventCount(), 1);
4568 QVERIFY(widget.internalWinId());
4569
4570 widget.destroy();
4571 QVERIFY(!widget.testAttribute(Qt::WA_WState_Created));
4572 QCOMPARE(widget.winIdChangeEventCount(), 2);
4573 QVERIFY(!widget.internalWinId());
4574
4575 // Create via QWidget, destroy via QWindow
4576 widget.create();
4577 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
4578 QCOMPARE(widget.winIdChangeEventCount(), 3);
4579 QVERIFY(widget.internalWinId());
4580
4581 widget.windowHandle()->destroy();
4582 QVERIFY(!widget.testAttribute(Qt::WA_WState_Created));
4583 QCOMPARE(widget.winIdChangeEventCount(), 4);
4584 QVERIFY(!widget.internalWinId());
4585
4586 // Create via QWidget again
4587 widget.create();
4588 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
4589 QCOMPARE(widget.winIdChangeEventCount(), 5);
4590 QVERIFY(widget.internalWinId());
4591
4592 // Destroy via QWindow, create via QWindow
4593 widget.windowHandle()->destroy();
4594 QVERIFY(widget.windowHandle());
4595 QVERIFY(!widget.testAttribute(Qt::WA_WState_Created));
4596 QCOMPARE(widget.winIdChangeEventCount(), 6);
4597 QVERIFY(!widget.internalWinId());
4598
4599 widget.windowHandle()->create();
4600 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
4601 QCOMPARE(widget.winIdChangeEventCount(), 7);
4602 QVERIFY(widget.internalWinId());
4603}
4604
4605void tst_QWidget::winIdChangeEvent()
4606{
4607 {
4608 // Transforming an alien widget into a native widget
4609 WinIdChangeWidget widget;
4610 const WId winIdBefore = widget.internalWinId();
4611 const WId winIdAfter = widget.winId();
4612 QVERIFY(winIdBefore != winIdAfter);
4613 QCOMPARE(widget.winIdChangeEventCount(), 1);
4614 }
4615
4616 {
4617 // Changing parent of a native widget
4618 QWidget parent1, parent2;
4619 WinIdChangeWidget child(&parent1);
4620 const WId winIdBefore = child.winId();
4621 QCOMPARE(child.winIdChangeEventCount(), 1);
4622 child.setParent(&parent2);
4623 const WId winIdAfter = child.internalWinId();
4624 QCOMPARE(winIdBefore, winIdAfter);
4625 QCOMPARE(child.winIdChangeEventCount(), 3);
4626 // winId is set to zero during reparenting
4627 QCOMPARE(WId(0), child.m_winIdList[1]);
4628 }
4629
4630 {
4631 // Changing grandparent of a native widget
4632 QWidget grandparent1, grandparent2;
4633 QWidget parent(&grandparent1);
4634 WinIdChangeWidget child(&parent);
4635 const WId winIdBefore = child.winId();
4636 QCOMPARE(child.winIdChangeEventCount(), 1);
4637 parent.setParent(&grandparent2);
4638 const WId winIdAfter = child.internalWinId();
4639 QCOMPARE(winIdBefore, winIdAfter);
4640 QCOMPARE(child.winIdChangeEventCount(), 1);
4641 }
4642
4643 {
4644 // Changing parent of an alien widget
4645 QWidget parent1, parent2;
4646 WinIdChangeWidget child(&parent1);
4647 const WId winIdBefore = child.internalWinId();
4648 child.setParent(&parent2);
4649 const WId winIdAfter = child.internalWinId();
4650 QCOMPARE(winIdBefore, winIdAfter);
4651 QCOMPARE(child.winIdChangeEventCount(), 0);
4652 }
4653
4654 {
4655 // Making native child widget into a top-level window
4656 QWidget parent;
4657 WinIdChangeWidget child(&parent);
4658 child.winId();
4659 const WId winIdBefore = child.internalWinId();
4660 QCOMPARE(child.winIdChangeEventCount(), 1);
4661 const Qt::WindowFlags flags = child.windowFlags();
4662 child.setWindowFlags(flags | Qt::Window);
4663 const WId winIdAfter = child.internalWinId();
4664 QCOMPARE(winIdBefore, winIdAfter);
4665 QCOMPARE(child.winIdChangeEventCount(), 3);
4666 // winId is set to zero during reparenting
4667 QCOMPARE(WId(0), child.m_winIdList[1]);
4668 }
4669}
4670
4671void tst_QWidget::persistentWinId()
4672{
4673 QScopedPointer<QWidget> parent(new QWidget);
4674 QWidget *w1 = new QWidget;
4675 QWidget *w2 = new QWidget;
4676 QWidget *w3 = new QWidget;
4677 w1->setParent(parent.data());
4678 w2->setParent(w1);
4679 w3->setParent(w2);
4680
4681 WId winId1 = w1->winId();
4682 WId winId2 = w2->winId();
4683 WId winId3 = w3->winId();
4684
4685 // reparenting should preserve the winId of the widget being reparented and of its children
4686 w1->setParent(nullptr);
4687 QCOMPARE(w1->winId(), winId1);
4688 QCOMPARE(w2->winId(), winId2);
4689 QCOMPARE(w3->winId(), winId3);
4690
4691 w1->setParent(parent.data());
4692 QCOMPARE(w1->winId(), winId1);
4693 QCOMPARE(w2->winId(), winId2);
4694 QCOMPARE(w3->winId(), winId3);
4695
4696 w2->setParent(nullptr);
4697 QCOMPARE(w2->winId(), winId2);
4698 QCOMPARE(w3->winId(), winId3);
4699
4700 w2->setParent(parent.data());
4701 QCOMPARE(w2->winId(), winId2);
4702 QCOMPARE(w3->winId(), winId3);
4703
4704 w2->setParent(w1);
4705 QCOMPARE(w2->winId(), winId2);
4706 QCOMPARE(w3->winId(), winId3);
4707
4708 w3->setParent(nullptr);
4709 QCOMPARE(w3->winId(), winId3);
4710
4711 w3->setParent(w1);
4712 QCOMPARE(w3->winId(), winId3);
4713
4714 w3->setParent(w2);
4715 QCOMPARE(w3->winId(), winId3);
4716}
4717
4718void tst_QWidget::transientParent()
4719{
4720 QWidget topLevel;
4721 topLevel.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize));
4722 topLevel.setWindowTitle(__FUNCTION__);
4723 QWidget *child = new QWidget(&topLevel);
4724 QMenu *menu = new QMenu(child); // QTBUG-41898: Use top level as transient parent for native widgets as well.
4725 QToolButton *toolButton = new QToolButton(child);
4726 toolButton->setMenu(menu);
4727 toolButton->winId();
4728 topLevel.show();
4729 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
4730 QCOMPARE(menu->windowHandle()->transientParent(), topLevel.windowHandle());
4731}
4732
4733void tst_QWidget::showNativeChild()
4734{
4735 if (m_platform == QStringLiteral("winrt"))
4736 QSKIP("WinRT does not support setGeometry");
4737 QWidget topLevel;
4738 topLevel.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize));
4739 topLevel.setWindowTitle(__FUNCTION__);
4740 QWidget child(&topLevel);
4741 child.winId();
4742 topLevel.show();
4743 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
4744}
4745
4746class ShowHideEventWidget : public QWidget
4747{
4748public:
4749 int numberOfShowEvents = 0, numberOfHideEvents = 0;
4750 int numberOfSpontaneousShowEvents = 0, numberOfSpontaneousHideEvents = 0;
4751
4752 using QWidget::QWidget;
4753 using QWidget::create;
4754
4755 void showEvent(QShowEvent *e) override
4756 {
4757 ++numberOfShowEvents;
4758 if (e->spontaneous())
4759 ++numberOfSpontaneousShowEvents;
4760 }
4761
4762 void hideEvent(QHideEvent *e) override
4763 {
4764 ++numberOfHideEvents;
4765 if (e->spontaneous())
4766 ++numberOfSpontaneousHideEvents;
4767 }
4768};
4769
4770void tst_QWidget::showHideEvent_data()
4771{
4772 QTest::addColumn<bool>(name: "show");
4773 QTest::addColumn<bool>(name: "hide");
4774 QTest::addColumn<bool>(name: "create");
4775 QTest::addColumn<int>(name: "expectedShowEvents");
4776 QTest::addColumn<int>(name: "expectedHideEvents");
4777
4778 QTest::newRow(dataTag: "window: only show")
4779 << true
4780 << false
4781 << false
4782 << 1
4783 << 0;
4784 QTest::newRow(dataTag: "window: show/hide")
4785 << true
4786 << true
4787 << false
4788 << 1
4789 << 1;
4790 QTest::newRow(dataTag: "window: show/hide/create")
4791 << true
4792 << true
4793 << true
4794 << 1
4795 << 1;
4796 QTest::newRow(dataTag: "window: hide/create")
4797 << false
4798 << true
4799 << true
4800 << 0
4801 << 0;
4802 QTest::newRow(dataTag: "window: only hide")
4803 << false
4804 << true
4805 << false
4806 << 0
4807 << 0;
4808 QTest::newRow(dataTag: "window: nothing")
4809 << false
4810 << false
4811 << false
4812 << 0
4813 << 0;
4814}
4815
4816void tst_QWidget::showHideEvent()
4817{
4818 QFETCH(bool, show);
4819 QFETCH(bool, hide);
4820 QFETCH(bool, create);
4821 QFETCH(int, expectedShowEvents);
4822 QFETCH(int, expectedHideEvents);
4823
4824 ShowHideEventWidget widget;
4825 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4826 if (show)
4827 widget.show();
4828 if (hide)
4829 widget.hide();
4830 if (create && !widget.testAttribute(attribute: Qt::WA_WState_Created))
4831 widget.create();
4832
4833 QCOMPARE(widget.numberOfShowEvents, expectedShowEvents);
4834 QCOMPARE(widget.numberOfHideEvents, expectedHideEvents);
4835}
4836
4837void tst_QWidget::showHideEventWhileMinimize()
4838{
4839 const QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
4840 if (!pi->hasCapability(cap: QPlatformIntegration::MultipleWindows)
4841 || !pi->hasCapability(cap: QPlatformIntegration::NonFullScreenWindows)
4842 || !pi->hasCapability(cap: QPlatformIntegration::WindowManagement)) {
4843 QSKIP("This test requires window management capabilities");
4844 }
4845 // QTBUG-41312, hide, show events should be received during minimized.
4846 ShowHideEventWidget widget;
4847 widget.setWindowTitle(__FUNCTION__);
4848 widget.resize(m_testWidgetSize);
4849 centerOnScreen(w: &widget);
4850 widget.show();
4851 QVERIFY(QTest::qWaitForWindowExposed(&widget));
4852 const int showEventsBeforeMinimize = widget.numberOfShowEvents;
4853 const int hideEventsBeforeMinimize = widget.numberOfHideEvents;
4854 widget.showMinimized();
4855 QTRY_COMPARE(widget.numberOfHideEvents, hideEventsBeforeMinimize + 1);
4856 widget.showNormal();
4857 QTRY_COMPARE(widget.numberOfShowEvents, showEventsBeforeMinimize + 1);
4858}
4859
4860void tst_QWidget::showHideChildrenWhileMinimize_QTBUG50589()
4861{
4862 const QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
4863 if (!pi->hasCapability(cap: QPlatformIntegration::MultipleWindows)
4864 || !pi->hasCapability(cap: QPlatformIntegration::NonFullScreenWindows)
4865 || !pi->hasCapability(cap: QPlatformIntegration::WindowManagement)) {
4866 QSKIP("This test requires window management capabilities");
4867 }
4868
4869 QWidget parent;
4870 ShowHideEventWidget child(&parent);
4871
4872 parent.setWindowTitle(QTest::currentTestFunction());
4873 parent.resize(m_testWidgetSize);
4874 centerOnScreen(w: &parent);
4875 parent.show();
4876 QVERIFY(QTest::qWaitForWindowExposed(&parent));
4877
4878 const int showEventsBeforeMinimize = child.numberOfSpontaneousShowEvents;
4879 const int hideEventsBeforeMinimize = child.numberOfSpontaneousHideEvents;
4880 parent.showMinimized();
4881 QTRY_COMPARE(child.numberOfSpontaneousHideEvents, hideEventsBeforeMinimize + 1);
4882 parent.showNormal();
4883 QTRY_COMPARE(child.numberOfSpontaneousShowEvents, showEventsBeforeMinimize + 1);
4884}
4885
4886void tst_QWidget::update()
4887{
4888#ifdef Q_OS_MACOS
4889 QSKIP("QTBUG-52974");
4890#endif
4891
4892 Q_CHECK_PAINTEVENTS
4893
4894 UpdateWidget w;
4895 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
4896 w.resize(w: 100, h: 100);
4897 centerOnScreen(w: &w);
4898 w.show();
4899 QVERIFY(QTest::qWaitForWindowExposed(&w));
4900
4901 QTRY_COMPARE(w.numPaintEvents, 1);
4902
4903 QCOMPARE(w.visibleRegion(), QRegion(w.rect()));
4904 QCOMPARE(w.paintedRegion, w.visibleRegion());
4905 w.reset();
4906
4907 UpdateWidget child(&w);
4908 child.setGeometry(ax: 10, ay: 10, aw: 80, ah: 80);
4909 child.show();
4910
4911 QPoint childOffset = child.mapToParent(QPoint());
4912
4913 // widgets are transparent by default, so both should get repaints
4914 {
4915 if (m_platform == QStringLiteral("winrt"))
4916 QEXPECT_FAIL("", "WinRT does not support setGeometry", Abort);
4917 QApplication::processEvents();
4918 QApplication::processEvents();
4919 QCOMPARE(child.numPaintEvents, 1);
4920 QCOMPARE(child.visibleRegion(), QRegion(child.rect()));
4921 QCOMPARE(child.paintedRegion, child.visibleRegion());
4922 QCOMPARE(w.numPaintEvents, 1);
4923 QCOMPARE(w.visibleRegion(), QRegion(w.rect()));
4924 QCOMPARE(w.paintedRegion, child.visibleRegion().translated(childOffset));
4925
4926 w.reset();
4927 child.reset();
4928
4929 w.update();
4930 QApplication::processEvents();
4931 QApplication::processEvents();
4932 QCOMPARE(child.numPaintEvents, 1);
4933 QCOMPARE(child.visibleRegion(), QRegion(child.rect()));
4934 QCOMPARE(child.paintedRegion, child.visibleRegion());
4935 QCOMPARE(w.numPaintEvents, 1);
4936 QCOMPARE(w.visibleRegion(), QRegion(w.rect()));
4937 QCOMPARE(w.paintedRegion, w.visibleRegion());
4938 }
4939
4940 QPalette opaquePalette = child.palette();
4941 opaquePalette.setColor(acr: child.backgroundRole(), acolor: QColor(Qt::red));
4942
4943 // setting an opaque background on the child should prevent paint-events
4944 // for the parent in the child area
4945 {
4946 child.setPalette(opaquePalette);
4947 child.setAutoFillBackground(true);
4948 QApplication::processEvents();
4949
4950 w.reset();
4951 child.reset();
4952
4953 w.update();
4954 QApplication::processEvents();
4955 QApplication::processEvents();
4956
4957 QCOMPARE(w.numPaintEvents, 1);
4958 QRegion expectedVisible = QRegion(w.rect())
4959 - child.visibleRegion().translated(p: childOffset);
4960 QCOMPARE(w.visibleRegion(), expectedVisible);
4961 QCOMPARE(w.paintedRegion, expectedVisible);
4962 QCOMPARE(child.numPaintEvents, 0);
4963
4964 w.reset();
4965 child.reset();
4966
4967 child.update();
4968 QApplication::processEvents();
4969 QApplication::processEvents();
4970
4971 QCOMPARE(w.numPaintEvents, 0);
4972 QCOMPARE(child.numPaintEvents, 1);
4973 QCOMPARE(child.paintedRegion, child.visibleRegion());
4974
4975 w.reset();
4976 child.reset();
4977 }
4978
4979 // overlapping sibling
4980 UpdateWidget sibling(&w);
4981 child.setGeometry(ax: 10, ay: 10, aw: 20, ah: 20);
4982 sibling.setGeometry(ax: 15, ay: 15, aw: 20, ah: 20);
4983 sibling.show();
4984
4985 QApplication::processEvents();
4986 w.reset();
4987 child.reset();
4988 sibling.reset();
4989
4990 const QPoint siblingOffset = sibling.mapToParent(QPoint());
4991
4992 sibling.update();
4993 QApplication::processEvents();
4994 QApplication::processEvents();
4995
4996 // child is opaque, sibling transparent
4997 {
4998 QCOMPARE(sibling.numPaintEvents, 1);
4999 QCOMPARE(sibling.paintedRegion, sibling.visibleRegion());
5000
5001 QCOMPARE(child.numPaintEvents, 1);
5002 QCOMPARE(child.paintedRegion.translated(childOffset),
5003 child.visibleRegion().translated(childOffset)
5004 & sibling.visibleRegion().translated(siblingOffset));
5005
5006 QCOMPARE(w.numPaintEvents, 1);
5007 QCOMPARE(w.paintedRegion,
5008 w.visibleRegion() & sibling.visibleRegion().translated(siblingOffset));
5009 QCOMPARE(w.paintedRegion,
5010 (w.visibleRegion() - child.visibleRegion().translated(childOffset))
5011 & sibling.visibleRegion().translated(siblingOffset));
5012
5013 }
5014 w.reset();
5015 child.reset();
5016 sibling.reset();
5017
5018 sibling.setPalette(opaquePalette);
5019 sibling.setAutoFillBackground(true);
5020
5021 sibling.update();
5022 QApplication::processEvents();
5023 QApplication::processEvents();
5024
5025 // child opaque, sibling opaque
5026 {
5027 QCOMPARE(sibling.numPaintEvents, 1);
5028 QCOMPARE(sibling.paintedRegion, sibling.visibleRegion());
5029
5030#ifdef Q_OS_MACOS
5031 if (child.internalWinId()) // child is native
5032 QEXPECT_FAIL(0, "Cocoa compositor paints child and sibling", Continue);
5033#endif
5034 QCOMPARE(child.numPaintEvents, 0);
5035 QCOMPARE(child.visibleRegion(),
5036 QRegion(child.rect())
5037 - sibling.visibleRegion().translated(siblingOffset - childOffset));
5038
5039 QCOMPARE(w.numPaintEvents, 0);
5040 QCOMPARE(w.visibleRegion(),
5041 QRegion(w.rect())
5042 - child.visibleRegion().translated(childOffset)
5043 - sibling.visibleRegion().translated(siblingOffset));
5044 }
5045}
5046
5047#ifndef Q_OS_MACOS
5048static inline bool isOpaque(QWidget *widget)
5049{
5050 if (!widget)
5051 return false;
5052 return qt_widget_private(widget)->isOpaque;
5053}
5054#endif
5055
5056void tst_QWidget::isOpaque()
5057{
5058#ifndef Q_OS_MACOS
5059 QWidget w;
5060 QVERIFY(::isOpaque(&w));
5061
5062 QWidget child(&w);
5063 QVERIFY(!::isOpaque(&child));
5064
5065 child.setAutoFillBackground(true);
5066 QVERIFY(::isOpaque(&child));
5067
5068 QPalette palette;
5069
5070 // background color
5071
5072 palette = child.palette();
5073 palette.setColor(acr: child.backgroundRole(), acolor: QColor(255, 0, 0, 127));
5074 child.setPalette(palette);
5075 QVERIFY(!::isOpaque(&child));
5076
5077 palette.setColor(acr: child.backgroundRole(), acolor: QColor(255, 0, 0, 255));
5078 child.setPalette(palette);
5079 QVERIFY(::isOpaque(&child));
5080
5081 palette.setColor(acr: QPalette::Window, acolor: QColor(0, 0, 255, 127));
5082 w.setPalette(palette);
5083
5084 QVERIFY(!::isOpaque(&w));
5085
5086 child.setAutoFillBackground(false);
5087 QVERIFY(!::isOpaque(&child));
5088
5089 // Qt::WA_OpaquePaintEvent
5090
5091 child.setAttribute(Qt::WA_OpaquePaintEvent);
5092 QVERIFY(::isOpaque(&child));
5093
5094 child.setAttribute(Qt::WA_OpaquePaintEvent, on: false);
5095 QVERIFY(!::isOpaque(&child));
5096
5097 // Qt::WA_NoSystemBackground
5098
5099 child.setAttribute(Qt::WA_NoSystemBackground);
5100 QVERIFY(!::isOpaque(&child));
5101
5102 child.setAttribute(Qt::WA_NoSystemBackground, on: false);
5103 QVERIFY(!::isOpaque(&child));
5104
5105 palette.setColor(acr: QPalette::Window, acolor: QColor(0, 0, 255, 255));
5106 w.setPalette(palette);
5107 QVERIFY(::isOpaque(&w));
5108
5109 w.setAttribute(Qt::WA_NoSystemBackground);
5110 QVERIFY(!::isOpaque(&w));
5111
5112 w.setAttribute(Qt::WA_NoSystemBackground, on: false);
5113 QVERIFY(::isOpaque(&w));
5114
5115 {
5116 QPalette palette = QApplication::palette();
5117 QPalette old = palette;
5118 palette.setColor(acr: QPalette::Window, acolor: Qt::transparent);
5119 QApplication::setPalette(palette);
5120
5121 QWidget widget;
5122 QVERIFY(!::isOpaque(&widget));
5123
5124 QApplication::setPalette(old);
5125 QCOMPARE(::isOpaque(&widget), old.color(QPalette::Window).alpha() == 255);
5126 }
5127#endif
5128}
5129
5130#ifndef Q_OS_MACOS
5131/*
5132 Test that scrolling of a widget invalidates the correct regions
5133*/
5134void tst_QWidget::scroll()
5135{
5136 if (m_platform == QStringLiteral("wayland"))
5137 QSKIP("Wayland: This fails. Figure out why.");
5138 QScreen *screen = QGuiApplication::primaryScreen();
5139 const int w = qMin(a: 500, b: screen->availableGeometry().width() / 2);
5140 const int h = qMin(a: 500, b: screen->availableGeometry().height() / 2);
5141
5142 UpdateWidget updateWidget;
5143 updateWidget.resize(w, h);
5144 updateWidget.reset();
5145 updateWidget.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250));
5146 updateWidget.showNormal();
5147 QApplication::setActiveWindow(&updateWidget);
5148 QVERIFY(QTest::qWaitForWindowActive(&updateWidget));
5149 QVERIFY(updateWidget.numPaintEvents > 0);
5150
5151 {
5152 updateWidget.reset();
5153 updateWidget.scroll(dx: 10, dy: 10);
5154 QCoreApplication::processEvents();
5155 QRegion dirty(QRect(0, 0, w, 10));
5156 dirty += QRegion(QRect(0, 10, 10, h - 10));
5157 if (m_platform == QStringLiteral("winrt"))
5158 QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
5159 QTRY_COMPARE(updateWidget.paintedRegion, dirty);
5160 }
5161
5162 {
5163 updateWidget.reset();
5164 updateWidget.update(ax: 0, ay: 0, aw: 10, ah: 10);
5165 updateWidget.scroll(dx: 0, dy: 10);
5166 QCoreApplication::processEvents();
5167 QRegion dirty(QRect(0, 0, w, 10));
5168 dirty += QRegion(QRect(0, 10, 10, 10));
5169 QTRY_COMPARE(updateWidget.paintedRegion, dirty);
5170 }
5171
5172 if (updateWidget.width() < 200 || updateWidget.height() < 200)
5173 QSKIP("Skip this test due to too small screen geometry.");
5174
5175 {
5176 updateWidget.reset();
5177 updateWidget.update(ax: 0, ay: 0, aw: 100, ah: 100);
5178 updateWidget.scroll(dx: 10, dy: 10, QRect(50, 50, 100, 100));
5179 QCoreApplication::processEvents();
5180 QRegion dirty(QRect(0, 0, 100, 50));
5181 dirty += QRegion(QRect(0, 50, 150, 10));
5182 dirty += QRegion(QRect(0, 60, 110, 40));
5183 dirty += QRegion(QRect(50, 100, 60, 10));
5184 dirty += QRegion(QRect(50, 110, 10, 40));
5185 QTRY_COMPARE(updateWidget.paintedRegion, dirty);
5186 }
5187
5188 {
5189 updateWidget.reset();
5190 updateWidget.update(ax: 0, ay: 0, aw: 100, ah: 100);
5191 updateWidget.scroll(dx: 10, dy: 10, QRect(100, 100, 100, 100));
5192 QCoreApplication::processEvents();
5193 QRegion dirty(QRect(0, 0, 100, 100));
5194 dirty += QRegion(QRect(100, 100, 100, 10));
5195 dirty += QRegion(QRect(100, 110, 10, 90));
5196 QTRY_COMPARE(updateWidget.paintedRegion, dirty);
5197 }
5198}
5199
5200// QTBUG-38999, scrolling a widget with native child widgets should move the children.
5201void tst_QWidget::scrollNativeChildren()
5202{
5203 QWidget parent;
5204 parent.setWindowTitle(QLatin1String(__FUNCTION__));
5205 parent.resize(w: 400, h: 400);
5206 centerOnScreen(w: &parent);
5207 QLabel *nativeLabel = new QLabel(QStringLiteral("nativeLabel"), &parent);
5208 const QPoint oldLabelPos(100, 100);
5209 nativeLabel->move(oldLabelPos);
5210 QVERIFY(nativeLabel->winId());
5211 parent.show();
5212 QVERIFY(QTest::qWaitForWindowExposed(&parent));
5213 const QPoint delta(50, 50);
5214 parent.scroll(dx: delta.x(), dy: delta.y());
5215 const QPoint newLabelPos = oldLabelPos + delta;
5216 QWindow *labelWindow = nativeLabel->windowHandle();
5217 QVERIFY(labelWindow);
5218 QTRY_COMPARE(labelWindow->geometry().topLeft(), newLabelPos);
5219 QTRY_COMPARE(nativeLabel->geometry().topLeft(), newLabelPos);
5220}
5221
5222#endif // Mac OS
5223
5224class DestroyedSlotChecker : public QObject
5225{
5226 Q_OBJECT
5227
5228public:
5229 bool wasQWidget = false;
5230
5231public slots:
5232 void destroyedSlot(QObject *object)
5233 {
5234 wasQWidget = (qobject_cast<QWidget *>(o: object) != nullptr || object->isWidgetType());
5235 }
5236};
5237
5238/*
5239 Test that qobject_cast<QWidget*> returns 0 in a slot
5240 connected to QObject::destroyed.
5241*/
5242void tst_QWidget::qobject_castInDestroyedSlot()
5243{
5244 DestroyedSlotChecker checker;
5245
5246 QWidget *widget = new QWidget();
5247
5248 QObject::connect(sender: widget, signal: &QObject::destroyed, receiver: &checker, slot: &DestroyedSlotChecker::destroyedSlot);
5249 delete widget;
5250
5251 QVERIFY(checker.wasQWidget);
5252}
5253
5254// Since X11 WindowManager operations are all async, and we have no way to know if the window
5255// manager has finished playing with the window geometry, this test can't be reliable on X11.
5256
5257using Rects = QVector<QRect>;
5258
5259void tst_QWidget::setWindowGeometry_data()
5260{
5261 QTest::addColumn<Rects>(name: "rects");
5262 QTest::addColumn<int>(name: "windowFlags");
5263
5264 QVector<Rects> rects;
5265 const int width = m_testWidgetSize.width();
5266 const int height = m_testWidgetSize.height();
5267 const QRect availableAdjusted = QGuiApplication::primaryScreen()->availableGeometry().adjusted(xp1: 100, yp1: 100, xp2: -100, yp2: -100);
5268 rects << Rects{QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize),
5269 availableAdjusted,
5270 QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)),
5271 QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)),
5272 QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0))}
5273 << Rects{availableAdjusted,
5274 QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)),
5275 QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)),
5276 QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)),
5277 QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height))}
5278 << Rects{QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)),
5279 QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)),
5280 QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)),
5281 QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)),
5282 availableAdjusted}
5283 << Rects{QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)),
5284 QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)),
5285 QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)),
5286 availableAdjusted,
5287 QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height))}
5288 << Rects{QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)),
5289 QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)),
5290 availableAdjusted,
5291 QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)),
5292 QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0))};
5293
5294 const Qt::WindowFlags windowFlags[] = {Qt::WindowFlags(), Qt::FramelessWindowHint};
5295
5296 const bool skipEmptyRects = (m_platform == QStringLiteral("windows"));
5297 for (Rects l : qAsConst(t&: rects)) {
5298 if (skipEmptyRects) {
5299 l.erase(begin: std::remove_if(first: l.begin(), last: l.end(), pred: [] (const QRect &r) { return r.isEmpty(); }),
5300 end: l.end());
5301 }
5302 const QRect &rect = l.constFirst();
5303 for (int windowFlag : windowFlags) {
5304 QTest::newRow(dataTag: QString("%1,%2 %3x%4, flags %5")
5305 .arg(a: rect.x())
5306 .arg(a: rect.y())
5307 .arg(a: rect.width())
5308 .arg(a: rect.height())
5309 .arg(a: windowFlag, fieldWidth: 0, base: 16).toLatin1())
5310 << l
5311 << windowFlag;
5312 }
5313 }
5314}
5315
5316void tst_QWidget::setWindowGeometry()
5317{
5318 if (m_platform == QStringLiteral("xcb"))
5319 QSKIP("X11: Skip this test due to Window manager positioning issues.");
5320 else if (m_platform == QStringLiteral("winrt"))
5321 QSKIP("WinRT does not support setWindowGeometry");
5322
5323 QFETCH(Rects, rects);
5324 QFETCH(int, windowFlags);
5325 QRect rect = rects.takeFirst();
5326
5327 {
5328 // test setGeometry() without actually showing the window
5329 QWidget widget;
5330 if (windowFlags != 0)
5331 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5332
5333 widget.setGeometry(rect);
5334 QTest::qWait(ms: 100);
5335 QCOMPARE(widget.geometry(), rect);
5336
5337 // setGeometry() without showing
5338 for (const QRect &r : qAsConst(t&: rects)) {
5339 widget.setGeometry(r);
5340 QTest::qWait(ms: 100);
5341 QCOMPARE(widget.geometry(), r);
5342 }
5343 }
5344
5345 {
5346 // setGeometry() first, then show()
5347 QWidget widget;
5348 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5349 if (windowFlags != 0)
5350 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5351
5352 widget.setGeometry(rect);
5353 widget.showNormal();
5354 if (rect.isValid()) {
5355 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5356 } else {
5357 // in case of an invalid rect, wait for the geometry to become
5358 // adjusted to the actual (valid) value.
5359 QApplication::processEvents();
5360 }
5361 QTRY_COMPARE(widget.geometry(), rect);
5362
5363 // setGeometry() while shown
5364 for (const QRect &r : qAsConst(t&: rects)) {
5365 widget.setGeometry(r);
5366 QTest::qWait(ms: 10);
5367 QTRY_COMPARE(widget.geometry(), r);
5368 }
5369 widget.setGeometry(rect);
5370 QTest::qWait(ms: 20);
5371 QTRY_COMPARE(widget.geometry(), rect);
5372
5373 // now hide
5374 widget.hide();
5375 QTest::qWait(ms: 20);
5376 QTRY_COMPARE(widget.geometry(), rect);
5377
5378 // setGeometry() after hide()
5379 for (const QRect &r : qAsConst(t&: rects)) {
5380 widget.setGeometry(r);
5381 QTest::qWait(ms: 10);
5382 QTRY_COMPARE(widget.geometry(), r);
5383 }
5384 widget.setGeometry(rect);
5385 QTest::qWait(ms: 10);
5386 QTRY_COMPARE(widget.geometry(), rect);
5387
5388 // show() again, geometry() should still be the same
5389 widget.show();
5390 if (rect.isValid())
5391 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5392 QTRY_COMPARE(widget.geometry(), rect);
5393
5394 // final hide(), again geometry() should be unchanged
5395 widget.hide();
5396 QTest::qWait(ms: 10);
5397 QTRY_COMPARE(widget.geometry(), rect);
5398 }
5399
5400 {
5401 // show() first, then setGeometry()
5402 QWidget widget;
5403 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5404 if (windowFlags != 0)
5405 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5406
5407 widget.showNormal();
5408 if (rect.isValid())
5409 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5410 widget.setGeometry(rect);
5411 QTest::qWait(ms: 10);
5412 QTRY_COMPARE(widget.geometry(), rect);
5413
5414 // setGeometry() while shown
5415 for (const QRect &r : qAsConst(t&: rects)) {
5416 widget.setGeometry(r);
5417 QTest::qWait(ms: 10);
5418 QTRY_COMPARE(widget.geometry(), r);
5419 }
5420 widget.setGeometry(rect);
5421 QTest::qWait(ms: 10);
5422 QTRY_COMPARE(widget.geometry(), rect);
5423
5424 // now hide
5425 widget.hide();
5426 QTest::qWait(ms: 10);
5427 QTRY_COMPARE(widget.geometry(), rect);
5428
5429 // setGeometry() after hide()
5430 for (const QRect &r : qAsConst(t&: rects)) {
5431 widget.setGeometry(r);
5432 QTest::qWait(ms: 10);
5433 QTRY_COMPARE(widget.geometry(), r);
5434 }
5435 widget.setGeometry(rect);
5436 QTest::qWait(ms: 10);
5437 QTRY_COMPARE(widget.geometry(), rect);
5438
5439 // show() again, geometry() should still be the same
5440 widget.show();
5441 if (rect.isValid())
5442 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5443 QTest::qWait(ms: 10);
5444 QTRY_COMPARE(widget.geometry(), rect);
5445
5446 // final hide(), again geometry() should be unchanged
5447 widget.hide();
5448 QTest::qWait(ms: 10);
5449 QTRY_COMPARE(widget.geometry(), rect);
5450 }
5451}
5452
5453#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
5454void tst_QWidget::setGeometry_win()
5455{
5456 QWidget widget;
5457
5458 setFrameless(&widget);
5459 widget.setGeometry(QRect(m_availableTopLeft + QPoint(0, 600), QSize(100, 100)));
5460 widget.show();
5461 widget.setWindowState(widget.windowState() | Qt::WindowMaximized);
5462 QRect geom = widget.normalGeometry();
5463 widget.close();
5464 widget.setGeometry(geom);
5465 widget.setWindowState(widget.windowState() | Qt::WindowMaximized);
5466 widget.show();
5467 RECT rt;
5468 ::GetWindowRect(winHandleOf(&widget), &rt);
5469 QVERIFY2(rt.left <= m_availableTopLeft.x(),
5470 msgComparisonFailed(int(rt.left), "<=", m_availableTopLeft.x()));
5471 QVERIFY2(rt.top <= m_availableTopLeft.y(),
5472 msgComparisonFailed(int(rt.top), "<=", m_availableTopLeft.y()));
5473}
5474#endif // defined (Q_OS_WIN) && !defined(Q_OS_WINRT)
5475
5476// Since X11 WindowManager operation are all async, and we have no way to know if the window
5477// manager has finished playing with the window geometry, this test can't be reliable on X11.
5478
5479void tst_QWidget::windowMoveResize_data()
5480{
5481 setWindowGeometry_data();
5482}
5483
5484void tst_QWidget::windowMoveResize()
5485{
5486 if (m_platform == QStringLiteral("xcb"))
5487 QSKIP("X11: Skip this test due to Window manager positioning issues.");
5488 if (m_platform == QStringLiteral("wayland"))
5489 QSKIP("Wayland: This fails. Figure out why.");
5490 if (m_platform == QStringLiteral("winrt"))
5491 QSKIP("WinRT does not support move/resize");
5492
5493 QFETCH(Rects, rects);
5494 QFETCH(int, windowFlags);
5495
5496 QRect rect = rects.takeFirst();
5497
5498 {
5499 // test setGeometry() without actually showing the window
5500 QWidget widget;
5501 if (windowFlags != 0)
5502 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5503
5504 widget.move(rect.topLeft());
5505 widget.resize(rect.size());
5506 QTest::qWait(ms: 10);
5507 QTRY_COMPARE(widget.pos(), rect.topLeft());
5508 QTRY_COMPARE(widget.size(), rect.size());
5509
5510 // move() without showing
5511 for (const QRect &r : qAsConst(t&: rects)) {
5512 widget.move(r.topLeft());
5513 widget.resize(r.size());
5514 QApplication::processEvents();
5515 QTRY_COMPARE(widget.pos(), r.topLeft());
5516 QTRY_COMPARE(widget.size(), r.size());
5517 }
5518 }
5519
5520 {
5521 // move() first, then show()
5522 QWidget widget;
5523 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5524 if (windowFlags != 0)
5525 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5526
5527 widget.move(rect.topLeft());
5528 widget.resize(rect.size());
5529 widget.showNormal();
5530
5531 QTest::qWait(ms: 10);
5532 QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), rect.topLeft(), m_fuzz),
5533 qPrintable(HighDpi::msgPointMismatch(widget.pos(), rect.topLeft())));
5534 // Windows: Minimum size of decorated windows.
5535 const bool expectResizeFail = (!windowFlags && (rect.width() < 160 || rect.height() < 40))
5536 && m_platform == QStringLiteral("windows");
5537 if (!expectResizeFail)
5538 QTRY_COMPARE(widget.size(), rect.size());
5539
5540 // move() while shown
5541 for (const QRect &r : qAsConst(t&: rects)) {
5542 // XCB: First resize after show of zero-sized gets wrong win_gravity.
5543 const bool expectMoveFail = !windowFlags
5544 && ((widget.width() == 0 || widget.height() == 0) && r.width() != 0 && r.height() != 0)
5545 && m_platform == QStringLiteral("xcb")
5546 && (rect == QRect(QPoint(130, 100), QSize(0, 200))
5547 || rect == QRect(QPoint(100, 50), QSize(200, 0))
5548 || rect == QRect(QPoint(130, 50), QSize(0, 0)));
5549 widget.move(r.topLeft());
5550 widget.resize(r.size());
5551 QApplication::processEvents();
5552 if (!expectMoveFail) {
5553 QTRY_COMPARE(widget.pos(), r.topLeft());
5554 QTRY_COMPARE(widget.size(), r.size());
5555 }
5556 }
5557 widget.move(rect.topLeft());
5558 widget.resize(rect.size());
5559 QApplication::processEvents();
5560 QTRY_COMPARE(widget.pos(), rect.topLeft());
5561 QTRY_COMPARE(widget.size(), rect.size());
5562
5563 // now hide
5564 widget.hide();
5565 QTest::qWait(ms: 10);
5566 QTRY_COMPARE(widget.pos(), rect.topLeft());
5567 QTRY_COMPARE(widget.size(), rect.size());
5568
5569 // move() after hide()
5570 for (const QRect &r : qAsConst(t&: rects)) {
5571 widget.move(r.topLeft());
5572 widget.resize(r.size());
5573 QApplication::processEvents();
5574#if defined(Q_OS_MACOS)
5575 if (r.width() == 0 && r.height() > 0) {
5576 widget.move(r.topLeft());
5577 widget.resize(r.size());
5578 }
5579#endif
5580 QTRY_COMPARE(widget.pos(), r.topLeft());
5581 QTRY_COMPARE(widget.size(), r.size());
5582 }
5583 widget.move(rect.topLeft());
5584 widget.resize(rect.size());
5585 QTest::qWait(ms: 10);
5586 QTRY_COMPARE(widget.pos(), rect.topLeft());
5587 QTRY_COMPARE(widget.size(), rect.size());
5588
5589 // show() again, pos() should be the same
5590 widget.show();
5591 if (rect.isValid())
5592 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5593 QApplication::processEvents();
5594 QTRY_COMPARE(widget.pos(), rect.topLeft());
5595 QTRY_COMPARE(widget.size(), rect.size());
5596
5597 // final hide(), again pos() should be unchanged
5598 widget.hide();
5599 QApplication::processEvents();
5600 QTRY_COMPARE(widget.pos(), rect.topLeft());
5601 QTRY_COMPARE(widget.size(), rect.size());
5602 }
5603
5604 {
5605 // show() first, then move()
5606 QWidget widget;
5607 if (windowFlags != 0)
5608 widget.setWindowFlags(Qt::WindowFlags(windowFlags));
5609
5610 widget.showNormal();
5611 if (rect.isValid())
5612 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5613 QApplication::processEvents();
5614 widget.move(rect.topLeft());
5615 widget.resize(rect.size());
5616 QApplication::processEvents();
5617 QTRY_COMPARE(widget.pos(), rect.topLeft());
5618 QTRY_COMPARE(widget.size(), rect.size());
5619
5620 // move() while shown
5621 for (const QRect &r : qAsConst(t&: rects)) {
5622 widget.move(r.topLeft());
5623 widget.resize(r.size());
5624 QApplication::processEvents();
5625 QTRY_COMPARE(widget.pos(), r.topLeft());
5626 QTRY_COMPARE(widget.size(), r.size());
5627 }
5628 widget.move(rect.topLeft());
5629 widget.resize(rect.size());
5630 QApplication::processEvents();
5631 QTRY_COMPARE(widget.pos(), rect.topLeft());
5632 QTRY_COMPARE(widget.size(), rect.size());
5633
5634 // now hide
5635 widget.hide();
5636 QApplication::processEvents();
5637 QTRY_COMPARE(widget.pos(), rect.topLeft());
5638 QTRY_COMPARE(widget.size(), rect.size());
5639
5640 // move() after hide()
5641 for (const QRect &r : qAsConst(t&: rects)) {
5642 widget.move(r.topLeft());
5643 widget.resize(r.size());
5644 QApplication::processEvents();
5645#if defined(Q_OS_MACOS)
5646 if (r.width() == 0 && r.height() > 0) {
5647 widget.move(r.topLeft());
5648 widget.resize(r.size());
5649 }
5650#endif
5651 QTRY_COMPARE(widget.pos(), r.topLeft());
5652 QTRY_COMPARE(widget.size(), r.size());
5653 }
5654 widget.move(rect.topLeft());
5655 widget.resize(rect.size());
5656 QApplication::processEvents();
5657 QTRY_COMPARE(widget.pos(), rect.topLeft());
5658 QTRY_COMPARE(widget.size(), rect.size());
5659
5660 // show() again, pos() should be the same
5661 widget.show();
5662 if (rect.isValid())
5663 QVERIFY(QTest::qWaitForWindowExposed(&widget));
5664 QTest::qWait(ms: 10);
5665 QTRY_COMPARE(widget.pos(), rect.topLeft());
5666 QTRY_COMPARE(widget.size(), rect.size());
5667
5668 // final hide(), again pos() should be unchanged
5669 widget.hide();
5670 QTest::qWait(ms: 10);
5671 QTRY_COMPARE(widget.pos(), rect.topLeft());
5672 QTRY_COMPARE(widget.size(), rect.size());
5673 }
5674}
5675
5676class ColorWidget : public QWidget
5677{
5678public:
5679 explicit ColorWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags(),
5680 const QColor &c = QColor(Qt::red))
5681 : QWidget(parent, f), color(c)
5682 {
5683 QPalette opaquePalette = palette();
5684 opaquePalette.setColor(acr: backgroundRole(), acolor: color);
5685 setPalette(opaquePalette);
5686 setAutoFillBackground(true);
5687 }
5688
5689 void paintEvent(QPaintEvent *e) override
5690 {
5691 r += e->region();
5692 }
5693
5694 void reset()
5695 {
5696 r = QRegion();
5697 }
5698
5699 void enterEvent(QEvent *) override { ++enters; }
5700 void leaveEvent(QEvent *) override { ++leaves; }
5701
5702 void resetCounts()
5703 {
5704 enters = 0;
5705 leaves = 0;
5706 }
5707
5708 QColor color;
5709 QRegion r;
5710 int enters = 0;
5711 int leaves = 0;
5712};
5713
5714static inline QByteArray msgRgbMismatch(unsigned actual, unsigned expected)
5715{
5716 return QByteArrayLiteral("Color mismatch, 0x") + QByteArray::number(actual, base: 16) +
5717 QByteArrayLiteral(" != 0x") + QByteArray::number(expected, base: 16);
5718}
5719
5720static QPixmap grabWindow(QWindow *window, int x, int y, int width, int height)
5721{
5722 QScreen *screen = window->screen();
5723 Q_ASSERT(screen);
5724 return screen->grabWindow(window: window->winId(), x, y, w: width, h: height);
5725}
5726
5727#define VERIFY_COLOR(child, region, color) verifyColor(child, region, color, __LINE__)
5728
5729bool verifyColor(QWidget &child, const QRegion &region, const QColor &color, int callerLine)
5730{
5731 QWindow *window = child.window()->windowHandle();
5732 Q_ASSERT(window);
5733 const QPoint offset = child.mapTo(child.window(), QPoint(0,0));
5734 bool grabBackingStore = false;
5735 for (QRect r : region) {
5736 QRect rect = r.translated(p: offset);
5737 for (int t = 0; t < 6; t++) {
5738 const QPixmap pixmap = grabBackingStore
5739 ? child.grab(rectangle: rect)
5740 : grabWindow(window, x: rect.left(), y: rect.top(), width: rect.width(), height: rect.height());
5741 const QSize actualSize = pixmap.size() / pixmap.devicePixelRatioF();
5742 if (!QTest::qCompare(t1: actualSize, t2: rect.size(), actual: "pixmap.size()", expected: "rect.size()", __FILE__, line: callerLine))
5743 return false;
5744 QPixmap expectedPixmap(pixmap); /* ensure equal formats */
5745 expectedPixmap.detach();
5746 expectedPixmap.fill(fillColor: color);
5747 QImage image = pixmap.toImage();
5748 uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0;
5749 uint firstPixel = image.pixel(x: 0,y: 0) | alphaCorrection;
5750 if (t < 5) {
5751 /* Normal run.
5752 If it succeeds: return success
5753 If it fails: do not return, but wait a bit and reiterate (retry)
5754 */
5755 if (firstPixel == QColor(color).rgb() && image == expectedPixmap.toImage())
5756 return true;
5757 if (t == 4) {
5758 grabBackingStore = true;
5759 rect = r;
5760 } else {
5761 QTest::qWait(ms: 200);
5762 }
5763 } else {
5764 // Last run, report failure if it still fails
5765 if (!QTest::qVerify(statement: firstPixel == QColor(color).rgb(),
5766 statementStr: "firstPixel == QColor(color).rgb()",
5767 qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())),
5768 __FILE__, line: callerLine))
5769 return false;
5770 if (!QTest::qVerify(statement: image == expectedPixmap.toImage(),
5771 statementStr: "image == expectedPixmap.toImage()",
5772 description: "grabbed pixmap differs from expected pixmap",
5773 __FILE__, line: callerLine))
5774 return false;
5775 }
5776 }
5777 }
5778 return true;
5779}
5780
5781void tst_QWidget::moveChild_data()
5782{
5783 QTest::addColumn<QPoint>(name: "offset");
5784
5785 QTest::newRow(dataTag: "right") << QPoint(20, 0);
5786 QTest::newRow(dataTag: "down") << QPoint(0, 20);
5787 QTest::newRow(dataTag: "left") << QPoint(-20, 0);
5788 QTest::newRow(dataTag: "up") << QPoint(0, -20);
5789}
5790
5791void tst_QWidget::moveChild()
5792{
5793 if (m_platform == QStringLiteral("wayland"))
5794 QSKIP("Wayland: This fails. Figure out why.");
5795 QFETCH(QPoint, offset);
5796
5797 ColorWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint);
5798 // prevent custom styles
5799 const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows")));
5800 parent.setStyle(style.data());
5801 ColorWidget child(&parent, Qt::Widget, Qt::blue);
5802
5803 parent.setGeometry(QRect(parent.screen()->availableGeometry().topLeft() + QPoint(50, 50),
5804 QSize(200, 200)));
5805 child.setGeometry(ax: 25, ay: 25, aw: 50, ah: 50);
5806#ifndef QT_NO_CURSOR // Try to make sure the cursor is not in a taskbar area to prevent tooltips or window highlighting
5807 QCursor::setPos(parent.geometry().topRight() + QPoint(50 , 50));
5808#endif
5809 parent.showNormal();
5810 QVERIFY(QTest::qWaitForWindowExposed(&parent));
5811
5812 QTRY_COMPARE(parent.r, QRegion(parent.rect()) - child.geometry());
5813 QTRY_COMPARE(child.r, QRegion(child.rect()));
5814
5815 if (m_platform == QStringLiteral("winrt"))
5816 QSKIP("WinRT does not support setGeometry (and we cannot use QEXPECT_FAIL because of VERIFY_COLOR)");
5817 VERIFY_COLOR(child, child.rect(),
5818 child.color);
5819 VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), parent.color);
5820 parent.reset();
5821 child.reset();
5822
5823 // move
5824
5825 const QRect oldGeometry = child.geometry();
5826
5827 QPoint pos = child.pos() + offset;
5828 child.move(pos);
5829 QTRY_COMPARE(pos, child.pos());
5830
5831 QTRY_COMPARE(parent.r, QRegion(oldGeometry) - child.geometry());
5832#if !defined(Q_OS_MACOS)
5833 // should be scrolled in backingstore
5834 QCOMPARE(child.r, QRegion());
5835#endif
5836 VERIFY_COLOR(child, child.rect(), child.color);
5837 VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), parent.color);
5838}
5839
5840void tst_QWidget::showAndMoveChild()
5841{
5842 if (m_platform == QStringLiteral("wayland"))
5843 QSKIP("Wayland: This fails. Figure out why.");
5844 QWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint);
5845 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5846 // prevent custom styles
5847 const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows")));
5848 parent.setStyle(style.data());
5849
5850 QRect desktopDimensions = parent.screen()->availableGeometry();
5851 desktopDimensions = desktopDimensions.adjusted(xp1: 64, yp1: 64, xp2: -64, yp2: -64);
5852
5853#ifndef QT_NO_CURSOR // Try to make sure the cursor is not in a taskbar area to prevent tooltips or window highlighting
5854 QCursor::setPos(desktopDimensions.topRight() + QPoint(40, 40));
5855#endif
5856 parent.setGeometry(desktopDimensions);
5857 parent.setPalette(Qt::red);
5858 parent.show();
5859 QApplication::setActiveWindow(&parent);
5860 QVERIFY(QTest::qWaitForWindowActive(&parent));
5861
5862 QWidget child(&parent);
5863 child.resize(w: desktopDimensions.width()/2, h: desktopDimensions.height()/2);
5864 child.setPalette(Qt::blue);
5865 child.setAutoFillBackground(true);
5866
5867 // Ensure that the child is repainted correctly when moved right after show.
5868 // NB! Do NOT processEvents() (or qWait()) in between show() and move().
5869 child.show();
5870 child.move(ax: desktopDimensions.width()/2, ay: desktopDimensions.height()/2);
5871 QCoreApplication::processEvents();
5872
5873 if (m_platform == QStringLiteral("winrt"))
5874 QSKIP("WinRT does not support setGeometry (and we cannot use QEXPECT_FAIL because of VERIFY_COLOR)");
5875 VERIFY_COLOR(child, child.rect(), Qt::blue);
5876 VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), Qt::red);
5877}
5878
5879
5880void tst_QWidget::subtractOpaqueSiblings()
5881{
5882#ifdef Q_OS_MACOS
5883 QSKIP("QTBUG-52974: Cocoa only has rect granularity.");
5884#endif
5885
5886 QWidget w;
5887 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5888 w.setGeometry(ax: 50, ay: 50, aw: 300, ah: 300);
5889
5890 ColorWidget *large = new ColorWidget(&w, Qt::Widget, Qt::red);
5891 large->setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
5892
5893 ColorWidget *medium = new ColorWidget(large, Qt::Widget, Qt::gray);
5894 medium->setGeometry(ax: 50, ay: 50, aw: 100, ah: 100);
5895
5896 ColorWidget *tall = new ColorWidget(&w, Qt::Widget, Qt::blue);
5897 tall->setGeometry(ax: 100, ay: 30, aw: 50, ah: 100);
5898
5899 w.show();
5900 QVERIFY(QTest::qWaitForWindowExposed(&w));
5901
5902 large->reset();
5903 medium->reset();
5904 tall->reset();
5905
5906 medium->update();
5907
5908 // QWidgetPrivate::subtractOpaqueSiblings() should prevent parts of medium
5909 // to be repainted and tall from be repainted at all.
5910
5911 QTRY_COMPARE(large->r, QRegion());
5912 QTRY_COMPARE(tall->r, QRegion());
5913 QTRY_COMPARE(medium->r.translated(medium->mapTo(&w, QPoint())),
5914 QRegion(medium->geometry().translated(large->pos()))
5915 - tall->geometry());
5916}
5917
5918void tst_QWidget::deleteStyle()
5919{
5920 QWidget widget;
5921 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
5922 widget.setStyle(QStyleFactory::create(QLatin1String("Windows")));
5923 widget.show();
5924 delete widget.style();
5925 QCoreApplication::processEvents();
5926}
5927
5928class TopLevelFocusCheck: public QWidget
5929{
5930 Q_OBJECT
5931public:
5932 QLineEdit* edit;
5933 explicit TopLevelFocusCheck(QWidget *parent = nullptr)
5934 : QWidget(parent), edit(new QLineEdit(this))
5935 {
5936 edit->hide();
5937 edit->installEventFilter(filterObj: this);
5938 }
5939
5940public slots:
5941 void mouseDoubleClickEvent ( QMouseEvent * /*event*/ ) override
5942 {
5943 edit->show();
5944 edit->setFocus(Qt::OtherFocusReason);
5945 QCoreApplication::processEvents();
5946 }
5947 bool eventFilter(QObject *obj, QEvent *event) override
5948 {
5949 if (obj == edit && event->type()== QEvent::FocusOut) {
5950 edit->hide();
5951 return true;
5952 }
5953 return false;
5954 }
5955};
5956
5957void tst_QWidget::multipleToplevelFocusCheck()
5958{
5959#ifdef Q_OS_MACOS
5960 QSKIP("QTBUG-52974");
5961#endif
5962
5963 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
5964 QSKIP("Window activation is not supported");
5965 else if (m_platform == QStringLiteral("winrt"))
5966 QSKIP("Winrt: Sometimes crashes in QTextLayout. - QTBUG-68297");
5967 TopLevelFocusCheck w1;
5968 TopLevelFocusCheck w2;
5969
5970 const QString title = QLatin1String(QTest::currentTestFunction());
5971 w1.setWindowTitle(title + QLatin1String("_W1"));
5972 w1.move(m_availableTopLeft + QPoint(20, 20));
5973 w1.resize(w: 200, h: 200);
5974 w1.show();
5975 QVERIFY(QTest::qWaitForWindowExposed(&w1));
5976 w2.setWindowTitle(title + QLatin1String("_W2"));
5977 w2.move(w1.frameGeometry().topRight() + QPoint(20, 0));
5978 w2.resize(w: 200,h: 200);
5979 w2.show();
5980 QVERIFY(QTest::qWaitForWindowExposed(&w2));
5981
5982 QApplication::setActiveWindow(&w1);
5983 w1.activateWindow();
5984 QVERIFY(QTest::qWaitForWindowActive(&w1));
5985 QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
5986 QTest::mouseDClick(widget: &w1, button: Qt::LeftButton);
5987 QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit));
5988
5989 w2.activateWindow();
5990 QApplication::setActiveWindow(&w2);
5991 QVERIFY(QTest::qWaitForWindowActive(&w2));
5992 QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
5993 QTest::mouseClick(widget: &w2, button: Qt::LeftButton);
5994 QTRY_COMPARE(QApplication::focusWidget(), nullptr);
5995
5996 QTest::mouseDClick(widget: &w2, button: Qt::LeftButton);
5997 QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w2.edit));
5998
5999 w1.activateWindow();
6000 QApplication::setActiveWindow(&w1);
6001 QVERIFY(QTest::qWaitForWindowActive(&w1));
6002 QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
6003 QTest::mouseDClick(widget: &w1, button: Qt::LeftButton);
6004 QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit));
6005
6006 w2.activateWindow();
6007 QApplication::setActiveWindow(&w2);
6008 QVERIFY(QTest::qWaitForWindowActive(&w2));
6009 QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
6010 QTest::mouseClick(widget: &w2, button: Qt::LeftButton);
6011 QTRY_COMPARE(QApplication::focusWidget(), nullptr);
6012}
6013
6014class FocusWidget: public QWidget
6015{
6016protected:
6017 bool event(QEvent *ev) override
6018 {
6019 if (ev->type() == QEvent::FocusAboutToChange)
6020 widgetDuringFocusAboutToChange = QApplication::focusWidget();
6021 return QWidget::event(event: ev);
6022 }
6023 void focusInEvent(QFocusEvent *) override
6024 {
6025 focusObjectDuringFocusIn = QGuiApplication::focusObject();
6026 detectedBadEventOrdering = focusObjectDuringFocusIn != mostRecentFocusObjectChange;
6027 }
6028 void focusOutEvent(QFocusEvent *) override
6029 {
6030 focusObjectDuringFocusOut = QGuiApplication::focusObject();
6031 widgetDuringFocusOut = QApplication::focusWidget();
6032 detectedBadEventOrdering = focusObjectDuringFocusOut != mostRecentFocusObjectChange;
6033 }
6034
6035 void focusObjectChanged(QObject *focusObject)
6036 {
6037 mostRecentFocusObjectChange = focusObject;
6038 }
6039
6040public:
6041 explicit FocusWidget(QWidget *parent) : QWidget(parent)
6042 {
6043 connect(qGuiApp, signal: &QGuiApplication::focusObjectChanged, receiver: this, slot: &FocusWidget::focusObjectChanged);
6044 }
6045
6046 QWidget *widgetDuringFocusAboutToChange = nullptr;
6047 QWidget *widgetDuringFocusOut = nullptr;
6048
6049 QObject *focusObjectDuringFocusIn = nullptr;
6050 QObject *focusObjectDuringFocusOut = nullptr;
6051
6052 QObject *mostRecentFocusObjectChange = nullptr;
6053 bool detectedBadEventOrdering = false;
6054};
6055
6056void tst_QWidget::setFocus()
6057{
6058 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
6059 QSKIP("Window activation is not supported");
6060
6061 QScopedPointer<QWidget> testWidget(new QWidget);
6062 testWidget->resize(m_testWidgetSize);
6063 testWidget->setWindowTitle(__FUNCTION__);
6064 centerOnScreen(w: testWidget.data());
6065 testWidget->show();
6066 QVERIFY(QTest::qWaitForWindowExposed(testWidget.data()));
6067
6068 const QPoint windowPos = testWidget->geometry().topRight() + QPoint(50, 0);
6069 {
6070 // move focus to another window
6071 testWidget->activateWindow();
6072 QApplication::setActiveWindow(testWidget.data());
6073 if (testWidget->focusWidget())
6074 testWidget->focusWidget()->clearFocus();
6075 else
6076 testWidget->clearFocus();
6077
6078 // window and children never shown, nobody gets focus
6079 QWidget window;
6080 window.setWindowTitle(QStringLiteral("#1 ") + __FUNCTION__);
6081 window.resize(m_testWidgetSize);
6082 window.move(windowPos);
6083
6084 QWidget child1(&window);
6085 child1.setFocusPolicy(Qt::StrongFocus);
6086
6087 QWidget child2(&window);
6088 child2.setFocusPolicy(Qt::StrongFocus);
6089
6090 child1.setFocus();
6091 QVERIFY(!child1.hasFocus());
6092 QCOMPARE(window.focusWidget(), &child1);
6093 QCOMPARE(QApplication::focusWidget(), nullptr);
6094
6095 child2.setFocus();
6096 QVERIFY(!child2.hasFocus());
6097 QCOMPARE(window.focusWidget(), &child2);
6098 QCOMPARE(QApplication::focusWidget(), nullptr);
6099 }
6100
6101 {
6102 // window and children show, but window not active, nobody gets focus
6103 QWidget window;
6104 window.setWindowTitle(QStringLiteral("#2 ") + __FUNCTION__);
6105 window.resize(m_testWidgetSize);
6106 window.move(windowPos);
6107
6108 QWidget child1(&window);
6109 child1.setFocusPolicy(Qt::StrongFocus);
6110
6111 QWidget child2(&window);
6112 child2.setFocusPolicy(Qt::StrongFocus);
6113
6114 window.show();
6115
6116 // note: window may be active, but we don't want it to be
6117 testWidget->activateWindow();
6118 QApplication::setActiveWindow(testWidget.data());
6119 if (testWidget->focusWidget())
6120 testWidget->focusWidget()->clearFocus();
6121 else
6122 testWidget->clearFocus();
6123
6124 child1.setFocus();
6125 if (m_platform == QStringLiteral("winrt"))
6126 QEXPECT_FAIL("", "WinRT fails here - QTBUG-68297", Abort);
6127 QVERIFY(!child1.hasFocus());
6128 QCOMPARE(window.focusWidget(), &child1);
6129 QCOMPARE(QApplication::focusWidget(), nullptr);
6130
6131 child2.setFocus();
6132 QVERIFY(!child2.hasFocus());
6133 QCOMPARE(window.focusWidget(), &child2);
6134 QCOMPARE(QApplication::focusWidget(), nullptr);
6135 }
6136
6137 {
6138 // window and children show, but window *is* active, children get focus
6139 QWidget window;
6140 window.setWindowTitle(QStringLiteral("#3 ") + __FUNCTION__);
6141 window.resize(m_testWidgetSize);
6142 window.move(windowPos);
6143
6144 FocusWidget child1(&window);
6145 child1.setFocusPolicy(Qt::StrongFocus);
6146
6147 QWidget child2(&window);
6148 child2.setFocusPolicy(Qt::StrongFocus);
6149
6150 window.show();
6151 window.activateWindow();
6152 QVERIFY(QTest::qWaitForWindowExposed(&window));
6153 QTRY_VERIFY(QGuiApplication::focusWindow());
6154
6155 child1.setFocus();
6156 QTRY_VERIFY(child1.hasFocus());
6157 QCOMPARE(window.focusWidget(), &child1);
6158 QCOMPARE(QApplication::focusWidget(), &child1);
6159
6160 child2.setFocus();
6161 QVERIFY(child2.hasFocus());
6162 QCOMPARE(window.focusWidget(), &child2);
6163 QCOMPARE(QApplication::focusWidget(), &child2);
6164
6165 // focus changed in between the events
6166 QCOMPARE(child1.widgetDuringFocusAboutToChange, &child1);
6167 QCOMPARE(child1.widgetDuringFocusOut, &child2);
6168 }
6169
6170 {
6171 // window shown and active, children created, don't get focus, but get focus when shown
6172 QWidget window;
6173 window.setWindowTitle(QStringLiteral("#4 ") + __FUNCTION__);
6174 window.resize(m_testWidgetSize);
6175 window.move(windowPos);
6176
6177 window.show();
6178 window.activateWindow();
6179 QVERIFY(QTest::qWaitForWindowExposed(&window));
6180 QTRY_VERIFY(QGuiApplication::focusWindow());
6181
6182 QWidget child1(&window);
6183 child1.setFocusPolicy(Qt::StrongFocus);
6184
6185 QWidget child2(&window);
6186 child2.setFocusPolicy(Qt::StrongFocus);
6187
6188 child1.setFocus();
6189 QVERIFY(!child1.hasFocus());
6190 QCOMPARE(window.focusWidget(), nullptr);
6191 QCOMPARE(QApplication::focusWidget(), nullptr);
6192
6193 child1.show();
6194 QApplication::processEvents();
6195 QTRY_VERIFY(child1.hasFocus());
6196 QCOMPARE(window.focusWidget(), &child1);
6197 QCOMPARE(QApplication::focusWidget(), &child1);
6198
6199 child2.setFocus();
6200 QVERIFY(!child2.hasFocus());
6201 QCOMPARE(window.focusWidget(), &child1);
6202 QCOMPARE(QApplication::focusWidget(), &child1);
6203
6204 child2.show();
6205 QVERIFY(child2.hasFocus());
6206 QCOMPARE(window.focusWidget(), &child2);
6207 QCOMPARE(QApplication::focusWidget(), &child2);
6208 }
6209
6210 {
6211 // window shown and active, children created, don't get focus,
6212 // even after setFocus(), hide(), then show()
6213 QWidget window;
6214 window.setWindowTitle(QStringLiteral("#5 ") + __FUNCTION__);
6215 window.resize(m_testWidgetSize);
6216 window.move(windowPos);
6217
6218 window.show();
6219 window.activateWindow();
6220 QVERIFY(QTest::qWaitForWindowExposed(&window));
6221 QTRY_VERIFY(QGuiApplication::focusWindow());
6222
6223 QWidget child1(&window);
6224 child1.setFocusPolicy(Qt::StrongFocus);
6225
6226 QWidget child2(&window);
6227 child2.setFocusPolicy(Qt::StrongFocus);
6228
6229 child1.setFocus();
6230 QVERIFY(!child1.hasFocus());
6231 QCOMPARE(window.focusWidget(), nullptr);
6232 QCOMPARE(QApplication::focusWidget(), nullptr);
6233
6234 child1.hide();
6235 QVERIFY(!child1.hasFocus());
6236 QCOMPARE(window.focusWidget(), nullptr);
6237 QCOMPARE(QApplication::focusWidget(), nullptr);
6238
6239 child1.show();
6240 QVERIFY(!child1.hasFocus());
6241 QCOMPARE(window.focusWidget(), nullptr);
6242 QCOMPARE(QApplication::focusWidget(), nullptr);
6243
6244 child2.setFocus();
6245 QVERIFY(!child2.hasFocus());
6246 QCOMPARE(window.focusWidget(), nullptr);
6247 QCOMPARE(QApplication::focusWidget(), nullptr);
6248
6249 child2.hide();
6250 QVERIFY(!child2.hasFocus());
6251 QCOMPARE(window.focusWidget(), nullptr);
6252 QCOMPARE(QApplication::focusWidget(), nullptr);
6253
6254 child2.show();
6255 QVERIFY(!child2.hasFocus());
6256 QCOMPARE(window.focusWidget(), nullptr);
6257 QCOMPARE(QApplication::focusWidget(), nullptr);
6258 }
6259
6260 {
6261 QWidget window;
6262 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6263 window.resize(m_testWidgetSize);
6264 window.move(windowPos);
6265
6266 FocusWidget child1(&window);
6267 QWidget child2(&window);
6268
6269 window.show();
6270 window.activateWindow();
6271 QVERIFY(QTest::qWaitForWindowExposed(&window));
6272 QTRY_VERIFY(QApplication::focusWindow());
6273
6274 QCOMPARE(QApplication::focusObject(), &window);
6275
6276 child1.setFocus();
6277 QTRY_VERIFY(child1.hasFocus());
6278 QCOMPARE(window.focusWidget(), &child1);
6279 QCOMPARE(QApplication::focusWidget(), &child1);
6280 QCOMPARE(QApplication::focusObject(), &child1);
6281 QCOMPARE(child1.focusObjectDuringFocusIn, &child1);
6282 QVERIFY2(!child1.detectedBadEventOrdering,
6283 "focusObjectChanged should be delivered before widget focus events on setFocus");
6284
6285 child1.clearFocus();
6286 QTRY_VERIFY(!child1.hasFocus());
6287 QCOMPARE(window.focusWidget(), nullptr);
6288 QCOMPARE(QApplication::focusWidget(), nullptr);
6289 QCOMPARE(QApplication::focusObject(), &window);
6290 QVERIFY(child1.focusObjectDuringFocusOut != &child1);
6291 QVERIFY2(!child1.detectedBadEventOrdering,
6292 "focusObjectChanged should be delivered before widget focus events on clearFocus");
6293 }
6294}
6295
6296template<class T> class EventSpy : public QObject
6297{
6298public:
6299 EventSpy(T *widget, QEvent::Type event)
6300 : m_widget(widget), eventToSpy(event)
6301 {
6302 if (m_widget)
6303 m_widget->installEventFilter(this);
6304 }
6305
6306 T *widget() const { return m_widget; }
6307 int count() const { return m_count; }
6308 void clear() { m_count = 0; }
6309
6310protected:
6311 bool eventFilter(QObject *object, QEvent *event) override
6312 {
6313 if (event->type() == eventToSpy)
6314 ++m_count;
6315 return QObject::eventFilter(watched: object, event);
6316 }
6317
6318private:
6319 T *m_widget;
6320 const QEvent::Type eventToSpy;
6321 int m_count = 0;
6322};
6323
6324#ifndef QT_NO_CURSOR
6325void tst_QWidget::setCursor()
6326{
6327 {
6328 QWidget window;
6329 window.resize(w: 200, h: 200);
6330 QWidget child(&window);
6331
6332 QVERIFY(!window.testAttribute(Qt::WA_SetCursor));
6333 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6334
6335 window.setCursor(window.cursor());
6336 QVERIFY(window.testAttribute(Qt::WA_SetCursor));
6337 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6338 QCOMPARE(child.cursor().shape(), window.cursor().shape());
6339 }
6340
6341 // do it again, but with window show()n
6342 {
6343 QWidget window;
6344 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6345 window.resize(w: 200, h: 200);
6346 QWidget child(&window);
6347 window.show();
6348
6349 QVERIFY(!window.testAttribute(Qt::WA_SetCursor));
6350 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6351
6352 window.setCursor(window.cursor());
6353 QVERIFY(window.testAttribute(Qt::WA_SetCursor));
6354 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6355 QCOMPARE(child.cursor().shape(), window.cursor().shape());
6356 }
6357
6358
6359 {
6360 QWidget window;
6361 window.resize(w: 200, h: 200);
6362 QWidget child(&window);
6363
6364 window.setCursor(Qt::WaitCursor);
6365 QVERIFY(window.testAttribute(Qt::WA_SetCursor));
6366 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6367 QCOMPARE(child.cursor().shape(), window.cursor().shape());
6368 }
6369
6370 // same thing again, just with window show()n
6371 {
6372 QWidget window;
6373 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6374 window.resize(w: 200, h: 200);
6375 QWidget child(&window);
6376
6377 window.show();
6378 QVERIFY(QTest::qWaitForWindowExposed(&window));
6379 window.setCursor(Qt::WaitCursor);
6380 QVERIFY(window.testAttribute(Qt::WA_SetCursor));
6381 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6382 QCOMPARE(child.cursor().shape(), window.cursor().shape());
6383 }
6384
6385 // reparenting child should not cause the WA_SetCursor to become set
6386 {
6387 QWidget window;
6388 window.resize(w: 200, h: 200);
6389 QWidget window2;
6390 window2.resize(w: 200, h: 200);
6391 QWidget child(&window);
6392
6393 window.setCursor(Qt::WaitCursor);
6394
6395 child.setParent(nullptr);
6396 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6397 QCOMPARE(child.cursor().shape(), QCursor().shape());
6398
6399 child.setParent(&window2);
6400 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6401 QCOMPARE(child.cursor().shape(), window2.cursor().shape());
6402
6403 window2.setCursor(Qt::WaitCursor);
6404 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6405 QCOMPARE(child.cursor().shape(), window2.cursor().shape());
6406 }
6407
6408 // again, with windows show()n
6409 {
6410 QWidget window;
6411 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6412 window.resize(w: 200, h: 200);
6413 QWidget window2;
6414 window2.resize(w: 200, h: 200);
6415 QWidget child(&window);
6416
6417 window.setCursor(Qt::WaitCursor);
6418 window.show();
6419
6420 child.setParent(nullptr);
6421 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6422 QCOMPARE(child.cursor().shape(), QCursor().shape());
6423
6424 child.setParent(&window2);
6425 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6426 QCOMPARE(child.cursor().shape(), window2.cursor().shape());
6427
6428 window2.show();
6429 window2.setCursor(Qt::WaitCursor);
6430 QVERIFY(!child.testAttribute(Qt::WA_SetCursor));
6431 QCOMPARE(child.cursor().shape(), window2.cursor().shape());
6432 }
6433
6434 // test if CursorChange is sent
6435 {
6436 QWidget widget;
6437 EventSpy<QWidget> spy(&widget, QEvent::CursorChange);
6438 QCOMPARE(spy.count(), 0);
6439 widget.setCursor(QCursor(Qt::WaitCursor));
6440 QCOMPARE(spy.count(), 1);
6441 widget.unsetCursor();
6442 QCOMPARE(spy.count(), 2);
6443 }
6444}
6445#endif
6446
6447void tst_QWidget::setToolTip()
6448{
6449 QWidget widget;
6450 widget.resize(w: 200, h: 200);
6451 // Showing the widget is not required for the tooltip event count test
6452 // to work. It should just prevent the application from becoming inactive
6453 // which would cause it to close all popups, interfering with the test
6454 // in the loop below.
6455 widget.setObjectName(QLatin1String("tst_qwidget setToolTip"));
6456 widget.setWindowTitle(widget.objectName());
6457 widget.show();
6458 QVERIFY(QTest::qWaitForWindowExposed(&widget));
6459 EventSpy<QWidget> spy(&widget, QEvent::ToolTipChange);
6460 QCOMPARE(spy.count(), 0);
6461
6462 QCOMPARE(widget.toolTip(), QString());
6463 widget.setToolTip(QString("Hello"));
6464 QCOMPARE(widget.toolTip(), QString("Hello"));
6465 QCOMPARE(spy.count(), 1);
6466 widget.setToolTip(QString());
6467 QCOMPARE(widget.toolTip(), QString());
6468 QCOMPARE(spy.count(), 2);
6469
6470 for (int pass = 0; pass < 2; ++pass) {
6471 QCursor::setPos(m_safeCursorPos);
6472 QScopedPointer<QWidget> popup(new QWidget(nullptr, Qt::Popup));
6473 popup->setObjectName(QLatin1String("tst_qwidget setToolTip #") + QString::number(pass));
6474 popup->setWindowTitle(popup->objectName());
6475 popup->setGeometry(ax: 50, ay: 50, aw: 150, ah: 50);
6476 QFrame *frame = new QFrame(popup.data());
6477 frame->setGeometry(ax: 0, ay: 0, aw: 50, ah: 50);
6478 frame->setFrameStyle(QFrame::Box | QFrame::Plain);
6479 EventSpy<QWidget> spy1(frame, QEvent::ToolTip);
6480 EventSpy<QWidget> spy2(popup.data(), QEvent::ToolTip);
6481 frame->setMouseTracking(pass != 0);
6482 frame->setToolTip(QLatin1String("TOOLTIP FRAME"));
6483 popup->setToolTip(QLatin1String("TOOLTIP POPUP"));
6484 popup->show();
6485 QVERIFY(QTest::qWaitForWindowExposed(popup.data()));
6486 QWindow *popupWindow = popup->windowHandle();
6487 QTest::qWait(ms: 10);
6488 QTest::mouseMove(window: popupWindow, pos: QPoint(25, 25));
6489 QTest::qWait(ms: 900); // delay is 700
6490
6491 QCOMPARE(spy1.count(), 1);
6492 QCOMPARE(spy2.count(), 0);
6493 if (pass == 0)
6494 QTest::qWait(ms: 2200); // delay is 2000
6495 QTest::mouseMove(window: popupWindow);
6496 }
6497
6498 QTRY_COMPARE(QApplication::topLevelWidgets().size(), 1);
6499}
6500
6501void tst_QWidget::testWindowIconChangeEventPropagation()
6502{
6503 typedef QSharedPointer<EventSpy<QWidget> > EventSpyPtr;
6504 typedef QSharedPointer<EventSpy<QWindow> > WindowEventSpyPtr;
6505 // Create widget hierarchy.
6506 QWidget topLevelWidget;
6507 topLevelWidget.setWindowTitle(QStringLiteral("TopLevel ") + __FUNCTION__);
6508 topLevelWidget.resize(m_testWidgetSize);
6509 topLevelWidget.move(m_availableTopLeft + QPoint(100, 100));
6510 QWidget topLevelChild(&topLevelWidget);
6511
6512 QDialog dialog(&topLevelWidget);
6513 dialog.resize(m_testWidgetSize);
6514 dialog.move(topLevelWidget.geometry().topRight() + QPoint(100, 0));
6515 dialog.setWindowTitle(QStringLiteral("Dialog ") + __FUNCTION__);
6516 QWidget dialogChild(&dialog);
6517
6518 QWidgetList widgets;
6519 widgets << &topLevelWidget << &topLevelChild
6520 << &dialog << &dialogChild;
6521 QCOMPARE(widgets.count(), 4);
6522
6523 topLevelWidget.show();
6524 dialog.show();
6525
6526 QWindowList windows;
6527 windows << topLevelWidget.windowHandle() << dialog.windowHandle();
6528 QWindow otherWindow;
6529 windows << &otherWindow;
6530 const int lastWidgetWindow = 1; // 0 and 1 are qwidgetwindow, 2 is a pure qwindow
6531
6532 // Create spy lists.
6533 QList <EventSpyPtr> applicationEventSpies;
6534 QList <EventSpyPtr> widgetEventSpies;
6535 for (QWidget *widget : qAsConst(t&: widgets)) {
6536 applicationEventSpies.append(t: EventSpyPtr::create(arguments&: widget, arguments: QEvent::ApplicationWindowIconChange));
6537 widgetEventSpies.append(t: EventSpyPtr::create(arguments&: widget, arguments: QEvent::WindowIconChange));
6538 }
6539 QList <WindowEventSpyPtr> appWindowEventSpies;
6540 QList <WindowEventSpyPtr> windowEventSpies;
6541 for (QWindow *window : qAsConst(t&: windows)) {
6542 appWindowEventSpies.append(t: WindowEventSpyPtr::create(arguments&: window, arguments: QEvent::ApplicationWindowIconChange));
6543 windowEventSpies.append(t: WindowEventSpyPtr::create(arguments&: window, arguments: QEvent::WindowIconChange));
6544 }
6545
6546 // QApplication::setWindowIcon
6547 const QIcon windowIcon = qApp->style()->standardIcon(standardIcon: QStyle::SP_TitleBarMenuButton);
6548 qApp->setWindowIcon(windowIcon);
6549
6550 for (int i = 0; i < widgets.count(); ++i) {
6551 // Check QEvent::ApplicationWindowIconChange
6552 EventSpyPtr spy = applicationEventSpies.at(i);
6553 QWidget *widget = spy->widget();
6554 if (widget->isWindow()) {
6555 QCOMPARE(spy->count(), 1);
6556 QCOMPARE(widget->windowIcon(), windowIcon);
6557 } else {
6558 QCOMPARE(spy->count(), 0);
6559 }
6560 spy->clear();
6561
6562 // Check QEvent::WindowIconChange
6563 spy = widgetEventSpies.at(i);
6564 QCOMPARE(spy->count(), 1);
6565 spy->clear();
6566 }
6567 for (int i = 0; i < windows.count(); ++i) {
6568 // Check QEvent::ApplicationWindowIconChange (sent to QWindow)
6569 // QWidgetWindows don't get this event, since the widget takes care of changing the icon
6570 WindowEventSpyPtr spy = appWindowEventSpies.at(i);
6571 QWindow *window = spy->widget();
6572 QCOMPARE(spy->count(), i > lastWidgetWindow ? 1 : 0);
6573 QCOMPARE(window->icon(), windowIcon);
6574 spy->clear();
6575
6576 // Check QEvent::WindowIconChange (sent to QWindow)
6577 spy = windowEventSpies.at(i);
6578 QCOMPARE(spy->count(), 1);
6579 spy->clear();
6580 }
6581
6582 // Set icon on a top-level widget.
6583 topLevelWidget.setWindowIcon(QIcon());
6584
6585 for (int i = 0; i < widgets.count(); ++i) {
6586 // Check QEvent::ApplicationWindowIconChange
6587 EventSpyPtr spy = applicationEventSpies.at(i);
6588 QCOMPARE(spy->count(), 0);
6589 spy->clear();
6590
6591 // Check QEvent::WindowIconChange
6592 spy = widgetEventSpies.at(i);
6593 QWidget *widget = spy->widget();
6594 if (widget == &topLevelWidget) {
6595 QCOMPARE(widget->windowIcon(), QIcon());
6596 QCOMPARE(spy->count(), 1);
6597 } else if (topLevelWidget.isAncestorOf(child: widget)) {
6598 QCOMPARE(spy->count(), 1);
6599 } else {
6600 QCOMPARE(spy->count(), 0);
6601 }
6602 spy->clear();
6603 }
6604
6605 // Cleanup.
6606 qApp->setWindowIcon(QIcon());
6607}
6608
6609void tst_QWidget::minAndMaxSizeWithX11BypassWindowManagerHint()
6610{
6611 if (m_platform != QStringLiteral("xcb"))
6612 QSKIP("This test is for X11 only.");
6613 // Same size as in QWidgetPrivate::create.
6614 const QSize desktopSize = QApplication::desktop()->size();
6615 const QSize originalSize(desktopSize.width() / 2, desktopSize.height() * 4 / 10);
6616
6617 { // Maximum size.
6618 QWidget widget(nullptr, Qt::X11BypassWindowManagerHint);
6619 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6620
6621 const QSize newMaximumSize = widget.size().boundedTo(otherSize: originalSize) - QSize(10, 10);
6622 widget.setMaximumSize(newMaximumSize);
6623 QCOMPARE(widget.size(), newMaximumSize);
6624
6625 widget.show();
6626 QVERIFY(QTest::qWaitForWindowExposed(&widget));
6627 QCOMPARE(widget.size(), newMaximumSize);
6628 }
6629
6630 { // Minimum size.
6631 QWidget widget(nullptr, Qt::X11BypassWindowManagerHint);
6632 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6633
6634 const QSize newMinimumSize = widget.size().expandedTo(otherSize: originalSize) + QSize(10, 10);
6635 widget.setMinimumSize(newMinimumSize);
6636 QCOMPARE(widget.size(), newMinimumSize);
6637
6638 widget.show();
6639 QVERIFY(QTest::qWaitForWindowExposed(&widget));
6640 QCOMPARE(widget.size(), newMinimumSize);
6641 }
6642}
6643
6644class ShowHideShowWidget : public QWidget, public QAbstractNativeEventFilter
6645{
6646 Q_OBJECT
6647
6648 int state = 0;
6649public:
6650 bool gotExpectedMapNotify = false;
6651 bool gotExpectedGlobalEvent = false;
6652
6653 ShowHideShowWidget()
6654 {
6655 startTimer(interval: 1000);
6656 }
6657
6658 void timerEvent(QTimerEvent *) override
6659 {
6660 switch (state++) {
6661 case 0:
6662 show();
6663 break;
6664 case 1:
6665 emit done();
6666 break;
6667 }
6668 }
6669
6670 bool isMapNotify(const QByteArray &eventType, void *message)
6671 {
6672 enum { XCB_MAP_NOTIFY = 19 };
6673 if (state == 1 && eventType == QByteArrayLiteral("xcb_generic_event_t")) {
6674 // XCB events have a uint8 response_type member at the beginning.
6675 const auto responseType = *reinterpret_cast<const unsigned char *>(message);
6676 return ((responseType & ~0x80) == XCB_MAP_NOTIFY);
6677 }
6678 return false;
6679 }
6680
6681#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
6682 bool nativeEvent(const QByteArray &eventType, void *message, qintptr *) override
6683#else
6684 bool nativeEvent(const QByteArray &eventType, void *message, long *) override
6685#endif
6686 {
6687 if (isMapNotify(eventType, message))
6688 gotExpectedMapNotify = true;
6689 return false;
6690 }
6691
6692 // QAbstractNativeEventFilter interface
6693#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
6694 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
6695#else
6696 bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override
6697#endif
6698 {
6699 if (isMapNotify(eventType, message))
6700 gotExpectedGlobalEvent = true;
6701 return false;
6702 }
6703
6704signals:
6705 void done();
6706};
6707
6708void tst_QWidget::showHideShowX11()
6709{
6710 if (m_platform != QStringLiteral("xcb"))
6711 QSKIP("This test is for X11 only.");
6712
6713 ShowHideShowWidget w;
6714 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6715 qApp->installNativeEventFilter(filterObj: &w);
6716
6717 w.show();
6718 QVERIFY(QTest::qWaitForWindowExposed(&w));
6719 w.hide();
6720
6721 QEventLoop eventLoop;
6722 connect(sender: &w, signal: &ShowHideShowWidget::done, receiver: &eventLoop, slot: &QEventLoop::quit);
6723 eventLoop.exec();
6724
6725 QVERIFY(w.gotExpectedGlobalEvent);
6726 QVERIFY(w.gotExpectedMapNotify);
6727}
6728
6729void tst_QWidget::clean_qt_x11_enforce_cursor()
6730{
6731 if (m_platform != QStringLiteral("xcb"))
6732 QSKIP("This test is for X11 only.");
6733
6734 {
6735 QWidget window;
6736 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
6737 QWidget *w = new QWidget(&window);
6738 QWidget *child = new QWidget(w);
6739 child->setAttribute(Qt::WA_SetCursor, on: true);
6740
6741 window.show();
6742 QApplication::setActiveWindow(&window);
6743 QVERIFY(QTest::qWaitForWindowActive(&window));
6744 QTest::qWait(ms: 100);
6745 QCursor::setPos(window.geometry().center());
6746 QTest::qWait(ms: 100);
6747
6748 child->setFocus();
6749 QApplication::processEvents();
6750 QTest::qWait(ms: 100);
6751
6752 delete w;
6753 }
6754
6755 QGraphicsScene scene;
6756 QLineEdit *edit = new QLineEdit;
6757 scene.addWidget(widget: edit);
6758
6759 // If the test didn't crash, then it passed.
6760}
6761
6762class EventRecorder : public QObject
6763{
6764 Q_OBJECT
6765
6766public:
6767 typedef QPair<QWidget *, QEvent::Type> WidgetEventTypePair;
6768 typedef QList<WidgetEventTypePair> EventList;
6769
6770 using QObject::QObject;
6771
6772 EventList eventList() const
6773 {
6774 return events;
6775 }
6776
6777 void clear()
6778 {
6779 events.clear();
6780 }
6781
6782 bool eventFilter(QObject *object, QEvent *event) override
6783 {
6784 QWidget *widget = qobject_cast<QWidget *>(o: object);
6785 if (widget && !event->spontaneous())
6786 events.append(t: qMakePair(x: widget, y: event->type()));
6787 return false;
6788 }
6789
6790 static QByteArray msgEventListMismatch(const EventList &expected, const EventList &actual);
6791 static QByteArray msgExpectFailQtBug26424(const EventList &expected, const EventList &actual)
6792 { return QByteArrayLiteral("QTBUG-26424: ") + msgEventListMismatch(expected, actual); }
6793
6794private:
6795 static inline void formatEventList(const EventList &l, QDebug &d);
6796
6797 EventList events;
6798};
6799
6800void EventRecorder::formatEventList(const EventList &l, QDebug &d)
6801{
6802 QWidget *lastWidget = nullptr;
6803 for (const WidgetEventTypePair &p : l) {
6804 if (p.first != lastWidget) {
6805 d << p.first << ':';
6806 lastWidget = p.first;
6807 }
6808 d << p.second << ' ';
6809 }
6810}
6811
6812QByteArray EventRecorder::msgEventListMismatch(const EventList &expected, const EventList &actual)
6813{
6814 QString result;
6815 QDebug d = QDebug(&result).nospace();
6816 d << "Event list mismatch, expected " << expected.size() << " (";
6817 EventRecorder::formatEventList(l: expected, d);
6818 d << "), actual " << actual.size() << " (";
6819 EventRecorder::formatEventList(l: actual, d);
6820 d << ')';
6821 return result.toLocal8Bit();
6822}
6823
6824void tst_QWidget::childEvents()
6825{
6826 if (m_platform == QStringLiteral("winrt"))
6827 QSKIP("WinRT: This fails. QTBUG-68297.");
6828 EventRecorder::EventList expected;
6829
6830 // Move away the cursor; otherwise it might result in an enter event if it's
6831 // inside the widget when the widget is shown.
6832 QCursor::setPos(m_safeCursorPos);
6833 QTest::qWait(ms: 100);
6834
6835 {
6836 // no children created, not shown
6837 QWidget widget;
6838 widget.resize(w: 200, h: 200);
6839 EventRecorder spy;
6840 widget.installEventFilter(filterObj: &spy);
6841
6842 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
6843
6844 QCoreApplication::sendPostedEvents();
6845
6846 expected =
6847 EventRecorder::EventList()
6848 << qMakePair(x: &widget, y: QEvent::PolishRequest)
6849 << qMakePair(x: &widget, y: QEvent::Polish)
6850 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1));
6851 QVERIFY2(spy.eventList() == expected,
6852 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6853 }
6854
6855 {
6856 // no children, shown
6857 QWidget widget;
6858 widget.resize(w: 200, h: 200);
6859 EventRecorder spy;
6860 widget.installEventFilter(filterObj: &spy);
6861
6862 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
6863
6864 widget.showNormal();
6865 expected =
6866 EventRecorder::EventList()
6867 << qMakePair(x: &widget, y: QEvent::Polish)
6868 << qMakePair(x: &widget, y: QEvent::PlatformSurface)
6869 << qMakePair(x: &widget, y: QEvent::WinIdChange)
6870 << qMakePair(x: &widget, y: QEvent::WindowIconChange)
6871 << qMakePair(x: &widget, y: QEvent::Move)
6872 << qMakePair(x: &widget, y: QEvent::Resize)
6873 << qMakePair(x: &widget, y: QEvent::Show)
6874 << qMakePair(x: &widget, y: QEvent::CursorChange)
6875 << qMakePair(x: &widget, y: QEvent::ShowToParent);
6876
6877 QVERIFY2(spy.eventList() == expected,
6878 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6879 spy.clear();
6880
6881 QCoreApplication::sendPostedEvents();
6882 expected =
6883 EventRecorder::EventList()
6884 << qMakePair(x: &widget, y: QEvent::PolishRequest)
6885 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1))
6886 << qMakePair(x: &widget, y: QEvent::UpdateLater)
6887 << qMakePair(x: &widget, y: QEvent::UpdateRequest);
6888
6889 QVERIFY2(spy.eventList() == expected,
6890 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6891 }
6892
6893 {
6894 // 2 children, not shown
6895 QWidget widget;
6896 widget.resize(w: 200, h: 200);
6897 EventRecorder spy;
6898 widget.installEventFilter(filterObj: &spy);
6899
6900 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
6901
6902 QWidget child1(&widget);
6903 QWidget child2;
6904 child2.setParent(&widget);
6905
6906 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 2)));
6907
6908 expected =
6909 EventRecorder::EventList()
6910 << qMakePair(x: &widget, y: QEvent::ChildAdded)
6911 << qMakePair(x: &widget, y: QEvent::ChildAdded);
6912 QVERIFY2(spy.eventList() == expected,
6913 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6914 spy.clear();
6915
6916 QCoreApplication::sendPostedEvents();
6917 expected =
6918 EventRecorder::EventList()
6919 << qMakePair(x: &widget, y: QEvent::PolishRequest)
6920 << qMakePair(x: &widget, y: QEvent::Polish)
6921 << qMakePair(x: &widget, y: QEvent::ChildPolished)
6922 << qMakePair(x: &widget, y: QEvent::ChildPolished)
6923 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1))
6924 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 2));
6925 QVERIFY2(spy.eventList() == expected,
6926 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6927 }
6928
6929 {
6930 // 2 children, widget shown
6931 QWidget widget;
6932 widget.resize(w: 200, h: 200);
6933 EventRecorder spy;
6934 widget.installEventFilter(filterObj: &spy);
6935
6936 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
6937
6938 QWidget child1(&widget);
6939 QWidget child2;
6940 child2.setParent(&widget);
6941
6942 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 2)));
6943
6944 expected =
6945 EventRecorder::EventList()
6946 << qMakePair(x: &widget, y: QEvent::ChildAdded)
6947 << qMakePair(x: &widget, y: QEvent::ChildAdded);
6948 QCOMPARE(spy.eventList(), expected);
6949 spy.clear();
6950
6951 widget.showNormal();
6952 expected =
6953 EventRecorder::EventList()
6954 << qMakePair(x: &widget, y: QEvent::Polish)
6955 << qMakePair(x: &widget, y: QEvent::ChildPolished)
6956 << qMakePair(x: &widget, y: QEvent::ChildPolished)
6957 << qMakePair(x: &widget, y: QEvent::PlatformSurface)
6958 << qMakePair(x: &widget, y: QEvent::WinIdChange)
6959 << qMakePair(x: &widget, y: QEvent::WindowIconChange)
6960 << qMakePair(x: &widget, y: QEvent::Move)
6961 << qMakePair(x: &widget, y: QEvent::Resize)
6962 << qMakePair(x: &widget, y: QEvent::Show)
6963 << qMakePair(x: &widget, y: QEvent::CursorChange)
6964 << qMakePair(x: &widget, y: QEvent::ShowToParent);
6965
6966 QVERIFY2(spy.eventList() == expected,
6967 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6968 spy.clear();
6969
6970 QCoreApplication::sendPostedEvents();
6971 expected =
6972 EventRecorder::EventList()
6973 << qMakePair(x: &widget, y: QEvent::PolishRequest)
6974 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1))
6975 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 2))
6976 << qMakePair(x: &widget, y: QEvent::UpdateLater)
6977 << qMakePair(x: &widget, y: QEvent::UpdateRequest);
6978
6979 QVERIFY2(spy.eventList() == expected,
6980 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
6981 }
6982
6983 {
6984 // 2 children, but one is reparented away, not shown
6985 QWidget widget;
6986 widget.resize(w: 200, h: 200);
6987 EventRecorder spy;
6988 widget.installEventFilter(filterObj: &spy);
6989
6990 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
6991
6992 QWidget child1(&widget);
6993 QWidget child2;
6994 child2.setParent(&widget);
6995 child2.setParent(nullptr);
6996
6997 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 2)));
6998
6999 expected =
7000 EventRecorder::EventList()
7001 << qMakePair(x: &widget, y: QEvent::ChildAdded)
7002 << qMakePair(x: &widget, y: QEvent::ChildAdded)
7003 << qMakePair(x: &widget, y: QEvent::ChildRemoved);
7004 QCOMPARE(spy.eventList(), expected);
7005 spy.clear();
7006
7007 QCoreApplication::sendPostedEvents();
7008 expected =
7009 EventRecorder::EventList()
7010 << qMakePair(x: &widget, y: QEvent::PolishRequest)
7011 << qMakePair(x: &widget, y: QEvent::Polish)
7012 << qMakePair(x: &widget, y: QEvent::ChildPolished)
7013 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1))
7014 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 2));
7015
7016 QVERIFY2(spy.eventList() == expected,
7017 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
7018 }
7019
7020 {
7021 // 2 children, but one is reparented away, then widget is shown
7022 QWidget widget;
7023 widget.resize(w: 200, h: 200);
7024 EventRecorder spy;
7025 widget.installEventFilter(filterObj: &spy);
7026
7027 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 1)));
7028
7029 QWidget child1(&widget);
7030 QWidget child2;
7031 child2.setParent(&widget);
7032 child2.setParent(nullptr);
7033
7034 QCoreApplication::postEvent(receiver: &widget, event: new QEvent(QEvent::Type(QEvent::User + 2)));
7035
7036 expected =
7037 EventRecorder::EventList()
7038 << qMakePair(x: &widget, y: QEvent::ChildAdded)
7039 << qMakePair(x: &widget, y: QEvent::ChildAdded)
7040 << qMakePair(x: &widget, y: QEvent::ChildRemoved);
7041 QCOMPARE(spy.eventList(), expected);
7042 spy.clear();
7043
7044 widget.showNormal();
7045 expected =
7046 EventRecorder::EventList()
7047 << qMakePair(x: &widget, y: QEvent::Polish)
7048 << qMakePair(x: &widget, y: QEvent::ChildPolished)
7049 << qMakePair(x: &widget, y: QEvent::PlatformSurface)
7050 << qMakePair(x: &widget, y: QEvent::WinIdChange)
7051 << qMakePair(x: &widget, y: QEvent::WindowIconChange)
7052 << qMakePair(x: &widget, y: QEvent::Move)
7053 << qMakePair(x: &widget, y: QEvent::Resize)
7054 << qMakePair(x: &widget, y: QEvent::Show)
7055 << qMakePair(x: &widget, y: QEvent::CursorChange)
7056 << qMakePair(x: &widget, y: QEvent::ShowToParent);
7057
7058 QVERIFY2(spy.eventList() == expected,
7059 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
7060 spy.clear();
7061
7062 QCoreApplication::sendPostedEvents();
7063 expected =
7064 EventRecorder::EventList()
7065 << qMakePair(x: &widget, y: QEvent::PolishRequest)
7066 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 1))
7067 << qMakePair(x: &widget, y: QEvent::Type(QEvent::User + 2))
7068 << qMakePair(x: &widget, y: QEvent::UpdateLater)
7069 << qMakePair(x: &widget, y: QEvent::UpdateRequest);
7070
7071 QVERIFY2(spy.eventList() == expected,
7072 EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData());
7073 }
7074}
7075
7076class RenderWidget : public QWidget
7077{
7078public:
7079 RenderWidget(QWidget *source)
7080 : source(source), ellipse(false) {}
7081
7082 void setEllipseEnabled(bool enable = true)
7083 {
7084 ellipse = enable;
7085 update();
7086 }
7087
7088protected:
7089 void paintEvent(QPaintEvent *)
7090 {
7091 if (ellipse) {
7092 QPainter painter(this);
7093 painter.fillRect(r: rect(), c: Qt::red);
7094 painter.end();
7095 QRegion regionToRender = QRegion(0, 0, source->width(), source->height() / 2,
7096 QRegion::Ellipse);
7097 source->render(target: this, targetOffset: QPoint(0, 30), sourceRegion: regionToRender);
7098 } else {
7099 source->render(target: this);
7100 }
7101 }
7102
7103private:
7104 QWidget *source;
7105 bool ellipse;
7106};
7107
7108void tst_QWidget::render()
7109{
7110 return;
7111 QCalendarWidget source;
7112 source.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7113 // disable anti-aliasing to eliminate potential differences when subpixel antialiasing
7114 // is enabled on the screen
7115 QFont f;
7116 f.setStyleStrategy(QFont::NoAntialias);
7117 source.setFont(f);
7118 source.show();
7119 QVERIFY(QTest::qWaitForWindowExposed(&source));
7120
7121 // Render the entire source into target.
7122 RenderWidget target(&source);
7123 target.resize(source.size());
7124 target.show();
7125
7126 QCoreApplication::processEvents();
7127 QCoreApplication::sendPostedEvents();
7128 QTest::qWait(ms: 250);
7129
7130 const QImage sourceImage = source.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1))).toImage();
7131 QCoreApplication::processEvents();
7132 QImage targetImage = target.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1))).toImage();
7133 QCoreApplication::processEvents();
7134 QCOMPARE(sourceImage, targetImage);
7135
7136 // Fill target.rect() will Qt::red and render
7137 // QRegion(0, 0, source->width(), source->height() / 2, QRegion::Ellipse)
7138 // of source into target with offset (0, 30).
7139 target.setEllipseEnabled();
7140 QCoreApplication::processEvents();
7141 QCoreApplication::sendPostedEvents();
7142
7143 targetImage = target.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1))).toImage();
7144 QVERIFY(sourceImage != targetImage);
7145
7146 QCOMPARE(targetImage.pixel(target.width() / 2, 29), QColor(Qt::red).rgb());
7147 QCOMPARE(targetImage.pixel(target.width() / 2, 30), sourceImage.pixel(source.width() / 2, 0));
7148
7149 // Test that a child widget properly fills its background
7150 {
7151 QWidget window;
7152 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7153 window.resize(w: 100, h: 100);
7154 // prevent custom styles
7155 window.setStyle(QStyleFactory::create(QLatin1String("Windows")));
7156 window.show();
7157 QVERIFY(QTest::qWaitForWindowExposed(&window));
7158 QWidget child(&window);
7159 child.resize(window.size());
7160 child.show();
7161
7162 QCoreApplication::processEvents();
7163 const QPixmap childPixmap = child.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1)));
7164 const QPixmap windowPixmap = window.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1)));
7165 QCOMPARE(childPixmap, windowPixmap);
7166 }
7167
7168 { // Check that the target offset is correct.
7169 QWidget widget;
7170 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7171 widget.resize(w: 200, h: 200);
7172 widget.setAutoFillBackground(true);
7173 widget.setPalette(Qt::red);
7174 // prevent custom styles
7175 widget.setStyle(QStyleFactory::create(QLatin1String("Windows")));
7176 widget.show();
7177 QVERIFY(QTest::qWaitForWindowExposed(&widget));
7178 QImage image(widget.size(), QImage::Format_RGB32);
7179 image.fill(pixel: QColor(Qt::blue).rgb());
7180
7181 // Target offset (0, 0)
7182 widget.render(target: &image, targetOffset: QPoint(), sourceRegion: QRect(20, 20, 100, 100));
7183 QCOMPARE(image.pixel(0, 0), QColor(Qt::red).rgb());
7184 QCOMPARE(image.pixel(99, 99), QColor(Qt::red).rgb());
7185 QCOMPARE(image.pixel(100, 100), QColor(Qt::blue).rgb());
7186
7187 // Target offset (20, 20).
7188 image.fill(pixel: QColor(Qt::blue).rgb());
7189 widget.render(target: &image, targetOffset: QPoint(20, 20), sourceRegion: QRect(20, 20, 100, 100));
7190 QCOMPARE(image.pixel(0, 0), QColor(Qt::blue).rgb());
7191 QCOMPARE(image.pixel(19, 19), QColor(Qt::blue).rgb());
7192 QCOMPARE(image.pixel(20, 20), QColor(Qt::red).rgb());
7193 QCOMPARE(image.pixel(119, 119), QColor(Qt::red).rgb());
7194 QCOMPARE(image.pixel(120, 120), QColor(Qt::blue).rgb());
7195 }
7196}
7197
7198// On Windows the active palette is used instead of the inactive palette even
7199// though the widget is invisible. This is probably related to task 178507/168682,
7200// but for the renderInvisible test it doesn't matter, we're mostly interested
7201// in testing the geometry so just workaround the palette issue for now.
7202static void workaroundPaletteIssue(QWidget *widget)
7203{
7204#ifndef Q_OS_WIN
7205 return;
7206#endif
7207 if (!widget)
7208 return;
7209
7210 QWidget *navigationBar = widget->findChild<QWidget *>(aName: QLatin1String("qt_calendar_navigationbar"));
7211 QVERIFY(navigationBar);
7212
7213 QPalette palette = navigationBar->palette();
7214 const QColor background = palette.color(cg: QPalette::Inactive, cr: navigationBar->backgroundRole());
7215 const QColor highlightedText = palette.color(cg: QPalette::Inactive, cr: QPalette::HighlightedText);
7216 palette.setColor(acg: QPalette::Active, acr: navigationBar->backgroundRole(), acolor: background);
7217 palette.setColor(acg: QPalette::Active, acr: QPalette::HighlightedText, acolor: highlightedText);
7218 navigationBar->setPalette(palette);
7219}
7220
7221//#define RENDER_DEBUG
7222void tst_QWidget::renderInvisible()
7223{
7224 if (m_platform == QStringLiteral("xcb"))
7225 QSKIP("QTBUG-26424");
7226 if (m_platform == QStringLiteral("winrt"))
7227 QSKIP("WinRT: This fails. QTBUG-68297.");
7228
7229 QScopedPointer<QCalendarWidget> calendar(new QCalendarWidget);
7230 calendar->move(m_availableTopLeft + QPoint(100, 100));
7231 calendar->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7232 // disable anti-aliasing to eliminate potential differences when subpixel antialiasing
7233 // is enabled on the screen
7234 QFont f;
7235 f.setStyleStrategy(QFont::NoAntialias);
7236 calendar->setFont(f);
7237 calendar->showNormal();
7238 QVERIFY(QTest::qWaitForWindowExposed(calendar.data()));
7239
7240 // Create a dummy focus widget to get rid of focus rect in reference image.
7241 QLineEdit dummyFocusWidget;
7242 dummyFocusWidget.setMinimumWidth(m_testWidgetSize.width());
7243 dummyFocusWidget.move(calendar->geometry().bottomLeft() + QPoint(0, 100));
7244 dummyFocusWidget.show();
7245 QVERIFY(QTest::qWaitForWindowExposed(&dummyFocusWidget));
7246 QCoreApplication::processEvents();
7247 QTest::qWait(ms: 120);
7248
7249 // Create normal reference image.
7250 const QSize calendarSize = calendar->size();
7251 QImage referenceImage(calendarSize, QImage::Format_ARGB32);
7252 calendar->render(target: &referenceImage);
7253#ifdef RENDER_DEBUG
7254 referenceImage.save("referenceImage.png");
7255#endif
7256 QVERIFY(!referenceImage.isNull());
7257
7258 // Create resized reference image.
7259 const QSize calendarSizeResized = calendar->size() + QSize(50, 50);
7260 calendar->resize(calendarSizeResized);
7261 QCoreApplication::processEvents();
7262 QTest::qWait(ms: 30);
7263 QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32);
7264 calendar->render(target: &referenceImageResized);
7265#ifdef RENDER_DEBUG
7266 referenceImageResized.save("referenceImageResized.png");
7267#endif
7268 QVERIFY(!referenceImageResized.isNull());
7269
7270 // Explicitly hide the calendar.
7271 calendar->hide();
7272 QCoreApplication::processEvents();
7273 QTest::qWait(ms: 30);
7274 workaroundPaletteIssue(widget: calendar.data());
7275
7276 { // Make sure we get the same image when the calendar is explicitly hidden.
7277 QImage testImage(calendarSizeResized, QImage::Format_ARGB32);
7278 calendar->render(target: &testImage);
7279#ifdef RENDER_DEBUG
7280 testImage.save("explicitlyHiddenCalendarResized.png");
7281#endif
7282 QCOMPARE(testImage, referenceImageResized);
7283 }
7284
7285 // Now that we have reference images we can delete the source and re-create
7286 // the calendar and check that we get the same images from a calendar which has never
7287 // been visible, laid out or created (Qt::WA_WState_Created).
7288 calendar.reset(other: new QCalendarWidget);
7289 calendar->setFont(f);
7290 workaroundPaletteIssue(widget: calendar.data());
7291
7292 { // Never been visible, created or laid out.
7293 QImage testImage(calendarSize, QImage::Format_ARGB32);
7294 calendar->render(target: &testImage);
7295#ifdef RENDER_DEBUG
7296 testImage.save("neverBeenVisibleCreatedOrLaidOut.png");
7297#endif
7298 QCOMPARE(testImage, referenceImage);
7299 }
7300
7301 calendar->hide();
7302 QCoreApplication::processEvents();
7303 QTest::qWait(ms: 30);
7304
7305 { // Calendar explicitly hidden.
7306 QImage testImage(calendarSize, QImage::Format_ARGB32);
7307 calendar->render(target: &testImage);
7308#ifdef RENDER_DEBUG
7309 testImage.save("explicitlyHiddenCalendar.png");
7310#endif
7311 QCOMPARE(testImage, referenceImage);
7312 }
7313
7314 // Get navigation bar and explicitly hide it.
7315 QWidget *navigationBar = calendar.data()->findChild<QWidget *>(aName: QLatin1String("qt_calendar_navigationbar"));
7316 QVERIFY(navigationBar);
7317 navigationBar->hide();
7318
7319 { // Check that the navigation bar isn't drawn when rendering the entire calendar.
7320 QImage testImage(calendarSize, QImage::Format_ARGB32);
7321 calendar->render(target: &testImage);
7322#ifdef RENDER_DEBUG
7323 testImage.save("calendarWithoutNavigationBar.png");
7324#endif
7325 QVERIFY(testImage != referenceImage);
7326 }
7327
7328 { // Make sure the navigation bar renders correctly even though it's hidden.
7329 QImage testImage(navigationBar->size(), QImage::Format_ARGB32);
7330 navigationBar->render(target: &testImage);
7331#ifdef RENDER_DEBUG
7332 testImage.save("explicitlyHiddenNavigationBar.png");
7333#endif
7334 QCOMPARE(testImage, referenceImage.copy(navigationBar->rect()));
7335 }
7336
7337 // Get next month button.
7338 QWidget *nextMonthButton = navigationBar->findChild<QWidget *>(aName: QLatin1String("qt_calendar_nextmonth"));
7339 QVERIFY(nextMonthButton);
7340
7341 { // Render next month button.
7342 // Fill test image with correct background color.
7343 QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32);
7344 navigationBar->render(target: &testImage, targetOffset: QPoint(), sourceRegion: QRegion(), renderFlags: QWidget::RenderFlags());
7345#ifdef RENDER_DEBUG
7346 testImage.save("nextMonthButtonBackground.png");
7347#endif
7348
7349 // Set the button's background color to Qt::transparent; otherwise it will fill the
7350 // background with QPalette::Window.
7351 const QPalette originalPalette = nextMonthButton->palette();
7352 QPalette palette = originalPalette;
7353 palette.setColor(acr: QPalette::Window, acolor: Qt::transparent);
7354 nextMonthButton->setPalette(palette);
7355
7356 // Render the button on top of the background.
7357 nextMonthButton->render(target: &testImage);
7358#ifdef RENDER_DEBUG
7359 testImage.save("nextMonthButton.png");
7360#endif
7361 const QRect buttonRect(nextMonthButton->mapTo(calendar.data(), QPoint()), nextMonthButton->size());
7362 QCOMPARE(testImage, referenceImage.copy(buttonRect));
7363
7364 // Restore palette.
7365 nextMonthButton->setPalette(originalPalette);
7366 }
7367
7368 // Navigation bar isn't explicitly hidden anymore.
7369 navigationBar->show();
7370 QCoreApplication::processEvents();
7371 QTest::qWait(ms: 30);
7372 QVERIFY(!calendar->isVisible());
7373
7374 // Now, completely mess up the layout. This will trigger an update on the layout
7375 // when the calendar is visible or shown, but it's not. QWidget::render must therefore
7376 // make sure the layout is activated before rendering.
7377 QVERIFY(!calendar->isVisible());
7378 calendar->resize(calendarSizeResized);
7379 QCoreApplication::processEvents();
7380
7381 { // Make sure we get an image equal to the resized reference image.
7382 QImage testImage(calendarSizeResized, QImage::Format_ARGB32);
7383 calendar->render(target: &testImage);
7384#ifdef RENDER_DEBUG
7385 testImage.save("calendarResized.png");
7386#endif
7387 QCOMPARE(testImage, referenceImageResized);
7388 }
7389
7390 { // Make sure we lay out the widget correctly the first time it's rendered.
7391 QCalendarWidget calendar;
7392 const QSize calendarSize = calendar.sizeHint();
7393
7394 QImage image(2 * calendarSize, QImage::Format_ARGB32);
7395 image.fill(pixel: QColor(Qt::red).rgb());
7396 calendar.render(target: &image);
7397
7398 for (int i = calendarSize.height(); i < 2 * calendarSize.height(); ++i)
7399 for (int j = calendarSize.width(); j < 2 * calendarSize.width(); ++j)
7400 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7401 }
7402
7403 { // Ensure that we don't call adjustSize() on invisible top-levels if render() is called
7404 // right after widgets have been added/removed to/from its layout.
7405 QWidget topLevel;
7406 topLevel.setLayout(new QVBoxLayout);
7407
7408 QWidget *widget = new QLineEdit;
7409 topLevel.layout()->addWidget(w: widget);
7410
7411 const QSize initialSize = topLevel.size();
7412 QPixmap pixmap(topLevel.sizeHint());
7413 topLevel.render(target: &pixmap); // triggers adjustSize()
7414 const QSize finalSize = topLevel.size();
7415 QVERIFY2(finalSize != initialSize,
7416 msgComparisonFailed(finalSize, "!=", initialSize));
7417
7418 topLevel.layout()->removeWidget(w: widget);
7419 QCOMPARE(topLevel.size(), finalSize);
7420 topLevel.render(target: &pixmap);
7421 QCOMPARE(topLevel.size(), finalSize);
7422
7423 topLevel.layout()->addWidget(w: widget);
7424 QCOMPARE(topLevel.size(), finalSize);
7425 topLevel.render(target: &pixmap);
7426 QCOMPARE(topLevel.size(), finalSize);
7427 }
7428}
7429
7430void tst_QWidget::renderWithPainter()
7431{
7432 QWidget widget(nullptr, Qt::Tool);
7433 // prevent custom styles
7434
7435 const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows")));
7436 widget.setStyle(style.data());
7437 widget.show();
7438 widget.resize(w: 70, h: 50);
7439 widget.setAutoFillBackground(true);
7440 widget.setPalette(Qt::black);
7441
7442 // Render the entire widget onto the image.
7443 QImage image(QSize(70, 50), QImage::Format_ARGB32);
7444 image.fill(pixel: QColor(Qt::red).rgb());
7445 QPainter painter(&image);
7446 widget.render(painter: &painter);
7447
7448 for (int i = 0; i < image.height(); ++i) {
7449 for (int j = 0; j < image.width(); ++j)
7450 QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb());
7451 }
7452
7453 // Translate painter (10, 10).
7454 painter.save();
7455 image.fill(pixel: QColor(Qt::red).rgb());
7456 painter.translate(dx: 10, dy: 10);
7457 widget.render(painter: &painter);
7458 painter.restore();
7459
7460 for (int i = 0; i < image.height(); ++i) {
7461 for (int j = 0; j < image.width(); ++j) {
7462 if (i < 10 || j < 10)
7463 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7464 else
7465 QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb());
7466 }
7467 }
7468
7469 // Pass target offset (10, 10) (the same as QPainter::translate).
7470 image.fill(pixel: QColor(Qt::red).rgb());
7471 widget.render(painter: &painter, targetOffset: QPoint(10, 10));
7472
7473 for (int i = 0; i < image.height(); ++i) {
7474 for (int j = 0; j < image.width(); ++j) {
7475 if (i < 10 || j < 10)
7476 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7477 else
7478 QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb());
7479 }
7480 }
7481
7482 // Translate (10, 10) and pass target offset (10, 10).
7483 painter.save();
7484 image.fill(pixel: QColor(Qt::red).rgb());
7485 painter.translate(dx: 10, dy: 10);
7486 widget.render(painter: &painter, targetOffset: QPoint(10, 10));
7487 painter.restore();
7488
7489 for (int i = 0; i < image.height(); ++i) {
7490 for (int j = 0; j < image.width(); ++j) {
7491 if (i < 20 || j < 20)
7492 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7493 else
7494 QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb());
7495 }
7496 }
7497
7498 // Rotate painter 90 degrees.
7499 painter.save();
7500 image.fill(pixel: QColor(Qt::red).rgb());
7501 painter.rotate(a: 90);
7502 widget.render(painter: &painter);
7503 painter.restore();
7504
7505 for (int i = 0; i < image.height(); ++i) {
7506 for (int j = 0; j < image.width(); ++j)
7507 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7508 }
7509
7510 // Translate and rotate.
7511 image.fill(pixel: QColor(Qt::red).rgb());
7512 widget.resize(w: 40, h: 10);
7513 painter.translate(dx: 10, dy: 10);
7514 painter.rotate(a: 90);
7515 widget.render(painter: &painter);
7516
7517 for (int i = 0; i < image.height(); ++i) {
7518 for (int j = 0; j < image.width(); ++j) {
7519 if (i >= 10 && j >= 0 && j < 10)
7520 QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb());
7521 else
7522 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7523 }
7524 }
7525
7526 // Make sure QWidget::render does not modify the render hints set on the painter.
7527 painter.setRenderHints(hints: QPainter::Antialiasing | QPainter::SmoothPixmapTransform
7528 | QPainter::TextAntialiasing);
7529 QPainter::RenderHints oldRenderHints = painter.renderHints();
7530 widget.render(painter: &painter);
7531 QCOMPARE(painter.renderHints(), oldRenderHints);
7532}
7533
7534void tst_QWidget::render_task188133()
7535{
7536 QMainWindow mainWindow;
7537
7538 // Make sure QWidget::render does not trigger QWidget::repaint/update
7539 // and asserts for Qt::WA_WState_Created.
7540 const QPixmap pixmap = mainWindow.grab(rectangle: QRect(QPoint(0, 0), QSize(-1, -1)));
7541 Q_UNUSED(pixmap)
7542}
7543
7544void tst_QWidget::render_task211796()
7545{
7546 class MyWidget : public QWidget
7547 {
7548 void resizeEvent(QResizeEvent *) override
7549 {
7550 QPixmap pixmap(size());
7551 render(target: &pixmap);
7552 }
7553 };
7554
7555 { // Please don't die in a resize recursion.
7556 MyWidget widget;
7557 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7558 widget.resize(m_testWidgetSize);
7559 centerOnScreen(w: &widget);
7560 widget.show();
7561 }
7562
7563 { // Same check with a deeper hierarchy.
7564 QWidget widget;
7565 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
7566 widget.resize(m_testWidgetSize);
7567 centerOnScreen(w: &widget);
7568 widget.show();
7569 QWidget child(&widget);
7570 MyWidget grandChild;
7571 grandChild.setParent(&child);
7572 grandChild.resize(w: 100, h: 100);
7573 child.show();
7574 }
7575}
7576
7577void tst_QWidget::render_task217815()
7578{
7579 // Make sure we don't change the size of the widget when calling
7580 // render() and the widget has an explicit size set.
7581 // This was a problem on Windows because we called createWinId(),
7582 // which in turn enforced the size to be bigger than the smallest
7583 // possible native window size (which is (115,something) on WinXP).
7584 QWidget widget;
7585 const QSize explicitSize(80, 20);
7586 widget.resize(explicitSize);
7587 QCOMPARE(widget.size(), explicitSize);
7588
7589 QPixmap pixmap(explicitSize);
7590 widget.render(target: &pixmap);
7591
7592 QCOMPARE(widget.size(), explicitSize);
7593}
7594
7595// Window Opacity is not supported on Windows CE.
7596void tst_QWidget::render_windowOpacity()
7597{
7598 if (m_platform == QStringLiteral("offscreen"))
7599 QSKIP("Platform offscreen does not support setting opacity");
7600
7601 const qreal opacity = 0.5;
7602
7603 { // Check that the painter opacity effects the widget drawing.
7604 QWidget topLevel;
7605 QWidget child(&topLevel);
7606 child.resize(w: 50, h: 50);
7607 child.setPalette(Qt::red);
7608 child.setAutoFillBackground(true);
7609
7610 QPixmap expected(child.size());
7611
7612 if (m_platform == QStringLiteral("xcb") && expected.depth() < 24)
7613 QSKIP("This test won't give correct results with dithered pixmaps");
7614
7615 expected.fill(fillColor: Qt::green);
7616 QPainter painter(&expected);
7617 painter.setOpacity(opacity);
7618 painter.fillRect(r: QRect(QPoint(0, 0), child.size()), c: Qt::red);
7619 painter.end();
7620
7621 QPixmap result(child.size());
7622 result.fill(fillColor: Qt::green);
7623 painter.begin(&result);
7624 painter.setOpacity(opacity);
7625 child.render(painter: &painter);
7626 painter.end();
7627 QCOMPARE(result, expected);
7628 }
7629
7630 { // Combine the opacity set on the painter with the widget opacity.
7631 class MyWidget : public QWidget
7632 {
7633 public:
7634 explicit MyWidget(qreal opacityIn) : opacity(opacityIn) {}
7635 void paintEvent(QPaintEvent *) override
7636 {
7637 QPainter painter(this);
7638 painter.setOpacity(opacity);
7639 QCOMPARE(painter.opacity(), opacity);
7640 painter.fillRect(r: rect(), c: Qt::red);
7641 }
7642 const qreal opacity;
7643 };
7644
7645 MyWidget widget(opacity);
7646 widget.resize(w: 50, h: 50);
7647 widget.setPalette(Qt::blue);
7648 widget.setAutoFillBackground(true);
7649
7650 QPixmap expected(widget.size());
7651 expected.fill(fillColor: Qt::green);
7652 QPainter painter(&expected);
7653 painter.setOpacity(opacity);
7654 QPixmap pixmap(widget.size());
7655 pixmap.fill(fillColor: Qt::blue);
7656 QPainter pixmapPainter(&pixmap);
7657 pixmapPainter.setOpacity(opacity);
7658 pixmapPainter.fillRect(r: QRect(QPoint(), widget.size()), c: Qt::red);
7659 painter.drawPixmap(p: QPoint(), pm: pixmap);
7660 painter.end();
7661
7662 QPixmap result(widget.size());
7663 result.fill(fillColor: Qt::green);
7664 painter.begin(&result);
7665 painter.setOpacity(opacity);
7666 widget.render(painter: &painter);
7667 painter.end();
7668 QCOMPARE(result, expected);
7669 }
7670}
7671
7672void tst_QWidget::render_systemClip()
7673{
7674 QWidget widget;
7675 widget.setPalette(Qt::blue);
7676 widget.resize(w: 100, h: 100);
7677
7678 QImage image(widget.size(), QImage::Format_RGB32);
7679 image.fill(pixel: QColor(Qt::red).rgb());
7680
7681 QPaintEngine *paintEngine = image.paintEngine();
7682 QVERIFY(paintEngine);
7683 paintEngine->setSystemClip(QRegion(0, 0, 50, 50));
7684
7685 QPainter painter(&image);
7686 // Make sure we're using the same paint engine and has the right clip set.
7687 QCOMPARE(painter.paintEngine(), paintEngine);
7688 QCOMPARE(paintEngine->systemClip(), QRegion(0, 0, 50, 50));
7689
7690 // Translate painter outside system clip.
7691 painter.translate(dx: 50, dy: 0);
7692 widget.render(painter: &painter);
7693
7694#ifdef RENDER_DEBUG
7695 image.save("outside_systemclip.png");
7696#endif
7697
7698 // All pixels should be red.
7699 for (int i = 0; i < image.height(); ++i) {
7700 for (int j = 0; j < image.width(); ++j)
7701 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7702 }
7703
7704 // Restore painter and refill image with red.
7705 image.fill(pixel: QColor(Qt::red).rgb());
7706 painter.translate(dx: -50, dy: 0);
7707
7708 // Set transform on the painter.
7709 QTransform transform;
7710 transform.shear(sh: 0, sv: 1);
7711 painter.setTransform(transform);
7712 widget.render(painter: &painter);
7713
7714#ifdef RENDER_DEBUG
7715 image.save("blue_triangle.png");
7716#endif
7717
7718 // We should now have a blue triangle starting at scan line 1, and the rest should be red.
7719 // rrrrrrrrrr
7720 // brrrrrrrrr
7721 // bbrrrrrrrr
7722 // bbbrrrrrrr
7723 // bbbbrrrrrr
7724 // rrrrrrrrrr
7725 // ...
7726
7727#ifndef Q_OS_MACOS
7728 for (int i = 0; i < image.height(); ++i) {
7729 for (int j = 0; j < image.width(); ++j) {
7730 if (i < 50 && j < i)
7731 QCOMPARE(image.pixel(j, i), QColor(Qt::blue).rgb());
7732 else
7733 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7734 }
7735 }
7736#else
7737 // We don't paint directly on the image on the Mac, so we cannot do the pixel comparison
7738 // as above due to QPainter::SmoothPixmapTransform. We therefore need to generate an
7739 // expected image by first painting on a pixmap, and then draw the pixmap onto
7740 // the image using QPainter::SmoothPixmapTransform. Then we can compare pixels :)
7741 // The check is basically the same, except that it takes the smoothening into account.
7742 QPixmap pixmap(50, 50);
7743 const QRegion sysClip(0, 0, 50, 50);
7744 widget.render(&pixmap, QPoint(), sysClip);
7745
7746 QImage expectedImage(widget.size(), QImage::Format_RGB32);
7747 expectedImage.fill(QColor(Qt::red).rgb());
7748 expectedImage.paintEngine()->setSystemClip(sysClip);
7749
7750 QPainter expectedImagePainter(&expectedImage);
7751 expectedImagePainter.setTransform(QTransform().shear(0, 1));
7752 // NB! This is the important part (SmoothPixmapTransform).
7753 expectedImagePainter.setRenderHints(QPainter::SmoothPixmapTransform);
7754 expectedImagePainter.drawPixmap(QPoint(0, 0), pixmap);
7755 expectedImagePainter.end();
7756
7757 QCOMPARE(image, expectedImage);
7758#endif
7759}
7760
7761void tst_QWidget::render_systemClip2_data()
7762{
7763 QTest::addColumn<bool>(name: "autoFillBackground");
7764 QTest::addColumn<bool>(name: "usePaintEvent");
7765 QTest::addColumn<QColor>(name: "expectedColor");
7766
7767 QTest::newRow(dataTag: "Only auto-fill background") << true << false << QColor(Qt::blue);
7768 QTest::newRow(dataTag: "Only draw in paintEvent") << false << true << QColor(Qt::green);
7769 QTest::newRow(dataTag: "Auto-fill background and draw in paintEvent") << true << true << QColor(Qt::green);
7770}
7771
7772void tst_QWidget::render_systemClip2()
7773{
7774 QFETCH(bool, autoFillBackground);
7775 QFETCH(bool, usePaintEvent);
7776 QFETCH(QColor, expectedColor);
7777
7778 QVERIFY2(expectedColor != QColor(Qt::red), "Qt::red is the reference color for the image, pick another color");
7779
7780 class MyWidget : public QWidget
7781 {
7782 public:
7783 explicit MyWidget(bool usePaintEventIn) : usePaintEvent(usePaintEventIn) {}
7784 const bool usePaintEvent;
7785 void paintEvent(QPaintEvent *) override
7786 {
7787 if (usePaintEvent)
7788 QPainter(this).fillRect(r: rect(), c: Qt::green);
7789 }
7790 };
7791
7792 MyWidget widget(usePaintEvent);
7793 widget.setPalette(Qt::blue);
7794 // NB! widget.setAutoFillBackground(autoFillBackground) won't do the
7795 // trick here since the widget is a top-level. The background is filled
7796 // regardless, unless Qt::WA_OpaquePaintEvent or Qt::WA_NoSystemBackground
7797 // is set. We therefore use the opaque attribute to turn off auto-fill.
7798 if (!autoFillBackground)
7799 widget.setAttribute(Qt::WA_OpaquePaintEvent);
7800 widget.resize(w: 100, h: 100);
7801
7802 QImage image(widget.size(), QImage::Format_RGB32);
7803 image.fill(pixel: QColor(Qt::red).rgb());
7804
7805 QPaintEngine *paintEngine = image.paintEngine();
7806 QVERIFY(paintEngine);
7807
7808 QRegion systemClip(QRegion(50, 0, 50, 10));
7809 systemClip += QRegion(90, 10, 10, 40);
7810 paintEngine->setSystemClip(systemClip);
7811
7812 // Render entire widget directly onto device.
7813 widget.render(target: &image);
7814
7815#ifdef RENDER_DEBUG
7816 image.save("systemclip_with_device.png");
7817#endif
7818 // All pixels within the system clip should now be
7819 // the expectedColor, and the rest should be red.
7820 for (int i = 0; i < image.height(); ++i) {
7821 for (int j = 0; j < image.width(); ++j) {
7822 if (systemClip.contains(p: QPoint(j, i)))
7823 QCOMPARE(image.pixel(j, i), expectedColor.rgb());
7824 else
7825 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7826 }
7827 }
7828
7829 // Refill image with red.
7830 image.fill(pixel: QColor(Qt::red).rgb());
7831 paintEngine->setSystemClip(systemClip);
7832
7833 // Do the same with an untransformed painter.
7834 QPainter painter(&image);
7835 //Make sure we're using the same paint engine and has the right clip set.
7836 QCOMPARE(painter.paintEngine(), paintEngine);
7837 QCOMPARE(paintEngine->systemClip(), systemClip);
7838
7839 widget.render(painter: &painter);
7840
7841#ifdef RENDER_DEBUG
7842 image.save("systemclip_with_untransformed_painter.png");
7843#endif
7844 // All pixels within the system clip should now be
7845 // the expectedColor, and the rest should be red.
7846 for (int i = 0; i < image.height(); ++i) {
7847 for (int j = 0; j < image.width(); ++j) {
7848 if (systemClip.contains(p: QPoint(j, i)))
7849 QCOMPARE(image.pixel(j, i), expectedColor.rgb());
7850 else
7851 QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb());
7852 }
7853 }
7854}
7855
7856void tst_QWidget::render_systemClip3_data()
7857{
7858 QTest::addColumn<QSize>(name: "size");
7859 QTest::addColumn<bool>(name: "useSystemClip");
7860
7861 // Reference: http://en.wikipedia.org/wiki/Flag_of_Norway
7862 QTest::newRow(dataTag: "Norwegian Civil Flag") << QSize(220, 160) << false;
7863 QTest::newRow(dataTag: "Norwegian War Flag") << QSize(270, 160) << true;
7864}
7865
7866// This test ensures that the current engine clip (systemClip + painter clip)
7867// is preserved after QPainter::setClipRegion(..., Qt::ReplaceClip);
7868void tst_QWidget::render_systemClip3()
7869{
7870 QFETCH(QSize, size);
7871 QFETCH(bool, useSystemClip);
7872
7873 // Calculate the inner/outer cross of the flag.
7874 QRegion outerCross(0, 0, size.width(), size.height());
7875 outerCross -= QRect(0, 0, 60, 60);
7876 outerCross -= QRect(100, 0, size.width() - 100, 60);
7877 outerCross -= QRect(0, 100, 60, 60);
7878 outerCross -= QRect(100, 100, size.width() - 100, 60);
7879
7880 QRegion innerCross(0, 0, size.width(), size.height());
7881 innerCross -= QRect(0, 0, 70, 70);
7882 innerCross -= QRect(90, 0, size.width() - 90, 70);
7883 innerCross -= QRect(0, 90, 70, 70);
7884 innerCross -= QRect(90, 90, size.width() - 90, 70);
7885
7886 const QRegion redArea(QRegion(0, 0, size.width(), size.height()) - outerCross);
7887 const QRegion whiteArea(outerCross - innerCross);
7888 QRegion systemClip;
7889
7890 // Okay, here's the image that should look like a Norwegian civil/war flag in the end.
7891 QImage flag(size, QImage::Format_ARGB32);
7892 flag.fill(pixel: QColor(Qt::transparent).rgba());
7893
7894 if (useSystemClip) {
7895 QPainterPath warClip(QPoint(size.width(), 0));
7896 warClip.lineTo(x: size.width() - 110, y: 60);
7897 warClip.lineTo(x: size.width(), y: 80);
7898 warClip.lineTo(x: size.width() - 110, y: 100);
7899 warClip.lineTo(x: size.width(), y: 160);
7900 warClip.closeSubpath();
7901 systemClip = QRegion(0, 0, size.width(), size.height()) - QRegion(warClip.toFillPolygon().toPolygon());
7902 flag.paintEngine()->setSystemClip(systemClip);
7903 }
7904
7905 QPainter painter(&flag);
7906 painter.fillRect(r: QRect(QPoint(), size), c: Qt::red); // Fill image background with red.
7907 painter.setClipRegion(outerCross); // Limit widget painting to inside the outer cross.
7908
7909 // Here's the widget that's supposed to draw the inner/outer cross of the flag.
7910 // The outer cross (white) should be drawn when the background is auto-filled, and
7911 // the inner cross (blue) should be drawn in the paintEvent.
7912 class MyWidget : public QWidget
7913 {
7914 public:
7915 void paintEvent(QPaintEvent *) override
7916 {
7917 QPainter painter(this);
7918 // Be evil and try to paint outside the outer cross. This should not be
7919 // possible since the shared painter is clipped to the outer cross.
7920 painter.setClipRect(x: 0, y: 0, w: 60, h: 60, op: Qt::ReplaceClip);
7921 painter.fillRect(r: rect(), c: Qt::green);
7922 painter.setClipRegion(clip, op: Qt::ReplaceClip);
7923 painter.fillRect(r: rect(), c: Qt::blue);
7924 }
7925 QRegion clip;
7926 };
7927
7928 MyWidget widget;
7929 widget.clip = innerCross;
7930 widget.setFixedSize(size);
7931 widget.setPalette(Qt::white);
7932 widget.setAutoFillBackground(true);
7933 widget.render(painter: &painter);
7934
7935#ifdef RENDER_DEBUG
7936 flag.save("flag.png");
7937#endif
7938
7939 // Let's make sure we got a Norwegian flag.
7940 for (int i = 0; i < flag.height(); ++i) {
7941 for (int j = 0; j < flag.width(); ++j) {
7942 const QPoint pixel(j, i);
7943 const QRgb pixelValue = flag.pixel(pt: pixel);
7944 if (useSystemClip && !systemClip.contains(p: pixel))
7945 QCOMPARE(pixelValue, QColor(Qt::transparent).rgba());
7946 else if (redArea.contains(p: pixel))
7947 QCOMPARE(pixelValue, QColor(Qt::red).rgba());
7948 else if (whiteArea.contains(p: pixel))
7949 QCOMPARE(pixelValue, QColor(Qt::white).rgba());
7950 else
7951 QCOMPARE(pixelValue, QColor(Qt::blue).rgba());
7952 }
7953 }
7954}
7955
7956void tst_QWidget::render_task252837()
7957{
7958 QWidget widget;
7959 widget.resize(w: 200, h: 200);
7960
7961 QPixmap pixmap(widget.size());
7962 QPainter painter(&pixmap);
7963 // Please do not crash.
7964 widget.render(painter: &painter);
7965}
7966
7967void tst_QWidget::render_worldTransform()
7968{
7969 class MyWidget : public QWidget
7970 {
7971 public:
7972 void paintEvent(QPaintEvent *) override
7973 {
7974 QPainter painter(this);
7975 // Make sure world transform is identity.
7976 QCOMPARE(painter.worldTransform(), QTransform());
7977
7978 // Make sure device transform is correct.
7979 const QPoint widgetOffset = geometry().topLeft();
7980 QTransform expectedDeviceTransform = QTransform::fromTranslate(dx: 105, dy: 5);
7981 expectedDeviceTransform.rotate(a: 90);
7982 expectedDeviceTransform.translate(dx: widgetOffset.x(), dy: widgetOffset.y());
7983 QCOMPARE(painter.deviceTransform(), expectedDeviceTransform);
7984
7985 // Set new world transform.
7986 QTransform newWorldTransform = QTransform::fromTranslate(dx: 10, dy: 10);
7987 newWorldTransform.rotate(a: 90);
7988 painter.setWorldTransform(matrix: newWorldTransform);
7989 QCOMPARE(painter.worldTransform(), newWorldTransform);
7990
7991 // Again, check device transform.
7992 expectedDeviceTransform.translate(dx: 10, dy: 10);
7993 expectedDeviceTransform.rotate(a: 90);
7994 QCOMPARE(painter.deviceTransform(), expectedDeviceTransform);
7995
7996 painter.fillRect(r: QRect(0, 0, 20, 10), c: Qt::green);
7997 }
7998 };
7999
8000 MyWidget widget;
8001 widget.setFixedSize(w: 100, h: 100);
8002 widget.setPalette(Qt::red);
8003 widget.setAutoFillBackground(true);
8004
8005 MyWidget child;
8006 child.setParent(&widget);
8007 child.move(ax: 50, ay: 50);
8008 child.setFixedSize(w: 50, h: 50);
8009 child.setPalette(Qt::blue);
8010 child.setAutoFillBackground(true);
8011
8012 QImage image(QSize(110, 110), QImage::Format_RGB32);
8013 image.fill(pixel: QColor(Qt::black).rgb());
8014
8015 QPainter painter(&image);
8016 painter.translate(dx: 105, dy: 5);
8017 painter.rotate(a: 90);
8018
8019 // Render widgets onto image.
8020 widget.render(painter: &painter);
8021#ifdef RENDER_DEBUG
8022 image.save("render_worldTransform_image.png");
8023#endif
8024
8025 // Ensure the transforms are unchanged after render.
8026 QCOMPARE(painter.worldTransform(), painter.worldTransform());
8027 QCOMPARE(painter.deviceTransform(), painter.deviceTransform());
8028 painter.end();
8029
8030 // Paint expected image.
8031 QImage expected(QSize(110, 110), QImage::Format_RGB32);
8032 expected.fill(pixel: QColor(Qt::black).rgb());
8033
8034 QPainter expectedPainter(&expected);
8035 expectedPainter.translate(dx: 105, dy: 5);
8036 expectedPainter.rotate(a: 90);
8037 expectedPainter.save();
8038 expectedPainter.fillRect(r: widget.rect(),c: Qt::red);
8039 expectedPainter.translate(dx: 10, dy: 10);
8040 expectedPainter.rotate(a: 90);
8041 expectedPainter.fillRect(r: QRect(0, 0, 20, 10), c: Qt::green);
8042 expectedPainter.restore();
8043 expectedPainter.translate(dx: 50, dy: 50);
8044 expectedPainter.fillRect(r: child.rect(),c: Qt::blue);
8045 expectedPainter.translate(dx: 10, dy: 10);
8046 expectedPainter.rotate(a: 90);
8047 expectedPainter.fillRect(r: QRect(0, 0, 20, 10), c: Qt::green);
8048 expectedPainter.end();
8049
8050#ifdef RENDER_DEBUG
8051 expected.save("render_worldTransform_expected.png");
8052#endif
8053
8054 QCOMPARE(image, expected);
8055}
8056
8057void tst_QWidget::setContentsMargins()
8058{
8059 QLabel label("why does it always rain on me?");
8060 QSize oldSize = label.sizeHint();
8061 label.setFrameStyle(QFrame::Sunken | QFrame::Box);
8062 QSize newSize = label.sizeHint();
8063 QVERIFY2(oldSize != newSize, msgComparisonFailed(oldSize, "!=", newSize));
8064
8065 QLabel label2("why does it always rain on me?");
8066 label2.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8067 label2.show();
8068 label2.setFrameStyle(QFrame::Sunken | QFrame::Box);
8069 QCOMPARE(newSize, label2.sizeHint());
8070
8071 QLabel label3("why does it always rain on me?");
8072 label3.setFrameStyle(QFrame::Sunken | QFrame::Box);
8073 QCOMPARE(newSize, label3.sizeHint());
8074}
8075
8076void tst_QWidget::moveWindowInShowEvent_data()
8077{
8078 QTest::addColumn<QPoint>(name: "initial");
8079 QTest::addColumn<QPoint>(name: "position");
8080
8081 QPoint p = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
8082
8083 QTest::newRow(dataTag: "1") << p << (p + QPoint(10, 10));
8084 QTest::newRow(dataTag: "2") << (p + QPoint(10,10)) << p;
8085}
8086
8087void tst_QWidget::moveWindowInShowEvent()
8088{
8089 if (m_platform == QStringLiteral("xcb"))
8090 QSKIP("QTBUG-26424");
8091
8092 QFETCH(QPoint, initial);
8093 QFETCH(QPoint, position);
8094
8095 class MoveWindowInShowEventWidget : public QWidget
8096 {
8097 public:
8098 QPoint position;
8099 void showEvent(QShowEvent *) override
8100 {
8101 move(position);
8102 }
8103 };
8104
8105 MoveWindowInShowEventWidget widget;
8106 QScreen *screen = QGuiApplication::primaryScreen();
8107 widget.resize(QSize(screen->availableGeometry().size() / 3).expandedTo(otherSize: QSize(1, 1)));
8108 // move to this position in showEvent()
8109 widget.position = position;
8110
8111 // put the widget in it's starting position
8112 widget.move(initial);
8113 QCOMPARE(widget.pos(), initial);
8114
8115 // show it
8116 widget.showNormal();
8117 QVERIFY(QTest::qWaitForWindowExposed(&widget));
8118 QTest::qWait(ms: 100);
8119 // it should have moved
8120 QCOMPARE(widget.pos(), position);
8121}
8122
8123void tst_QWidget::repaintWhenChildDeleted()
8124{
8125#ifdef Q_OS_WIN
8126 QTest::qWait(1000);
8127#endif
8128 ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red);
8129 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8130 QPoint startPoint = w.screen()->availableGeometry().topLeft();
8131 startPoint.rx() += 50;
8132 startPoint.ry() += 50;
8133 w.setGeometry(QRect(startPoint, QSize(100, 100)));
8134 w.show();
8135 QVERIFY(QTest::qWaitForWindowExposed(&w));
8136 QTRY_COMPARE(w.r, QRegion(w.rect()));
8137 w.r = QRegion();
8138
8139 {
8140 ColorWidget child(&w, Qt::Widget, Qt::blue);
8141 child.setGeometry(ax: 10, ay: 10, aw: 10, ah: 10);
8142 child.show();
8143 QTRY_COMPARE(child.r, QRegion(child.rect()));
8144 w.r = QRegion();
8145 }
8146
8147 QTRY_COMPARE(w.r, QRegion(10, 10, 10, 10));
8148}
8149
8150// task 175114
8151void tst_QWidget::hideOpaqueChildWhileHidden()
8152{
8153 ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red);
8154 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8155 QPoint startPoint = w.screen()->availableGeometry().topLeft();
8156 startPoint.rx() += 50;
8157 startPoint.ry() += 50;
8158 w.setGeometry(QRect(startPoint, QSize(100, 100)));
8159
8160 ColorWidget child(&w, Qt::Widget, Qt::blue);
8161 child.setGeometry(ax: 10, ay: 10, aw: 80, ah: 80);
8162
8163 ColorWidget child2(&child, Qt::Widget, Qt::white);
8164 child2.setGeometry(ax: 10, ay: 10, aw: 60, ah: 60);
8165
8166 w.show();
8167 QVERIFY(QTest::qWaitForWindowExposed(&w));
8168 QTRY_COMPARE(child2.r, QRegion(child2.rect()));
8169 child.r = QRegion();
8170 child2.r = QRegion();
8171 w.r = QRegion();
8172
8173 child.hide();
8174 child2.hide();
8175
8176 if (m_platform == QStringLiteral("winrt"))
8177 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
8178 QTRY_COMPARE(w.r, QRegion(child.geometry()));
8179
8180 child.show();
8181 QTRY_COMPARE(child.r, QRegion(child.rect()));
8182 QCOMPARE(child2.r, QRegion());
8183}
8184
8185// This test doesn't make sense without support for showMinimized().
8186void tst_QWidget::updateWhileMinimized()
8187{
8188 if (m_platform == QStringLiteral("wayland"))
8189 QSKIP("Wayland: This fails. Figure out why.");
8190 if (m_platform == QStringLiteral("offscreen"))
8191 QSKIP("Platform offscreen does not support showMinimized()");
8192
8193#if defined(Q_OS_QNX)
8194 QSKIP("Platform does not support showMinimized()");
8195#endif
8196 UpdateWidget widget;
8197 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8198 // Filter out activation change and focus events to avoid update() calls in QWidget.
8199 widget.updateOnActivationChangeAndFocusIn = false;
8200 widget.reset();
8201 widget.show();
8202 QVERIFY(QTest::qWaitForWindowExposed(&widget));
8203 QTRY_VERIFY(widget.numPaintEvents > 0);
8204 QTest::qWait(ms: 150);
8205
8206 // Minimize window.
8207 widget.showMinimized();
8208 QTest::qWait(ms: 110);
8209
8210 widget.reset();
8211
8212 // The widget is not visible on the screen (but isVisible() still returns true).
8213 // Make sure update requests are discarded until the widget is shown again.
8214 widget.update(ax: 0, ay: 0, aw: 50, ah: 50);
8215 QTest::qWait(ms: 10);
8216 if (m_platform == QStringLiteral("winrt"))
8217 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
8218 int count = 0;
8219 // mutter/GNOME Shell doesn't unmap when minimizing window.
8220 // More details at https://gitlab.gnome.org/GNOME/mutter/issues/185
8221 if (m_platform == QStringLiteral("xcb")) {
8222 const QString desktop = qgetenv(varName: "XDG_CURRENT_DESKTOP");
8223 qDebug() << "xcb: XDG_CURRENT_DESKTOP=" << desktop;
8224 if (desktop == QStringLiteral("ubuntu:GNOME")
8225 || desktop == QStringLiteral("GNOME-Classic:GNOME")
8226 || desktop == QStringLiteral("GNOME"))
8227 count = 1;
8228 }
8229 QCOMPARE(widget.numPaintEvents, count);
8230
8231 // Restore window.
8232 widget.showNormal();
8233 QTRY_COMPARE(widget.numPaintEvents, 1);
8234 QCOMPARE(widget.paintedRegion, QRegion(0, 0, 50, 50));
8235}
8236
8237class PaintOnScreenWidget: public QWidget
8238{
8239public:
8240 using QWidget::QWidget;
8241#if defined(Q_OS_WIN)
8242 // This is the only way to enable PaintOnScreen on Windows.
8243 QPaintEngine *paintEngine() const override { return nullptr; }
8244#endif
8245};
8246
8247void tst_QWidget::alienWidgets()
8248{
8249 if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows"))
8250 QSKIP("This test is only for X11/Windows.");
8251
8252 qApp->setAttribute(attribute: Qt::AA_DontCreateNativeWidgetSiblings);
8253 QWidget parent;
8254 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8255 parent.resize(m_testWidgetSize);
8256 QWidget child(&parent);
8257 QWidget grandChild(&child);
8258 QWidget greatGrandChild(&grandChild);
8259 parent.show();
8260
8261 QVERIFY(QTest::qWaitForWindowExposed(&parent));
8262
8263 // Verify that the WA_WState_Created attribute is set
8264 // and the top-level is the only native window.
8265 QVERIFY(parent.testAttribute(Qt::WA_WState_Created));
8266 QVERIFY(parent.internalWinId());
8267
8268 QVERIFY(child.testAttribute(Qt::WA_WState_Created));
8269 QVERIFY(!child.internalWinId());
8270
8271 QVERIFY(grandChild.testAttribute(Qt::WA_WState_Created));
8272 QVERIFY(!grandChild.internalWinId());
8273
8274 QVERIFY(greatGrandChild.testAttribute(Qt::WA_WState_Created));
8275 QVERIFY(!greatGrandChild.internalWinId());
8276
8277 // Enforce native windows all the way up in the parent hierarchy
8278 // if not WA_DontCreateNativeAncestors is set.
8279 grandChild.setAttribute(Qt::WA_DontCreateNativeAncestors);
8280 greatGrandChild.setAttribute(Qt::WA_NativeWindow);
8281 QVERIFY(greatGrandChild.internalWinId());
8282 QVERIFY(grandChild.internalWinId());
8283 QVERIFY(!child.internalWinId());
8284
8285 {
8286 // Ensure that hide() on an ancestor of a widget with
8287 // Qt::WA_DontCreateNativeAncestors still gets unmapped
8288 QWidget window;
8289 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8290 window.resize(m_testWidgetSize);
8291 QWidget widget(&window);
8292 QWidget child(&widget);
8293 child.setAttribute(Qt::WA_NativeWindow);
8294 child.setAttribute(Qt::WA_DontCreateNativeAncestors);
8295 window.show();
8296 QVERIFY(QTest::qWaitForWindowExposed(&window));
8297 QTRY_VERIFY(child.testAttribute(Qt::WA_Mapped));
8298 widget.hide();
8299 QTRY_VERIFY(!child.testAttribute(Qt::WA_Mapped));
8300 }
8301
8302 // Enforce a native window when calling QWidget::winId.
8303 QVERIFY(child.winId());
8304 QVERIFY(child.internalWinId());
8305
8306 // Check that paint on screen widgets (incl. children) are native.
8307 PaintOnScreenWidget paintOnScreen(&parent);
8308 QWidget paintOnScreenChild(&paintOnScreen);
8309 paintOnScreen.show();
8310 QVERIFY(paintOnScreen.testAttribute(Qt::WA_WState_Created));
8311 QVERIFY(!paintOnScreen.testAttribute(Qt::WA_NativeWindow));
8312 QVERIFY(!paintOnScreen.internalWinId());
8313 QVERIFY(!paintOnScreenChild.testAttribute(Qt::WA_NativeWindow));
8314 QVERIFY(!paintOnScreenChild.internalWinId());
8315
8316 paintOnScreen.setAttribute(Qt::WA_PaintOnScreen);
8317 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8318 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8319 QVERIFY(paintOnScreen.testAttribute(Qt::WA_NativeWindow));
8320 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8321 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8322 QVERIFY(paintOnScreen.internalWinId());
8323 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8324 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8325 QVERIFY(paintOnScreenChild.testAttribute(Qt::WA_NativeWindow));
8326 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8327 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8328 QVERIFY(paintOnScreenChild.internalWinId());
8329
8330 // Check that widgets with the Qt::MSWindowsOwnDC attribute set
8331 // are native.
8332 QWidget msWindowsOwnDC(&parent, Qt::MSWindowsOwnDC);
8333 msWindowsOwnDC.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8334 msWindowsOwnDC.show();
8335 QVERIFY(msWindowsOwnDC.testAttribute(Qt::WA_WState_Created));
8336 QVERIFY(msWindowsOwnDC.testAttribute(Qt::WA_NativeWindow));
8337 QVERIFY(msWindowsOwnDC.internalWinId());
8338
8339 { // Enforce a native window when calling QWidget::handle() (on X11) or QWidget::getDC() (on Windows).
8340 QWidget widget(&parent);
8341 widget.show();
8342 QVERIFY(widget.testAttribute(Qt::WA_WState_Created));
8343 QVERIFY(!widget.internalWinId());
8344
8345 widget.winId();
8346 QVERIFY(widget.internalWinId());
8347 }
8348
8349 if (m_platform == QStringLiteral("xcb")) {
8350 // Make sure we don't create native windows when setting Qt::WA_X11NetWmWindowType attributes
8351 // on alien widgets (see task 194231).
8352 QWidget dummy;
8353 dummy.resize(m_testWidgetSize);
8354 QVERIFY(dummy.winId());
8355 QWidget widget(&dummy);
8356 widget.setAttribute(Qt::WA_X11NetWmWindowTypeToolBar);
8357 QVERIFY(!widget.internalWinId());
8358 }
8359
8360 { // Make sure we create native ancestors when setting Qt::WA_PaintOnScreen before show().
8361 QWidget topLevel;
8362 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8363 topLevel.resize(m_testWidgetSize);
8364 QWidget child(&topLevel);
8365 QWidget grandChild(&child);
8366 PaintOnScreenWidget greatGrandChild(&grandChild);
8367
8368 greatGrandChild.setAttribute(Qt::WA_PaintOnScreen);
8369 QVERIFY(!child.internalWinId());
8370 QVERIFY(!grandChild.internalWinId());
8371 QVERIFY(!greatGrandChild.internalWinId());
8372
8373 topLevel.show();
8374 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8375 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8376 QVERIFY(child.internalWinId());
8377 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8378 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8379 QVERIFY(grandChild.internalWinId());
8380 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8381 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8382 QVERIFY(greatGrandChild.internalWinId());
8383 }
8384
8385 { // Ensure that widgets reparented into Qt::WA_PaintOnScreen widgets become native.
8386 QWidget topLevel;
8387 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8388 topLevel.resize(m_testWidgetSize);
8389 QWidget *widget = new PaintOnScreenWidget(&topLevel);
8390 widget->setAttribute(Qt::WA_PaintOnScreen);
8391 QWidget *child = new QWidget;
8392 QWidget *dummy = new QWidget(child);
8393 QWidget *grandChild = new QWidget(child);
8394 QWidget *dummy2 = new QWidget(grandChild);
8395
8396 child->setParent(widget);
8397
8398 QVERIFY(!topLevel.internalWinId());
8399 QVERIFY(!child->internalWinId());
8400 QVERIFY(!dummy->internalWinId());
8401 QVERIFY(!grandChild->internalWinId());
8402 QVERIFY(!dummy2->internalWinId());
8403
8404 topLevel.show();
8405 QVERIFY(topLevel.internalWinId());
8406 QVERIFY(widget->testAttribute(Qt::WA_NativeWindow));
8407 QVERIFY(child->internalWinId());
8408 QVERIFY(child->testAttribute(Qt::WA_NativeWindow));
8409 QVERIFY(!child->testAttribute(Qt::WA_PaintOnScreen));
8410 QVERIFY(!dummy->internalWinId());
8411 QVERIFY(!dummy->testAttribute(Qt::WA_NativeWindow));
8412 QVERIFY(!grandChild->internalWinId());
8413 QVERIFY(!grandChild->testAttribute(Qt::WA_NativeWindow));
8414 QVERIFY(!dummy2->internalWinId());
8415 QVERIFY(!dummy2->testAttribute(Qt::WA_NativeWindow));
8416 }
8417
8418 { // Ensure that ancestors of a Qt::WA_PaintOnScreen widget stay native
8419 // if they are re-created (typically in QWidgetPrivate::setParent_sys) (task 210822).
8420 QWidget window;
8421 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8422 window.resize(m_testWidgetSize);
8423 QWidget child(&window);
8424
8425 QWidget grandChild;
8426 grandChild.setWindowTitle("This causes the widget to be created");
8427
8428 PaintOnScreenWidget paintOnScreenWidget;
8429 paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen);
8430 paintOnScreenWidget.setParent(&grandChild);
8431
8432 grandChild.setParent(&child);
8433
8434 window.show();
8435
8436 QVERIFY(window.internalWinId());
8437 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8438 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8439 QVERIFY(child.internalWinId());
8440 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8441 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8442 QVERIFY(child.testAttribute(Qt::WA_NativeWindow));
8443 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8444 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8445 QVERIFY(grandChild.internalWinId());
8446 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8447 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8448 QVERIFY(grandChild.testAttribute(Qt::WA_NativeWindow));
8449 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8450 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8451 QVERIFY(paintOnScreenWidget.internalWinId());
8452 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
8453 QEXPECT_FAIL("", "QTBUG-26424", Continue);
8454 QVERIFY(paintOnScreenWidget.testAttribute(Qt::WA_NativeWindow));
8455 }
8456
8457 { // Ensure that all siblings are native unless Qt::AA_DontCreateNativeWidgetSiblings is set.
8458 qApp->setAttribute(attribute: Qt::AA_DontCreateNativeWidgetSiblings, on: false);
8459 QWidget mainWindow;
8460 mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8461 QWidget *toolBar = new QWidget(&mainWindow);
8462 QWidget *dockWidget = new QWidget(&mainWindow);
8463 QWidget *centralWidget = new QWidget(&mainWindow);
8464 centralWidget->setMinimumSize(m_testWidgetSize);
8465
8466 QWidget *button = new QWidget(centralWidget);
8467 QWidget *mdiArea = new QWidget(centralWidget);
8468
8469 QWidget *horizontalScroll = new QWidget(mdiArea);
8470 QWidget *verticalScroll = new QWidget(mdiArea);
8471 QWidget *viewport = new QWidget(mdiArea);
8472
8473 viewport->setAttribute(Qt::WA_NativeWindow);
8474 mainWindow.show();
8475
8476 // Ensure that the viewport and its siblings are native:
8477 QVERIFY(verticalScroll->testAttribute(Qt::WA_NativeWindow));
8478 QVERIFY(verticalScroll->testAttribute(Qt::WA_NativeWindow));
8479 QVERIFY(horizontalScroll->testAttribute(Qt::WA_NativeWindow));
8480
8481 // Ensure that the mdi area and its siblings are native:
8482 QVERIFY(mdiArea->testAttribute(Qt::WA_NativeWindow));
8483 QVERIFY(button->testAttribute(Qt::WA_NativeWindow));
8484
8485 // Ensure that the central widget and its siblings are native:
8486 QVERIFY(centralWidget->testAttribute(Qt::WA_NativeWindow));
8487 QVERIFY(dockWidget->testAttribute(Qt::WA_NativeWindow));
8488 QVERIFY(toolBar->testAttribute(Qt::WA_NativeWindow));
8489 }
8490}
8491
8492using WidgetAttributes = QVector<Qt::WidgetAttribute>;
8493
8494void tst_QWidget::nativeWindowPosition_data()
8495{
8496 QTest::addColumn<WidgetAttributes>(name: "attributes");
8497
8498 QTest::newRow(dataTag: "non-native all the way")
8499 << WidgetAttributes{};
8500 QTest::newRow(dataTag: "native all the way")
8501 << WidgetAttributes{ Qt::WA_NativeWindow };
8502 QTest::newRow(dataTag: "native with non-native ancestor")
8503 << WidgetAttributes{ Qt::WA_NativeWindow, Qt::WA_DontCreateNativeAncestors };
8504}
8505
8506void tst_QWidget::nativeWindowPosition()
8507{
8508 QWidget topLevel;
8509 QWidget child(&topLevel);
8510 child.move(ax: 5, ay: 5);
8511
8512 QWidget grandChild(&child);
8513 grandChild.move(ax: 10, ay: 10);
8514
8515 QFETCH(WidgetAttributes, attributes);
8516 for (auto attribute : attributes)
8517 grandChild.setAttribute(attribute);
8518
8519 topLevel.show();
8520 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
8521
8522 QCOMPARE(child.pos(), QPoint(5, 5));
8523 QCOMPARE(grandChild.pos(), QPoint(10, 10));
8524}
8525
8526class ASWidget : public QWidget
8527{
8528public:
8529 ASWidget(QSize sizeHint, QSizePolicy sizePolicy, bool layout, bool hfwLayout, QWidget *parent = nullptr)
8530 : QWidget(parent), mySizeHint(sizeHint)
8531 {
8532 setObjectName(QStringLiteral("ASWidget"));
8533 setWindowTitle(objectName());
8534 setSizePolicy(sizePolicy);
8535 if (layout) {
8536 QSizePolicy sp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
8537 sp.setHeightForWidth(hfwLayout);
8538
8539 QVBoxLayout *vbox = new QVBoxLayout;
8540 vbox->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
8541 vbox->addWidget(new ASWidget(sizeHint + QSize(30, 20), sp, false, false));
8542 setLayout(vbox);
8543 }
8544 }
8545
8546 QSize sizeHint() const override
8547 {
8548 if (layout())
8549 return layout()->totalSizeHint();
8550 return mySizeHint;
8551 }
8552 int heightForWidth(int width) const override
8553 {
8554 return sizePolicy().hasHeightForWidth() ? width * 2 : -1;
8555 }
8556
8557 QSize mySizeHint;
8558};
8559
8560void tst_QWidget::adjustSize_data()
8561{
8562 const int MagicW = 200;
8563 const int MagicH = 100;
8564
8565 QTest::addColumn<QSize>(name: "sizeHint");
8566 QTest::addColumn<int>(name: "hPolicy");
8567 QTest::addColumn<int>(name: "vPolicy");
8568 QTest::addColumn<bool>(name: "hfwSP");
8569 QTest::addColumn<bool>(name: "layout");
8570 QTest::addColumn<bool>(name: "hfwLayout");
8571 QTest::addColumn<bool>(name: "haveParent");
8572 QTest::addColumn<QSize>(name: "expectedSize");
8573
8574 QTest::newRow(dataTag: "1") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8575 << false << false << false << false << QSize(5, qMax(a: 6, b: MagicH));
8576 QTest::newRow(dataTag: "2") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8577 << true << false << false << false << QSize(5, qMax(a: 10, b: MagicH));
8578 QTest::newRow(dataTag: "3") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8579 << false << true << false << false << QSize(35, 26);
8580 QTest::newRow(dataTag: "4") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8581 << false << true << true << false << QSize(35, 70);
8582 QTest::newRow(dataTag: "5") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8583 << false << false << false << false << QSize(100000, 100000);
8584 QTest::newRow(dataTag: "6") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8585 << true << false << false << false << QSize(100000, 100000);
8586 QTest::newRow(dataTag: "7") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8587 << false << true << false << false << QSize(100000, 100000);
8588 QTest::newRow(dataTag: "8") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8589 << false << true << true << false << QSize(100000, 100000);
8590 QTest::newRow(dataTag: "9") << QSize(5, 6) << int(QSizePolicy::Expanding) << int(QSizePolicy::Minimum)
8591 << true << false << false << false << QSize(qMax(a: 5, b: MagicW), 10);
8592
8593 QTest::newRow(dataTag: "1c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8594 << false << false << false << true << QSize(5, 6);
8595 QTest::newRow(dataTag: "2c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8596 << true << false << false << true << QSize(5, 6 /* or 10 would be OK too, since hfw contradicts sizeHint() */);
8597 QTest::newRow(dataTag: "3c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8598 << false << true << false << true << QSize(35, 26);
8599 QTest::newRow(dataTag: "4c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8600 << false << true << true << true << QSize(35, 70);
8601 QTest::newRow(dataTag: "5c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8602 << false << false << false << true << QSize(40001, 30001);
8603 QTest::newRow(dataTag: "6c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8604 << true << false << false << true << QSize(40001, 30001 /* or 80002 would be OK too, since hfw contradicts sizeHint() */);
8605 QTest::newRow(dataTag: "7c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8606 << false << true << false << true << QSize(40001 + 30, 30001 + 20);
8607 QTest::newRow(dataTag: "8c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding)
8608 << false << true << true << true << QSize(40001 + 30, 80002 + 60);
8609 QTest::newRow(dataTag: "9c") << QSize(5, 6) << int(QSizePolicy::Expanding) << int(QSizePolicy::Minimum)
8610 << true << false << false << true << QSize(5, 6);
8611}
8612
8613void tst_QWidget::adjustSize()
8614{
8615 QFETCH(QSize, sizeHint);
8616 QFETCH(int, hPolicy);
8617 QFETCH(int, vPolicy);
8618 QFETCH(bool, hfwSP);
8619 QFETCH(bool, layout);
8620 QFETCH(bool, hfwLayout);
8621 QFETCH(bool, haveParent);
8622 QFETCH(QSize, expectedSize);
8623
8624 QScopedPointer<QWidget> parent(new QWidget);
8625
8626 QSizePolicy sp = QSizePolicy(QSizePolicy::Policy(hPolicy), QSizePolicy::Policy(vPolicy));
8627 sp.setHeightForWidth(hfwSP);
8628
8629 QWidget *child = new ASWidget(sizeHint, sp, layout, hfwLayout, haveParent ? parent.data() : nullptr);
8630 child->resize(w: 123, h: 456);
8631 child->adjustSize();
8632 if (expectedSize == QSize(100000, 100000)) {
8633 QVERIFY2(child->size().width() < sizeHint.width(),
8634 msgComparisonFailed(child->size().width(), "<", sizeHint.width()));
8635 QVERIFY2(child->size().height() < sizeHint.height(),
8636 msgComparisonFailed(child->size().height(), "<", sizeHint.height()));
8637 } else {
8638 QCOMPARE(child->size(), expectedSize);
8639 }
8640 if (!haveParent)
8641 delete child;
8642}
8643
8644class TestLayout : public QVBoxLayout
8645{
8646 Q_OBJECT
8647public:
8648 using QVBoxLayout::QVBoxLayout;
8649
8650 void invalidate() override
8651 {
8652 invalidated = true;
8653 }
8654
8655 bool invalidated = false;
8656};
8657
8658void tst_QWidget::updateGeometry_data()
8659{
8660 QTest::addColumn<QSize>(name: "minSize");
8661 QTest::addColumn<bool>(name: "shouldInvalidate");
8662 QTest::addColumn<QSize>(name: "maxSize");
8663 QTest::addColumn<bool>(name: "shouldInvalidate2");
8664 QTest::addColumn<QSizePolicy::Policy>(name: "verticalSizePolicy");
8665 QTest::addColumn<bool>(name: "shouldInvalidate3");
8666 QTest::addColumn<bool>(name: "setVisible");
8667 QTest::addColumn<bool>(name: "shouldInvalidate4");
8668
8669 QTest::newRow(dataTag: "setMinimumSize")
8670 << QSize(100, 100) << true
8671 << QSize() << false
8672 << QSizePolicy::Preferred << false
8673 << true << false;
8674 QTest::newRow(dataTag: "setMaximumSize")
8675 << QSize() << false
8676 << QSize(100, 100) << true
8677 << QSizePolicy::Preferred << false
8678 << true << false;
8679 QTest::newRow(dataTag: "setMinimumSize, then maximumSize to a different size")
8680 << QSize(100, 100) << true
8681 << QSize(300, 300) << true
8682 << QSizePolicy::Preferred << false
8683 << true << false;
8684 QTest::newRow(dataTag: "setMinimumSize, then maximumSize to the same size")
8685 << QSize(100, 100) << true
8686 << QSize(100, 100) << true
8687 << QSizePolicy::Preferred << false
8688 << true << false;
8689 QTest::newRow(dataTag: "setMinimumSize, then maximumSize to the same size and then hide it")
8690 << QSize(100, 100) << true
8691 << QSize(100, 100) << true
8692 << QSizePolicy::Preferred << false
8693 << false << true;
8694 QTest::newRow(dataTag: "Change sizePolicy")
8695 << QSize() << false
8696 << QSize() << false
8697 << QSizePolicy::Minimum << true
8698 << true << false;
8699
8700}
8701
8702void tst_QWidget::updateGeometry()
8703{
8704 QFETCH(QSize, minSize);
8705 QFETCH(bool, shouldInvalidate);
8706 QFETCH(QSize, maxSize);
8707 QFETCH(bool, shouldInvalidate2);
8708 QFETCH(QSizePolicy::Policy, verticalSizePolicy);
8709 QFETCH(bool, shouldInvalidate3);
8710 QFETCH(bool, setVisible);
8711 QFETCH(bool, shouldInvalidate4);
8712 QWidget parent;
8713 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
8714 + QLatin1String(QTest::currentDataTag()));
8715 parent.resize(w: 200, h: 200);
8716 TestLayout *lout = new TestLayout();
8717 parent.setLayout(lout);
8718 QWidget *child = new QWidget(&parent);
8719 lout->addWidget(child);
8720 parent.show();
8721 QApplication::processEvents();
8722
8723 lout->invalidated = false;
8724 if (minSize.isValid())
8725 child->setMinimumSize(minSize);
8726 QCOMPARE(lout->invalidated, shouldInvalidate);
8727
8728 lout->invalidated = false;
8729 if (maxSize.isValid())
8730 child->setMaximumSize(maxSize);
8731 QCOMPARE(lout->invalidated, shouldInvalidate2);
8732
8733 lout->invalidated = false;
8734 child->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, verticalSizePolicy));
8735 if (shouldInvalidate3)
8736 QCOMPARE(lout->invalidated, true);
8737
8738 lout->invalidated = false;
8739 if (!setVisible)
8740 child->setVisible(false);
8741 QCOMPARE(lout->invalidated, shouldInvalidate4);
8742}
8743
8744void tst_QWidget::sendUpdateRequestImmediately()
8745{
8746 UpdateWidget updateWidget;
8747 updateWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8748 updateWidget.show();
8749
8750 QVERIFY(QTest::qWaitForWindowExposed(&updateWidget));
8751
8752 QCoreApplication::processEvents();
8753 updateWidget.reset();
8754
8755 QCOMPARE(updateWidget.numUpdateRequestEvents, 0);
8756 updateWidget.repaint();
8757 QCOMPARE(updateWidget.numUpdateRequestEvents, 1);
8758}
8759
8760void tst_QWidget::doubleRepaint()
8761{
8762#ifdef Q_OS_MACOS
8763 QSKIP("QTBUG-52974");
8764#endif
8765
8766#if defined(Q_OS_MACOS)
8767 if (!macHasAccessToWindowsServer())
8768 QSKIP("Not having window server access causes the wrong number of repaints to be issues");
8769#endif
8770 UpdateWidget widget;
8771 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8772 centerOnScreen(w: &widget);
8773 widget.setFocusPolicy(Qt::StrongFocus);
8774 // Filter out activation change and focus events to avoid update() calls in QWidget.
8775 widget.updateOnActivationChangeAndFocusIn = false;
8776
8777 // Show: 1 repaint
8778 int expectedRepaints = 1;
8779 widget.show();
8780 QVERIFY(QTest::qWaitForWindowExposed(&widget));
8781 QTRY_COMPARE(widget.numPaintEvents, expectedRepaints);
8782 widget.numPaintEvents = 0;
8783
8784 // Minmize: Should not trigger a repaint.
8785 widget.showMinimized();
8786 QTest::qWait(ms: 10);
8787#if defined(Q_OS_QNX)
8788 QEXPECT_FAIL("", "Platform does not support showMinimized()", Continue);
8789#endif
8790 if (m_platform == QStringLiteral("winrt"))
8791 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
8792 QCOMPARE(widget.numPaintEvents, 0);
8793 widget.numPaintEvents = 0;
8794
8795 // Restore: Should not trigger a repaint.
8796 widget.showNormal();
8797 QVERIFY(QTest::qWaitForWindowExposed(&widget));
8798 QTest::qWait(ms: 10);
8799 QCOMPARE(widget.numPaintEvents, 0);
8800}
8801
8802void tst_QWidget::resizeInPaintEvent()
8803{
8804 QWidget window;
8805 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8806 UpdateWidget widget(&window);
8807 window.resize(w: 200, h: 200);
8808 window.show();
8809 QApplication::setActiveWindow(&window);
8810 QVERIFY(QTest::qWaitForWindowExposed(&window));
8811 QTRY_VERIFY(widget.numPaintEvents > 0);
8812
8813 widget.reset();
8814 QCOMPARE(widget.numPaintEvents, 0);
8815
8816 widget.resizeInPaintEvent = true;
8817 // This will call resize in the paintEvent, which in turn will call
8818 // invalidateBackingStore() and a new update request should be posted.
8819 // the resize triggers another update.
8820 widget.update();
8821 QTRY_COMPARE(widget.numPaintEvents, 2);
8822}
8823
8824void tst_QWidget::opaqueChildren()
8825{
8826 QWidget widget;
8827 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8828 widget.resize(w: 200, h: 200);
8829
8830 QWidget child(&widget);
8831 child.setGeometry(ax: -700, ay: -700, aw: 200, ah: 200);
8832
8833 QWidget grandChild(&child);
8834 grandChild.resize(w: 200, h: 200);
8835
8836 QWidget greatGrandChild(&grandChild);
8837 greatGrandChild.setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
8838 greatGrandChild.setPalette(Qt::red);
8839 greatGrandChild.setAutoFillBackground(true); // Opaque child widget.
8840
8841 widget.show();
8842 QVERIFY(QTest::qWaitForWindowExposed(&widget));
8843
8844 // Child, grandChild and greatGrandChild are outside the ancestor clip.
8845 QRegion expectedOpaqueRegion(50, 50, 150, 150);
8846 QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion);
8847
8848 // Now they are all inside the ancestor clip.
8849 child.setGeometry(ax: 50, ay: 50, aw: 150, ah: 150);
8850 QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion);
8851
8852 // Set mask on greatGrandChild.
8853 const QRegion mask(10, 10, 50, 50);
8854 greatGrandChild.setMask(mask);
8855 expectedOpaqueRegion &= mask.translated(dx: 50, dy: 50);
8856 QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion);
8857
8858 // Make greatGrandChild "transparent".
8859 greatGrandChild.setAutoFillBackground(false);
8860 QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), QRegion());
8861}
8862
8863
8864class MaskSetWidget : public QWidget
8865{
8866 Q_OBJECT
8867public:
8868 using QWidget::QWidget;
8869
8870 void paintEvent(QPaintEvent *event) override
8871 {
8872 QPainter p(this);
8873
8874 paintedRegion += event->region();
8875 for (const QRect &r : event->region())
8876 p.fillRect(r, c: Qt::red);
8877 }
8878
8879 void resizeEvent(QResizeEvent *) override
8880 {
8881 setMask(QRegion(QRect(0, 0, width(), 10).normalized()));
8882 }
8883
8884 QRegion paintedRegion;
8885
8886public slots:
8887 void resizeDown() { setGeometry(QRect(0, 50, 50, 50)); }
8888 void resizeUp() { setGeometry(QRect(0, 50, 150, 50)); }
8889};
8890
8891void tst_QWidget::setMaskInResizeEvent()
8892{
8893 UpdateWidget w;
8894 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8895 w.reset();
8896 w.resize(w: 200, h: 200);
8897 centerOnScreen(w: &w);
8898 w.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
8899 w.raise();
8900
8901 MaskSetWidget testWidget(&w);
8902 testWidget.setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
8903 testWidget.setMask(QRegion(QRect(0,0,100,10)));
8904 testWidget.show();
8905 w.show();
8906 QVERIFY(QTest::qWaitForWindowExposed(&w));
8907 QTRY_VERIFY(w.numPaintEvents > 0);
8908
8909 w.reset();
8910 testWidget.paintedRegion = QRegion();
8911 QTimer::singleShot(msec: 0, receiver: &testWidget, SLOT(resizeDown()));
8912 QTest::qWait(ms: 100);
8913
8914 QRegion expectedParentUpdate(0, 0, 100, 10); // Old testWidget area.
8915 expectedParentUpdate += testWidget.geometry(); // New testWidget area.
8916 if (m_platform == QStringLiteral("winrt"))
8917 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
8918 QTRY_COMPARE(w.paintedRegion, expectedParentUpdate);
8919 QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask());
8920
8921 testWidget.paintedRegion = QRegion();
8922 // Now resize the widget again, but in the oposite direction
8923 QTimer::singleShot(msec: 0, receiver: &testWidget, SLOT(resizeUp()));
8924 QTest::qWait(ms: 100);
8925
8926 QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask());
8927}
8928
8929class MoveInResizeWidget : public QWidget
8930{
8931 Q_OBJECT
8932public:
8933 explicit MoveInResizeWidget(QWidget *p = nullptr)
8934 : QWidget(p)
8935 {
8936 setWindowFlags(Qt::FramelessWindowHint);
8937 }
8938
8939 void resizeEvent(QResizeEvent *) override
8940 {
8941 move(QPoint(100,100));
8942
8943 static bool firstTime = true;
8944 if (firstTime)
8945 QTimer::singleShot(interval: 250, receiver: this, slot: &MoveInResizeWidget::resizeMe);
8946
8947 firstTime = false;
8948 }
8949
8950public slots:
8951 void resizeMe() {
8952 resize(w: 100, h: 100);
8953 }
8954};
8955
8956void tst_QWidget::moveInResizeEvent()
8957{
8958 MoveInResizeWidget testWidget;
8959 testWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8960 testWidget.setGeometry(ax: 50, ay: 50, aw: 200, ah: 200);
8961 testWidget.show();
8962 QVERIFY(QTest::qWaitForWindowExposed(&testWidget));
8963
8964 QRect expectedGeometry(100,100, 100, 100);
8965 QTRY_COMPARE(testWidget.geometry(), expectedGeometry);
8966}
8967
8968#ifdef QT_BUILD_INTERNAL
8969void tst_QWidget::immediateRepaintAfterInvalidateBackingStore()
8970{
8971 if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows"))
8972 QSKIP("We don't support immediate repaint right after show on other platforms.");
8973
8974 QScopedPointer<UpdateWidget> widget(new UpdateWidget);
8975 widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8976 centerOnScreen(w: widget.data());
8977 widget->show();
8978 QVERIFY(QTest::qWaitForWindowExposed(widget.data()));
8979
8980 widget->numPaintEvents = 0;
8981
8982 // Marks the area covered by the widget as dirty in the backing store and
8983 // posts an UpdateRequest event.
8984 qt_widget_private(widget: widget.data())->invalidateBackingStore(widget->rect());
8985 QCOMPARE(widget->numPaintEvents, 0);
8986
8987 // The entire widget is already dirty, but this time we want to update immediately
8988 // by calling repaint(), and thus we have to repaint the widget and not wait for
8989 // the UpdateRequest to be sent when we get back to the event loop.
8990 widget->update();
8991 QTRY_COMPARE(widget->numPaintEvents, 1);
8992}
8993#endif
8994
8995void tst_QWidget::effectiveWinId()
8996{
8997 QWidget parent;
8998 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
8999 parent.resize(w: 200, h: 200);
9000 QWidget child(&parent);
9001
9002 // Shouldn't crash.
9003 QVERIFY(!parent.effectiveWinId());
9004 QVERIFY(!child.effectiveWinId());
9005
9006 parent.show();
9007
9008 QVERIFY(parent.effectiveWinId());
9009 QVERIFY(child.effectiveWinId());
9010}
9011
9012void tst_QWidget::effectiveWinId2()
9013{
9014 QWidget parent;
9015
9016 class MyWidget : public QWidget
9017 {
9018 bool event(QEvent *e) override
9019 {
9020 if (e->type() == QEvent::WinIdChange) {
9021 // Shouldn't crash.
9022 effectiveWinId();
9023 }
9024
9025 return QWidget::event(event: e);
9026 }
9027 };
9028
9029 MyWidget child;
9030 child.setParent(&parent);
9031 parent.show();
9032
9033 child.setParent(nullptr);
9034 child.setParent(&parent);
9035}
9036
9037class CustomWidget : public QWidget
9038{
9039public:
9040 mutable int metricCallCount = 0;
9041
9042 using QWidget::QWidget;
9043
9044 int metric(PaintDeviceMetric metric) const override
9045 {
9046 ++metricCallCount;
9047 return QWidget::metric(metric);
9048 }
9049};
9050
9051void tst_QWidget::customDpi()
9052{
9053 QScopedPointer<QWidget> topLevel(new QWidget);
9054 CustomWidget *custom = new CustomWidget(topLevel.data());
9055 QWidget *child = new QWidget(custom);
9056
9057 custom->metricCallCount = 0;
9058 topLevel->logicalDpiX();
9059 QCOMPARE(custom->metricCallCount, 0);
9060 custom->logicalDpiX();
9061 QCOMPARE(custom->metricCallCount, 1);
9062 child->logicalDpiX();
9063 QCOMPARE(custom->metricCallCount, 1);
9064}
9065
9066void tst_QWidget::customDpiProperty()
9067{
9068 QScopedPointer<QWidget> topLevel(new QWidget);
9069 QWidget *middle = new CustomWidget(topLevel.data());
9070 QWidget *child = new QWidget(middle);
9071
9072 const int initialDpiX = topLevel->logicalDpiX();
9073 const int initialDpiY = topLevel->logicalDpiY();
9074
9075 middle->setProperty(name: "_q_customDpiX", value: 300);
9076 middle->setProperty(name: "_q_customDpiY", value: 400);
9077
9078 QCOMPARE(topLevel->logicalDpiX(), initialDpiX);
9079 QCOMPARE(topLevel->logicalDpiY(), initialDpiY);
9080
9081 QCOMPARE(middle->logicalDpiX(), 300);
9082 QCOMPARE(middle->logicalDpiY(), 400);
9083
9084 QCOMPARE(child->logicalDpiX(), 300);
9085 QCOMPARE(child->logicalDpiY(), 400);
9086
9087 middle->setProperty(name: "_q_customDpiX", value: QVariant());
9088 middle->setProperty(name: "_q_customDpiY", value: QVariant());
9089
9090 QCOMPARE(topLevel->logicalDpiX(), initialDpiX);
9091 QCOMPARE(topLevel->logicalDpiY(), initialDpiY);
9092
9093 QCOMPARE(middle->logicalDpiX(), initialDpiX);
9094 QCOMPARE(middle->logicalDpiY(), initialDpiY);
9095
9096 QCOMPARE(child->logicalDpiX(), initialDpiX);
9097 QCOMPARE(child->logicalDpiY(), initialDpiY);
9098}
9099
9100void tst_QWidget::quitOnCloseAttribute()
9101{
9102 QWidget w;
9103 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true);
9104 w.setAttribute(Qt::WA_QuitOnClose, on: false);
9105 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9106
9107 w.setAttribute(Qt::WA_QuitOnClose);
9108 w.setWindowFlags(Qt::Tool);
9109 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9110
9111 w.setAttribute(Qt::WA_QuitOnClose);
9112 w.setWindowFlags(Qt::Popup);
9113 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9114
9115 w.setAttribute(Qt::WA_QuitOnClose);
9116 w.setWindowFlags(Qt::ToolTip);
9117 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9118
9119 w.setAttribute(Qt::WA_QuitOnClose);
9120 w.setWindowFlags(Qt::SplashScreen);
9121 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9122
9123 w.setAttribute(Qt::WA_QuitOnClose);
9124 w.setWindowFlags(Qt::SubWindow);
9125 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9126
9127 w.setAttribute(Qt::WA_QuitOnClose);
9128 w.setWindowFlags(Qt::Dialog);
9129 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true);
9130 w.show();
9131 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true);
9132 w.setWindowFlags(Qt::Tool);
9133 QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false);
9134}
9135
9136void tst_QWidget::moveRect()
9137{
9138 QWidget widget;
9139 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9140 widget.resize(w: 200, h: 200);
9141 widget.setUpdatesEnabled(false);
9142 QWidget child(&widget);
9143 child.setUpdatesEnabled(false);
9144 child.setAttribute(Qt::WA_OpaquePaintEvent);
9145 widget.show();
9146 QVERIFY(QTest::qWaitForWindowExposed(&widget));
9147 child.move(ax: 10, ay: 10); // Don't crash.
9148}
9149
9150#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
9151class GDIWidget : public QDialog
9152{
9153 Q_OBJECT
9154public:
9155 GDIWidget() {
9156 setAttribute(Qt::WA_PaintOnScreen);
9157 timer.setSingleShot(true);
9158 timer.setInterval(0);
9159 }
9160 QPaintEngine *paintEngine() const override { return nullptr; }
9161
9162 void paintEvent(QPaintEvent *) override
9163 {
9164 QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
9165 const auto hdc = reinterpret_cast<HDC>(ni->nativeResourceForWindow(QByteArrayLiteral("getDC"), windowHandle()));
9166 if (hdc) {
9167 const HBRUSH brush = CreateSolidBrush(RGB(255, 0, 0));
9168 SelectObject(hdc, brush);
9169 Rectangle(hdc, 0, 0, 10, 10);
9170 DeleteObject(brush);
9171 ni->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), windowHandle());
9172 } else {
9173 qWarning("%s: Unable to obtain native DC.", Q_FUNC_INFO);
9174 }
9175 if (!timer.isActive()) {
9176 connect(&timer, &QTimer::timeout, this,
9177 hdc ? &GDIWidget::slotTimer : &QDialog::reject);
9178 timer.start();
9179 }
9180 }
9181
9182 QSize sizeHint() const override { return {400, 300}; };
9183
9184private slots:
9185 void slotTimer() {
9186 QScreen *screen = windowHandle()->screen();
9187 const QImage im = screen->grabWindow(internalWinId(), 0, 0, -1, -1).toImage();
9188 color = im.pixel(1, 1);
9189 accept();
9190 }
9191
9192public:
9193 QColor color;
9194 QTimer timer;
9195};
9196
9197void tst_QWidget::gdiPainting()
9198{
9199 GDIWidget w;
9200 w.exec();
9201
9202 QCOMPARE(w.color, QColor(255, 0, 0));
9203
9204}
9205
9206void tst_QWidget::paintOnScreenPossible()
9207{
9208 QWidget w1;
9209 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9210 w1.setAttribute(Qt::WA_PaintOnScreen);
9211 QVERIFY(!w1.testAttribute(Qt::WA_PaintOnScreen));
9212
9213 GDIWidget w2;
9214 w2.setAttribute(Qt::WA_PaintOnScreen);
9215 QVERIFY(w2.testAttribute(Qt::WA_PaintOnScreen));
9216}
9217#endif // Q_OS_WIN && !Q_OS_WINRT
9218
9219void tst_QWidget::reparentStaticWidget()
9220{
9221 QWidget window1;
9222 window1.setWindowTitle(QStringLiteral("window1 ") + __FUNCTION__);
9223 window1.resize(m_testWidgetSize);
9224 window1.move(m_availableTopLeft + QPoint(100, 100));
9225
9226 QWidget *child = new QWidget(&window1);
9227 child->setPalette(Qt::red);
9228 child->setAutoFillBackground(true);
9229 child->setAttribute(Qt::WA_StaticContents);
9230 child->resize(w: window1.width() - 40, h: window1.height() - 40);
9231 child->setWindowTitle(QStringLiteral("child ") + __FUNCTION__);
9232
9233 QWidget *grandChild = new QWidget(child);
9234 grandChild->setPalette(Qt::blue);
9235 grandChild->setAutoFillBackground(true);
9236 grandChild->resize(w: 50, h: 50);
9237 grandChild->setAttribute(Qt::WA_StaticContents);
9238 window1.show();
9239 QVERIFY(QTest::qWaitForWindowExposed(&window1));
9240
9241 QWidget window2;
9242 window2.setWindowTitle(QStringLiteral("window2 ") + __FUNCTION__);
9243 window2.resize(m_testWidgetSize);
9244 window2.move(window1.geometry().topRight() + QPoint(100, 0));
9245 window2.show();
9246 QVERIFY(QTest::qWaitForWindowExposed(&window2));
9247
9248 // Reparent into another top-level.
9249 child->setParent(&window2);
9250 child->show();
9251
9252 // Please don't crash.
9253 window1.resize(window1.size() + QSize(2, 2));
9254 QTest::qWait(ms: 20);
9255
9256 // Make sure we move all static children even though
9257 // the reparented widget itself is non-static.
9258 child->setAttribute(Qt::WA_StaticContents, on: false);
9259 child->setParent(&window1);
9260 child->show();
9261
9262 // Please don't crash.
9263 window2.resize(window2.size() + QSize(2, 2));
9264 QTest::qWait(ms: 20);
9265
9266 child->setParent(nullptr);
9267 child->show();
9268 QTest::qWait(ms: 20);
9269
9270 // Please don't crash.
9271 child->resize(child->size() + QSize(2, 2));
9272 window2.resize(window2.size() + QSize(2, 2));
9273 QTest::qWait(ms: 20);
9274
9275 QWidget *siblingOfGrandChild = new QWidget(child);
9276 siblingOfGrandChild->show();
9277 QTest::qWait(ms: 20);
9278
9279 // Nothing should happen when reparenting within the same top-level.
9280 grandChild->setParent(siblingOfGrandChild);
9281 grandChild->show();
9282 QTest::qWait(ms: 20);
9283
9284 QWidget paintOnScreen;
9285 paintOnScreen.setWindowTitle(QStringLiteral("paintOnScreen ") + __FUNCTION__);
9286 paintOnScreen.resize(m_testWidgetSize);
9287 paintOnScreen.move(window1.geometry().bottomLeft() + QPoint(0, 50));
9288
9289 paintOnScreen.setAttribute(Qt::WA_PaintOnScreen);
9290 paintOnScreen.show();
9291 QVERIFY(QTest::qWaitForWindowExposed(&paintOnScreen));
9292 QTest::qWait(ms: 20);
9293
9294 child->setParent(&paintOnScreen);
9295 child->show();
9296 QTest::qWait(ms: 20);
9297
9298 // Please don't crash.
9299 paintOnScreen.resize(paintOnScreen.size() + QSize(2, 2));
9300 QTest::qWait(ms: 20);
9301
9302}
9303
9304void tst_QWidget::QTBUG6883_reparentStaticWidget2()
9305{
9306 QMainWindow mw;
9307 mw.setWindowTitle(QStringLiteral("MainWindow ") + __FUNCTION__);
9308 mw.move(m_availableTopLeft + QPoint(100, 100));
9309
9310 QDockWidget *one = new QDockWidget(QStringLiteral("Dock ") + __FUNCTION__, &mw);
9311 mw.addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: one , orientation: Qt::Vertical);
9312
9313 QWidget *child = new QWidget();
9314 child->setPalette(Qt::red);
9315 child->setAutoFillBackground(true);
9316 child->setAttribute(Qt::WA_StaticContents);
9317 child->resize(m_testWidgetSize);
9318 one->setWidget(child);
9319
9320 QToolBar *mainTools = mw.addToolBar(title: "Main Tools");
9321 QLineEdit *le = new QLineEdit;
9322 le->setMinimumWidth(m_testWidgetSize.width());
9323 mainTools->addWidget(widget: le);
9324
9325 mw.show();
9326 QVERIFY(QTest::qWaitForWindowExposed(&mw));
9327
9328 one->setFloating(true);
9329 QTest::qWait(ms: 20);
9330 //do not crash
9331}
9332
9333class ColorRedWidget : public QWidget
9334{
9335public:
9336 explicit ColorRedWidget(QWidget *parent = nullptr)
9337 : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip)
9338 {
9339 }
9340
9341 void paintEvent(QPaintEvent *) override
9342 {
9343 QPainter p(this);
9344 p.fillRect(r: rect(),c: Qt::red);
9345 }
9346};
9347
9348void tst_QWidget::translucentWidget()
9349{
9350 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
9351 QSKIP("Wayland: This fails. Figure out why.");
9352
9353 QPixmap pm(16,16);
9354 pm.fill(fillColor: Qt::red);
9355 ColorRedWidget label;
9356 label.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9357 label.setFixedSize(w: 16,h: 16);
9358 label.setAttribute(Qt::WA_TranslucentBackground);
9359 const QPoint labelPos = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
9360 label.move(labelPos);
9361 label.show();
9362 QVERIFY(QTest::qWaitForWindowExposed(&label));
9363
9364 QPixmap widgetSnapshot;
9365
9366#ifdef Q_OS_WIN
9367 QWidget *desktopWidget = QApplication::desktop()->screen(0);
9368 widgetSnapshot = grabWindow(desktopWidget->windowHandle(), labelPos.x(), labelPos.y(), label.width(), label.height());
9369#else
9370 widgetSnapshot = label.grab(rectangle: QRect(QPoint(0, 0), label.size()));
9371#endif
9372 const QImage actual = widgetSnapshot.toImage().convertToFormat(f: QImage::Format_RGB32);
9373 QImage expected = pm.toImage().scaled(s: label.devicePixelRatioF() * pm.size());
9374 expected.setDevicePixelRatio(label.devicePixelRatioF());
9375 if (m_platform == QStringLiteral("winrt"))
9376 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
9377 QCOMPARE(actual.size(),expected.size());
9378 QCOMPARE(actual,expected);
9379
9380 const QWindow *window = label.windowHandle();
9381 const QSurfaceFormat translucentFormat = window->requestedFormat();
9382 label.setAttribute(Qt::WA_TranslucentBackground, on: false);
9383 const QSurfaceFormat opaqueFormat = window->requestedFormat();
9384 QVERIFY(translucentFormat != opaqueFormat);
9385}
9386
9387class MaskResizeTestWidget : public QWidget
9388{
9389 Q_OBJECT
9390public:
9391 explicit MaskResizeTestWidget(QWidget* p = nullptr) : QWidget(p)
9392 {
9393 setMask(QRegion(QRect(0, 0, 100, 100).normalized()));
9394 }
9395
9396 void paintEvent(QPaintEvent* event) override
9397 {
9398 QPainter p(this);
9399
9400 paintedRegion += event->region();
9401 for (const QRect &r : event->region())
9402 p.fillRect(r, c: Qt::red);
9403 }
9404
9405 QRegion paintedRegion;
9406
9407public slots:
9408 void enlargeMask() {
9409 QRegion newMask(QRect(0, 0, 150, 150).normalized());
9410 setMask(newMask);
9411 }
9412
9413 void shrinkMask() {
9414 QRegion newMask(QRect(0, 0, 50, 50).normalized());
9415 setMask(newMask);
9416 }
9417
9418};
9419
9420void tst_QWidget::setClearAndResizeMask()
9421{
9422 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
9423 QSKIP("Wayland: This fails. Figure out why.");
9424
9425 UpdateWidget topLevel;
9426 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9427 topLevel.resize(w: 160, h: 160);
9428 centerOnScreen(w: &topLevel);
9429 topLevel.show();
9430 QApplication::setActiveWindow(&topLevel);
9431 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
9432 QTRY_VERIFY(topLevel.numPaintEvents > 0);
9433 topLevel.reset();
9434
9435 // Mask top-level widget
9436 const QRegion topLevelMask(0, 0, 100, 100, QRegion::Ellipse);
9437 topLevel.setMask(topLevelMask);
9438 QCOMPARE(topLevel.mask(), topLevelMask);
9439 // Ensure that the top-level doesn't get any update.
9440 // We don't control what's happening on platforms other than X11, Windows
9441 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows"))
9442 QCOMPARE(topLevel.numPaintEvents, 0);
9443
9444 topLevel.reset();
9445
9446 // Clear top-level mask
9447 topLevel.clearMask();
9448 QCOMPARE(topLevel.mask(), QRegion());
9449 QTest::qWait(ms: 10);
9450 QRegion outsideOldMask(topLevel.rect());
9451 outsideOldMask -= topLevelMask;
9452 // Ensure that the top-level gets an update for the area outside the old mask.
9453 // We don't control what's happening on platforms other than X11, Windows
9454 if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) {
9455 QTRY_VERIFY(topLevel.numPaintEvents > 0);
9456 QTRY_COMPARE(topLevel.paintedRegion, outsideOldMask);
9457 }
9458
9459 UpdateWidget child(&topLevel);
9460 child.setAutoFillBackground(true); // NB! Opaque child.
9461 child.setPalette(Qt::red);
9462 child.resize(w: 100, h: 100);
9463 child.show();
9464 QTest::qWait(ms: 10);
9465
9466 child.reset();
9467 topLevel.reset();
9468
9469 // Mask child widget with a mask that is smaller than the rect
9470 const QRegion childMask(0, 0, 50, 50);
9471 child.setMask(childMask);
9472 QTRY_COMPARE(child.mask(), childMask);
9473 // and ensure that the child widget doesn't get any update.
9474#ifdef Q_OS_MACOS
9475 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9476 if (child.internalWinId())
9477 QCOMPARE(child.numPaintEvents, 1);
9478 else
9479#endif
9480 QCOMPARE(child.numPaintEvents, 0);
9481 // and the parent widget gets an update for the newly exposed area.
9482 QTRY_COMPARE(topLevel.numPaintEvents, 1);
9483 QRegion expectedParentExpose(child.rect());
9484 expectedParentExpose -= childMask;
9485 QCOMPARE(topLevel.paintedRegion, expectedParentExpose);
9486
9487 child.reset();
9488 topLevel.reset();
9489
9490 // Clear child widget mask
9491 child.clearMask();
9492 QTRY_COMPARE(child.mask(), QRegion());
9493 // and ensure that that the child widget gets an update for the area outside the old mask.
9494 QTRY_COMPARE(child.numPaintEvents, 1);
9495 outsideOldMask = child.rect();
9496#ifdef Q_OS_MACOS
9497 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9498 if (!child.internalWinId())
9499#endif
9500 outsideOldMask -= childMask;
9501 QCOMPARE(child.paintedRegion, outsideOldMask);
9502 // and the parent widget doesn't get any update.
9503 QCOMPARE(topLevel.numPaintEvents, 0);
9504
9505 child.reset();
9506 topLevel.reset();
9507
9508 // Mask child widget with a mask that is bigger than the rect
9509 child.setMask(QRegion(0, 0, 1000, 1000));
9510#ifdef Q_OS_MACOS
9511 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9512 if (child.internalWinId())
9513 QTRY_COMPARE(child.numPaintEvents, 1);
9514 else
9515#endif
9516 // and ensure that we don't get any updates at all.
9517 QTRY_COMPARE(child.numPaintEvents, 0);
9518 QCOMPARE(topLevel.numPaintEvents, 0);
9519
9520 // ...and the same applies when clearing the mask.
9521 child.clearMask();
9522 QTest::qWait(ms: 100);
9523#ifdef Q_OS_MACOS
9524 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9525 if (child.internalWinId())
9526 QTRY_VERIFY(child.numPaintEvents > 0);
9527 else
9528#endif
9529 QCOMPARE(child.numPaintEvents, 0);
9530 QCOMPARE(topLevel.numPaintEvents, 0);
9531
9532 QWidget resizeParent;
9533 MaskResizeTestWidget resizeChild(&resizeParent);
9534
9535 resizeParent.resize(w: 300,h: 300);
9536 resizeParent.raise();
9537 resizeParent.setWindowFlags(Qt::WindowStaysOnTopHint);
9538 resizeChild.setGeometry(ax: 50,ay: 50,aw: 200,ah: 200);
9539 QPalette pal = resizeParent.palette();
9540 pal.setColor(acr: QPalette::Window, acolor: QColor(Qt::white));
9541 resizeParent.setPalette(pal);
9542
9543 resizeParent.show();
9544 QVERIFY(QTest::qWaitForWindowExposed(&resizeParent));
9545 // Disable the size grip on the Mac; otherwise it'll be included when grabbing the window.
9546 resizeParent.setFixedSize(resizeParent.size());
9547 resizeChild.show();
9548 QTest::qWait(ms: 100);
9549 resizeChild.paintedRegion = QRegion();
9550
9551 QTimer::singleShot(msec: 100, receiver: &resizeChild, SLOT(shrinkMask()));
9552 QTest::qWait(ms: 200);
9553#ifdef Q_OS_MACOS
9554 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9555 if (child.internalWinId())
9556 QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask());
9557 else
9558#endif
9559 QTRY_COMPARE(resizeChild.paintedRegion, QRegion());
9560
9561 resizeChild.paintedRegion = QRegion();
9562 const QRegion oldMask = resizeChild.mask();
9563 QTimer::singleShot(msec: 0, receiver: &resizeChild, SLOT(enlargeMask()));
9564 QTest::qWait(ms: 100);
9565#ifdef Q_OS_MACOS
9566 // Mac always issues a full update when calling setMask, and we cannot force it to not do so.
9567 if (child.internalWinId())
9568 QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask());
9569 else
9570#endif
9571 QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask() - oldMask);
9572}
9573
9574void tst_QWidget::maskedUpdate()
9575{
9576 UpdateWidget topLevel;
9577 topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9578 topLevel.resize(w: 200, h: 200);
9579 centerOnScreen(w: &topLevel);
9580 const QRegion topLevelMask(50, 50, 70, 70);
9581 topLevel.setMask(topLevelMask);
9582
9583 UpdateWidget child(&topLevel);
9584 child.setGeometry(ax: 20, ay: 20, aw: 180, ah: 180);
9585 const QRegion childMask(60, 60, 30, 30);
9586 child.setMask(childMask);
9587
9588 UpdateWidget grandChild(&child);
9589 grandChild.setGeometry(ax: 50, ay: 50, aw: 100, ah: 100);
9590 const QRegion grandChildMask(20, 20, 10, 10);
9591 grandChild.setMask(grandChildMask);
9592
9593 topLevel.show();
9594 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
9595 QTRY_VERIFY(topLevel.numPaintEvents > 0);
9596
9597
9598#define RESET_WIDGETS \
9599 topLevel.reset(); \
9600 child.reset(); \
9601 grandChild.reset();
9602
9603#define CLEAR_MASK(widget) \
9604 widget.clearMask(); \
9605 QTest::qWait(100); \
9606 RESET_WIDGETS;
9607
9608 // All widgets are transparent at this point, so any call to update() will result
9609 // in composition, i.e. the update propagates to ancestors and children.
9610
9611 // TopLevel update.
9612 RESET_WIDGETS;
9613 topLevel.update();
9614 QTest::qWait(ms: 10);
9615
9616 QTRY_COMPARE(topLevel.paintedRegion, topLevelMask);
9617 QTRY_COMPARE(child.paintedRegion, childMask);
9618 QTRY_COMPARE(grandChild.paintedRegion, grandChildMask);
9619
9620 // Child update.
9621 RESET_WIDGETS;
9622 child.update();
9623 QTest::qWait(ms: 10);
9624
9625 QTRY_COMPARE(topLevel.paintedRegion, childMask.translated(child.pos()));
9626 QTRY_COMPARE(child.paintedRegion, childMask);
9627 QTRY_COMPARE(grandChild.paintedRegion, grandChildMask);
9628
9629 // GrandChild update.
9630 RESET_WIDGETS;
9631 grandChild.update();
9632 QTest::qWait(ms: 10);
9633
9634 QTRY_COMPARE(topLevel.paintedRegion, grandChildMask.translated(grandChild.mapTo(&topLevel, QPoint())));
9635 QTRY_COMPARE(child.paintedRegion, grandChildMask.translated(grandChild.pos()));
9636 QTRY_COMPARE(grandChild.paintedRegion, grandChildMask);
9637
9638 topLevel.setAttribute(Qt::WA_OpaquePaintEvent);
9639 child.setAttribute(Qt::WA_OpaquePaintEvent);
9640 grandChild.setAttribute(Qt::WA_OpaquePaintEvent);
9641
9642 // All widgets are now opaque, which means no composition, i.e.
9643 // the update does not propate to ancestors and children.
9644
9645 // TopLevel update.
9646 RESET_WIDGETS;
9647 topLevel.update();
9648 QTest::qWait(ms: 10);
9649
9650 QRegion expectedTopLevelUpdate = topLevelMask;
9651 expectedTopLevelUpdate -= childMask.translated(p: child.pos()); // Subtract opaque children.
9652 QTRY_COMPARE(topLevel.paintedRegion, expectedTopLevelUpdate);
9653 QTRY_COMPARE(child.paintedRegion, QRegion());
9654 QTRY_COMPARE(grandChild.paintedRegion, QRegion());
9655
9656 // Child update.
9657 RESET_WIDGETS;
9658 child.update();
9659 QTest::qWait(ms: 10);
9660
9661 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9662 QRegion expectedChildUpdate = childMask;
9663 expectedChildUpdate -= grandChildMask.translated(p: grandChild.pos()); // Subtract oapque children.
9664 QTRY_COMPARE(child.paintedRegion, expectedChildUpdate);
9665 QTRY_COMPARE(grandChild.paintedRegion, QRegion());
9666
9667 // GrandChild update.
9668 RESET_WIDGETS;
9669 grandChild.update();
9670 QTest::qWait(ms: 10);
9671
9672 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9673 QTRY_COMPARE(child.paintedRegion, QRegion());
9674 QTRY_COMPARE(grandChild.paintedRegion, grandChildMask);
9675
9676 // GrandChild update.
9677 CLEAR_MASK(grandChild);
9678 grandChild.update();
9679 QTest::qWait(ms: 10);
9680
9681 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9682 QTRY_COMPARE(child.paintedRegion, QRegion());
9683 QRegion expectedGrandChildUpdate = grandChild.rect();
9684 // Clip with parent's mask.
9685 expectedGrandChildUpdate &= childMask.translated(p: -grandChild.pos());
9686 QCOMPARE(grandChild.paintedRegion, expectedGrandChildUpdate);
9687
9688 // GrandChild update.
9689 CLEAR_MASK(child);
9690 grandChild.update();
9691 QTest::qWait(ms: 10);
9692
9693 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9694 QTRY_COMPARE(child.paintedRegion, QRegion());
9695 expectedGrandChildUpdate = grandChild.rect();
9696 // Clip with parent's mask.
9697 expectedGrandChildUpdate &= topLevelMask.translated(p: -grandChild.mapTo(&topLevel, QPoint()));
9698 QTRY_COMPARE(grandChild.paintedRegion, expectedGrandChildUpdate);
9699
9700 // Child update.
9701 RESET_WIDGETS;
9702 child.update();
9703 QTest::qWait(ms: 10);
9704
9705 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9706 expectedChildUpdate = child.rect();
9707 // Clip with parent's mask.
9708 expectedChildUpdate &= topLevelMask.translated(p: -child.pos());
9709 expectedChildUpdate -= grandChild.geometry(); // Subtract opaque children.
9710 QTRY_COMPARE(child.paintedRegion, expectedChildUpdate);
9711 QTRY_COMPARE(grandChild.paintedRegion, QRegion());
9712
9713 // GrandChild update.
9714 CLEAR_MASK(topLevel);
9715 grandChild.update();
9716 QTest::qWait(ms: 10);
9717
9718 QTRY_COMPARE(topLevel.paintedRegion, QRegion());
9719 QTRY_COMPARE(child.paintedRegion, QRegion());
9720 QTRY_COMPARE(grandChild.paintedRegion, QRegion(grandChild.rect())); // Full update.
9721}
9722
9723#ifndef QT_NO_CURSOR
9724void tst_QWidget::syntheticEnterLeave()
9725{
9726 if (m_platform == QStringLiteral("wayland"))
9727 QSKIP("Wayland: This fails. Figure out why.");
9728 class MyWidget : public QWidget
9729 {
9730 public:
9731 using QWidget::QWidget;
9732 void enterEvent(QEvent *) override { ++numEnterEvents; }
9733 void leaveEvent(QEvent *) override { ++numLeaveEvents; }
9734 int numEnterEvents = 0;
9735 int numLeaveEvents = 0;
9736 };
9737
9738 QCursor::setPos(m_safeCursorPos);
9739
9740 MyWidget window;
9741 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9742 window.setWindowFlags(Qt::WindowStaysOnTopHint);
9743 window.move(ax: 200, ay: 200);
9744 window.resize(w: 200, h: 200);
9745
9746 MyWidget *child1 = new MyWidget(&window);
9747 child1->setPalette(Qt::blue);
9748 child1->setAutoFillBackground(true);
9749 child1->resize(w: 200, h: 200);
9750 child1->setCursor(Qt::OpenHandCursor);
9751
9752 MyWidget *child2 = new MyWidget(&window);
9753 child2->resize(w: 200, h: 200);
9754
9755 MyWidget *grandChild = new MyWidget(child2);
9756 grandChild->setPalette(Qt::red);
9757 grandChild->setAutoFillBackground(true);
9758 grandChild->resize(w: 200, h: 200);
9759 grandChild->setCursor(Qt::WaitCursor);
9760
9761 window.show();
9762 window.raise();
9763
9764 QVERIFY(QTest::qWaitForWindowExposed(&window));
9765
9766#define RESET_EVENT_COUNTS \
9767 window.numEnterEvents = 0; \
9768 window.numLeaveEvents = 0; \
9769 child1->numEnterEvents = 0; \
9770 child1->numLeaveEvents = 0; \
9771 child2->numEnterEvents = 0; \
9772 child2->numLeaveEvents = 0; \
9773 grandChild->numEnterEvents = 0; \
9774 grandChild->numLeaveEvents = 0;
9775
9776 // Position the cursor in the middle of the window.
9777 const QPoint globalPos = window.mapToGlobal(QPoint(100, 100));
9778 QCursor::setPos(globalPos); // Enter child2 and grandChild.
9779 QTest::qWait(ms: 300);
9780
9781 QCOMPARE(window.numLeaveEvents, 0);
9782 QCOMPARE(child2->numLeaveEvents, 0);
9783 QCOMPARE(grandChild->numLeaveEvents, 0);
9784 QCOMPARE(child1->numLeaveEvents, 0);
9785
9786 // This event arrives asynchronously
9787 QTRY_COMPARE(window.numEnterEvents, 1);
9788 QCOMPARE(child2->numEnterEvents, 1);
9789 QCOMPARE(grandChild->numEnterEvents, 1);
9790 QCOMPARE(child1->numEnterEvents, 0);
9791
9792 RESET_EVENT_COUNTS;
9793 child2->hide(); // Leave child2 and grandChild, enter child1.
9794
9795 QCOMPARE(window.numLeaveEvents, 0);
9796 QCOMPARE(child2->numLeaveEvents, 1);
9797 QCOMPARE(grandChild->numLeaveEvents, 1);
9798 QCOMPARE(child1->numLeaveEvents, 0);
9799
9800 QCOMPARE(window.numEnterEvents, 0);
9801 QCOMPARE(child2->numEnterEvents, 0);
9802 QCOMPARE(grandChild->numEnterEvents, 0);
9803 QCOMPARE(child1->numEnterEvents, 1);
9804
9805 RESET_EVENT_COUNTS;
9806 child2->show(); // Leave child1, enter child2 and grandChild.
9807
9808 QCOMPARE(window.numLeaveEvents, 0);
9809 QCOMPARE(child2->numLeaveEvents, 0);
9810 QCOMPARE(grandChild->numLeaveEvents, 0);
9811 QCOMPARE(child1->numLeaveEvents, 1);
9812
9813 QCOMPARE(window.numEnterEvents, 0);
9814 QCOMPARE(child2->numEnterEvents, 1);
9815 QCOMPARE(grandChild->numEnterEvents, 1);
9816 QCOMPARE(child1->numEnterEvents, 0);
9817
9818 RESET_EVENT_COUNTS;
9819 delete child2; // Enter child1 (and do not send leave events to child2 and grandChild).
9820
9821 QCOMPARE(window.numLeaveEvents, 0);
9822 QCOMPARE(child1->numLeaveEvents, 0);
9823
9824 QCOMPARE(window.numEnterEvents, 0);
9825 QCOMPARE(child1->numEnterEvents, 1);
9826}
9827#endif
9828
9829#ifndef QT_NO_CURSOR
9830void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave()
9831{
9832 if (m_platform == QStringLiteral("wayland"))
9833 QSKIP("Wayland: Clients can't set cursor position on wayland.");
9834 class SELParent : public QWidget
9835 {
9836 public:
9837 using QWidget::QWidget;
9838
9839 void mousePressEvent(QMouseEvent *) override { child->show(); }
9840 QWidget *child = nullptr;
9841 };
9842
9843 class SELChild : public QWidget
9844 {
9845 public:
9846 using QWidget::QWidget;
9847 void enterEvent(QEvent *) override { ++numEnterEvents; }
9848 void mouseMoveEvent(QMouseEvent *event) override
9849 {
9850 QCOMPARE(event->button(), Qt::NoButton);
9851 QCOMPARE(event->buttons(), QApplication::mouseButtons());
9852 QCOMPARE(event->modifiers(), QApplication::keyboardModifiers());
9853 ++numMouseMoveEvents;
9854 }
9855 void reset() { numEnterEvents = numMouseMoveEvents = 0; }
9856 int numEnterEvents = 0, numMouseMoveEvents = 0;
9857 };
9858
9859 QCursor::setPos(m_safeCursorPos);
9860
9861 SELParent parent;
9862 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9863 parent.move(ax: 200, ay: 200);
9864 parent.resize(w: 200, h: 200);
9865 SELChild child(&parent);
9866 child.resize(w: 200, h: 200);
9867 parent.show();
9868 QVERIFY(QTest::qWaitForWindowActive(&parent));
9869
9870 QCursor::setPos(child.mapToGlobal(QPoint(100, 100)));
9871 // Make sure the cursor has entered the child.
9872 QTRY_VERIFY(child.numEnterEvents > 0);
9873
9874 child.hide();
9875 child.reset();
9876 child.show();
9877
9878 // Make sure the child gets enter event and no mouse move event.
9879 QTRY_COMPARE(child.numEnterEvents, 1);
9880 QCOMPARE(child.numMouseMoveEvents, 0);
9881
9882 child.hide();
9883 child.reset();
9884 child.setMouseTracking(true);
9885 child.show();
9886
9887 // Make sure the child gets enter event.
9888 // Note that we verify event->button() and event->buttons()
9889 // in SELChild::mouseMoveEvent().
9890 QTRY_COMPARE(child.numEnterEvents, 1);
9891 QCOMPARE(child.numMouseMoveEvents, 0);
9892
9893 // Sending synthetic enter/leave trough the parent's mousePressEvent handler.
9894 parent.child = &child;
9895
9896 child.hide();
9897 child.reset();
9898 QTest::mouseClick(widget: &parent, button: Qt::LeftButton);
9899
9900 // Make sure the child gets enter event.
9901 QTRY_COMPARE(child.numEnterEvents, 1);
9902 QCOMPARE(child.numMouseMoveEvents, 0);
9903
9904 child.hide();
9905 child.reset();
9906 QTest::keyPress(widget: &parent, key: Qt::Key_Shift);
9907 QTest::mouseClick(widget: &parent, button: Qt::LeftButton);
9908
9909 // Make sure the child gets enter event
9910 QTRY_COMPARE(child.numEnterEvents, 1);
9911 QCOMPARE(child.numMouseMoveEvents, 0);
9912 QTest::keyRelease(widget: &child, key: Qt::Key_Shift);
9913 child.hide();
9914 child.reset();
9915 child.setMouseTracking(false);
9916 QTest::mouseClick(widget: &parent, button: Qt::LeftButton);
9917
9918 // Make sure the child gets enter event and no mouse move event.
9919 QTRY_COMPARE(child.numEnterEvents, 1);
9920 QCOMPARE(child.numMouseMoveEvents, 0);
9921 }
9922#endif
9923
9924void tst_QWidget::windowFlags()
9925{
9926 QWidget w;
9927 const auto baseFlags = w.windowFlags();
9928 w.setWindowFlags(w.windowFlags() | Qt::FramelessWindowHint);
9929 QVERIFY(w.windowFlags() & Qt::FramelessWindowHint);
9930 w.setWindowFlag(Qt::WindowStaysOnTopHint, on: true);
9931 QCOMPARE(w.windowFlags(), baseFlags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
9932 w.setWindowFlag(Qt::FramelessWindowHint, on: false);
9933 QCOMPARE(w.windowFlags(), baseFlags | Qt::WindowStaysOnTopHint);
9934}
9935
9936void tst_QWidget::initialPosForDontShowOnScreenWidgets()
9937{
9938 { // Check default position.
9939 const QPoint expectedPos(0, 0);
9940 QWidget widget;
9941 widget.setAttribute(Qt::WA_DontShowOnScreen);
9942 widget.winId(); // Make sure QWidgetPrivate::create is called.
9943 QCOMPARE(widget.pos(), expectedPos);
9944 QCOMPARE(widget.geometry().topLeft(), expectedPos);
9945 }
9946
9947 { // Explicitly move to a position.
9948 const QPoint expectedPos(100, 100);
9949 QWidget widget;
9950 widget.setAttribute(Qt::WA_DontShowOnScreen);
9951 widget.move(expectedPos);
9952 widget.winId(); // Make sure QWidgetPrivate::create is called.
9953 QCOMPARE(widget.pos(), expectedPos);
9954 QCOMPARE(widget.geometry().topLeft(), expectedPos);
9955 }
9956}
9957
9958class MyEvilObject : public QObject
9959{
9960 Q_OBJECT
9961public:
9962 explicit MyEvilObject(QWidget *widgetToCrash) : QObject(), widget(widgetToCrash)
9963 {
9964 connect(sender: widget, signal: &QObject::destroyed, receiver: this, slot: &MyEvilObject::beEvil);
9965 delete widget;
9966 }
9967 QWidget *widget;
9968
9969private slots:
9970 void beEvil(QObject *) { widget->update(ax: 0, ay: 0, aw: 150, ah: 150); }
9971};
9972
9973void tst_QWidget::updateOnDestroyedSignal()
9974{
9975 QWidget widget;
9976 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9977
9978 QWidget *child = new QWidget(&widget);
9979 child->resize(m_testWidgetSize);
9980 child->setAutoFillBackground(true);
9981 child->setPalette(Qt::red);
9982
9983 widget.show();
9984 QVERIFY(QTest::qWaitForWindowExposed(&widget));
9985
9986 // Please do not crash.
9987 MyEvilObject evil(child);
9988 QTest::qWait(ms: 200);
9989}
9990
9991void tst_QWidget::toplevelLineEditFocus()
9992{
9993 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
9994 QSKIP("Wayland: This fails. Figure out why.");
9995
9996 QLineEdit w;
9997 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
9998 w.setMinimumWidth(m_testWidgetSize.width());
9999 w.show();
10000 QVERIFY(QTest::qWaitForWindowExposed(&w));
10001
10002 QTRY_COMPARE(QApplication::activeWindow(), static_cast<const QWidget *>(&w));
10003 QTRY_COMPARE(QApplication::focusWidget(), static_cast<const QWidget *>(&w));
10004}
10005
10006void tst_QWidget::focusWidget_task254563()
10007{
10008 //having different visibility for widget is important
10009 QWidget top;
10010 top.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10011 top.show();
10012 QWidget container(&top);
10013 QWidget *widget = new QWidget(&container);
10014 widget->show();
10015
10016 widget->setFocus(); //set focus (will set the focus widget up to the toplevel to be 'widget')
10017 container.setFocus();
10018 delete widget; // will call clearFocus but that doesn't help
10019 QVERIFY(top.focusWidget() != widget); //dangling pointer
10020}
10021
10022// This test case relies on developer build (AUTOTEST_EXPORT).
10023#ifdef QT_BUILD_INTERNAL
10024void tst_QWidget::destroyBackingStore()
10025{
10026 UpdateWidget w;
10027 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10028 centerOnScreen(w: &w);
10029 w.reset();
10030 w.show();
10031
10032 QVERIFY(QTest::qWaitForWindowExposed(&w));
10033 QApplication::processEvents();
10034 QTRY_VERIFY(w.numPaintEvents > 0);
10035 w.reset();
10036 w.update();
10037 qt_widget_private(widget: &w)->topData()->repaintManager.reset(p: new QWidgetRepaintManager(&w));
10038
10039 w.update();
10040 QApplication::processEvents();
10041
10042 QCOMPARE(w.numPaintEvents, 1);
10043
10044 // Check one more time, because the second time around does more caching.
10045 w.update();
10046 QApplication::processEvents();
10047 QCOMPARE(w.numPaintEvents, 2);
10048}
10049#endif // QT_BUILD_INTERNAL
10050
10051// Helper function
10052QWidgetRepaintManager* repaintManager(QWidget &widget)
10053{
10054 QWidgetRepaintManager *repaintManager = nullptr;
10055#ifdef QT_BUILD_INTERNAL
10056 if (QTLWExtra *topExtra = qt_widget_private(widget: &widget)->maybeTopData())
10057 repaintManager = topExtra->repaintManager.get();
10058#endif
10059 return repaintManager;
10060}
10061
10062// Tables of 5000 elements do not make sense on Windows Mobile.
10063void tst_QWidget::rectOutsideCoordinatesLimit_task144779()
10064{
10065#ifndef QT_NO_CURSOR
10066 QGuiApplication::setOverrideCursor(Qt::BlankCursor); //keep the cursor out of screen grabs
10067#endif
10068 QWidget main(nullptr, Qt::FramelessWindowHint); //don't get confused by the size of the window frame
10069 main.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10070 QPalette palette;
10071 palette.setColor(acr: QPalette::Window, acolor: Qt::red);
10072 main.setPalette(palette);
10073
10074 QRect desktopDimensions = main.screen()->availableGeometry();
10075 QSize mainSize(400, 400);
10076 mainSize = mainSize.boundedTo(otherSize: desktopDimensions.size());
10077 main.resize(mainSize);
10078
10079 QWidget *offsetWidget = new QWidget(&main);
10080 offsetWidget->setGeometry(ax: 0, ay: -(15000 - mainSize.height()), aw: mainSize.width(), ah: 15000);
10081
10082 // big widget is too big for the coordinates, it must be limited by wrect
10083 // if wrect is not at the right position because of offsetWidget, bigwidget
10084 // is not painted correctly
10085 QWidget *bigWidget = new QWidget(offsetWidget);
10086 bigWidget->setGeometry(ax: 0, ay: 0, aw: mainSize.width(), ah: 50000);
10087 palette.setColor(acr: QPalette::Window, acolor: Qt::green);
10088 bigWidget->setPalette(palette);
10089 bigWidget->setAutoFillBackground(true);
10090
10091 main.showNormal();
10092 QVERIFY(QTest::qWaitForWindowExposed(&main));
10093
10094 QPixmap correct(main.size());
10095 correct.fill(fillColor: Qt::green);
10096 const QPixmap mainPixmap = grabFromWidget(w: &main, rect: QRect(QPoint(0, 0), QSize(-1, -1)));
10097
10098 if (m_platform == QStringLiteral("winrt"))
10099 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
10100 QTRY_COMPARE(mainPixmap.toImage().convertToFormat(QImage::Format_RGB32),
10101 correct.toImage().convertToFormat(QImage::Format_RGB32));
10102#ifndef QT_NO_CURSOR
10103 QGuiApplication::restoreOverrideCursor();
10104#endif
10105}
10106
10107void tst_QWidget::setGraphicsEffect()
10108{
10109 // Check that we don't have any effect by default.
10110 QScopedPointer<QWidget> widget(new QWidget);
10111 widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10112 QVERIFY(!widget->graphicsEffect());
10113
10114 // SetGet check.
10115 QPointer<QGraphicsEffect> blurEffect = new QGraphicsBlurEffect;
10116 widget->setGraphicsEffect(blurEffect);
10117 QCOMPARE(widget->graphicsEffect(), static_cast<QGraphicsEffect *>(blurEffect));
10118
10119 // Ensure the existing effect is deleted when setting a new one.
10120 QPointer<QGraphicsEffect> shadowEffect = new QGraphicsDropShadowEffect;
10121 widget->setGraphicsEffect(shadowEffect);
10122 QVERIFY(!blurEffect);
10123 QCOMPARE(widget->graphicsEffect(), static_cast<QGraphicsEffect *>(shadowEffect));
10124 blurEffect = new QGraphicsBlurEffect;
10125
10126 // Ensure the effect is uninstalled when setting it on a new target.
10127 QScopedPointer<QWidget> anotherWidget(new QWidget);
10128 anotherWidget->setGraphicsEffect(blurEffect);
10129 widget->setGraphicsEffect(blurEffect);
10130 QVERIFY(!anotherWidget->graphicsEffect());
10131 QVERIFY(!shadowEffect);
10132
10133 // Ensure the existing effect is deleted when deleting the widget.
10134 widget.reset();
10135 QVERIFY(!blurEffect);
10136 anotherWidget.reset();
10137
10138 // Ensure the effect is uninstalled when deleting it
10139 widget.reset(other: new QWidget);
10140 blurEffect = new QGraphicsBlurEffect;
10141 widget->setGraphicsEffect(blurEffect);
10142 delete blurEffect;
10143 QVERIFY(!widget->graphicsEffect());
10144
10145 // Ensure the existing effect is uninstalled and deleted when setting a null effect
10146 blurEffect = new QGraphicsBlurEffect;
10147 widget->setGraphicsEffect(blurEffect);
10148 widget->setGraphicsEffect(nullptr);
10149 QVERIFY(!widget->graphicsEffect());
10150 QVERIFY(!blurEffect);
10151}
10152
10153
10154class TestGraphicsEffect : public QGraphicsEffect
10155{
10156public:
10157 TestGraphicsEffect(QObject *parent = nullptr)
10158 : QGraphicsEffect(parent)
10159 {
10160 m_pattern = QPixmap(10, 10);
10161 m_pattern.fill(fillColor: Qt::lightGray);
10162 QPainter p(&m_pattern);
10163 p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray));
10164 p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray));
10165 }
10166 void setExtent(int extent)
10167 {
10168 m_extent = extent;
10169 }
10170 QRectF boundingRectFor(const QRectF &sr) const override
10171 {
10172 return QRectF(sr.x() - m_extent, sr.y() - m_extent,
10173 sr.width() + 2 * m_extent, sr.height() + 2 * m_extent);
10174 }
10175protected:
10176 void draw(QPainter *painter) override
10177 {
10178 QBrush brush;
10179 brush.setTexture(m_pattern);
10180 brush.setStyle(Qt::TexturePattern);
10181 QPaintDevice *p = painter->device();
10182 painter->fillRect(QRect(-m_extent, -m_extent,
10183 p->width() + m_extent, p->height() + m_extent), brush);
10184 }
10185 QPixmap m_pattern;
10186 int m_extent = 0;
10187};
10188
10189static QImage fillExpected1()
10190{
10191 QImage expected(QSize(40, 40), QImage::Format_RGB32);
10192 QPainter p(&expected);
10193 p.fillRect(QRect{{0, 0}, expected.size()}, QBrush(Qt::gray));
10194 p.fillRect(QRect(10, 10, 10, 10), QBrush(Qt::red));
10195 p.fillRect(QRect(20, 20, 10, 10), QBrush(Qt::blue));
10196 return expected;
10197}
10198static QImage fillExpected2()
10199{
10200 QImage expected = fillExpected1();
10201 QPainter p(&expected);
10202 p.fillRect(QRect(10, 10, 5, 5), QBrush(Qt::darkGray));
10203 p.fillRect(QRect(15, 15, 5, 5), QBrush(Qt::darkGray));
10204 p.fillRect(QRect(15, 10, 5, 5), QBrush(Qt::lightGray));
10205 p.fillRect(QRect(10, 15, 5, 5), QBrush(Qt::lightGray));
10206 return expected;
10207}
10208static QImage fillExpected3()
10209{
10210 QImage expected(QSize(40, 40), QImage::Format_RGB32);
10211 QPixmap pattern;
10212 pattern = QPixmap(10, 10);
10213 pattern.fill(fillColor: Qt::lightGray);
10214 QPainter p(&pattern);
10215 p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray));
10216 p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray));
10217 QBrush brush;
10218 brush.setTexture(pattern);
10219 brush.setStyle(Qt::TexturePattern);
10220 QPainter p2(&expected);
10221 p2.fillRect(QRect{{0, 0}, expected.size()}, brush);
10222 return expected;
10223}
10224static QImage fillExpected4()
10225{
10226 QImage expected = fillExpected1();
10227 QPixmap pattern;
10228 pattern = QPixmap(10, 10);
10229 pattern.fill(fillColor: Qt::lightGray);
10230 QPainter p(&pattern);
10231 p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray));
10232 p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray));
10233 QBrush brush;
10234 brush.setTexture(pattern);
10235 brush.setStyle(Qt::TexturePattern);
10236 QPainter p2(&expected);
10237 p2.fillRect(QRect{{15, 15}, QSize{20, 20}}, brush);
10238 return expected;
10239}
10240
10241void tst_QWidget::render_graphicsEffect_data()
10242{
10243 QTest::addColumn<QImage>(name: "expected");
10244 QTest::addColumn<bool>(name: "topLevelEffect");
10245 QTest::addColumn<bool>(name: "child1Effect");
10246 QTest::addColumn<bool>(name: "child2Effect");
10247 QTest::addColumn<int>(name: "extent");
10248
10249 QTest::addRow(format: "no_effect") << fillExpected1() << false << false << false << 0;
10250 QTest::addRow(format: "first_child_effect") << fillExpected2() << false << true << false << 0;
10251 QTest::addRow(format: "top_level_effect") << fillExpected3() << true << false << false << 0;
10252 QTest::addRow(format: "effect_with_extent") << fillExpected4() << false << false << true << 5;
10253}
10254
10255void tst_QWidget::render_graphicsEffect()
10256{
10257 QFETCH(QImage, expected);
10258 QFETCH(bool, topLevelEffect);
10259 QFETCH(bool, child1Effect);
10260 QFETCH(bool, child2Effect);
10261 QFETCH(int, extent);
10262
10263 QScopedPointer<QWidget> topLevel(new QWidget);
10264 topLevel->setPalette(Qt::gray);
10265 topLevel->resize(w: 40, h: 40);
10266 topLevel->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::")
10267 + QLatin1String(QTest::currentDataTag()));
10268
10269 // Render widget with 2 child widgets
10270 QImage image(topLevel->size(), QImage::Format_RGB32);
10271 image.fill(pixel: QColor(Qt::gray).rgb());
10272
10273 QPainter painter(&image);
10274
10275 QWidget *childWidget1(new QWidget(topLevel.data()));
10276 childWidget1->setAutoFillBackground(true);
10277 childWidget1->setPalette(Qt::red);
10278 childWidget1->resize(w: 10, h: 10);
10279 childWidget1->move(ax: 10, ay: 10);
10280 QWidget *childWidget2(new QWidget(topLevel.data()));
10281 childWidget2->setAutoFillBackground(true);
10282 childWidget2->setPalette(Qt::blue);
10283 childWidget2->resize(w: 10, h: 10);
10284 childWidget2->move(ax: 20, ay: 20);
10285
10286 TestGraphicsEffect *graphicsEffect(new TestGraphicsEffect(topLevel.data()));
10287 if (topLevelEffect)
10288 topLevel->setGraphicsEffect(graphicsEffect);
10289 if (child1Effect)
10290 childWidget1->setGraphicsEffect(graphicsEffect);
10291 if (child2Effect)
10292 childWidget2->setGraphicsEffect(graphicsEffect);
10293 graphicsEffect->setExtent(extent);
10294
10295 // Render without effect
10296 topLevel->render(painter: &painter);
10297#ifdef RENDER_DEBUG
10298 image.save("render_GraphicsEffect" + QTest::currentDataTag() + ".png");
10299 expected.save("render_GraphicsEffect_expected" + QTest::currentDataTag() + ".png");
10300#endif
10301 QCOMPARE(image, expected);
10302}
10303
10304void tst_QWidget::activateWindow()
10305{
10306 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
10307 QSKIP("Window activation is not supported.");
10308
10309 // Test case for QTBUG-26711
10310
10311 // Create first mainwindow and set it active
10312 QScopedPointer<QMainWindow> mainwindow(new QMainWindow);
10313 mainwindow->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10314 QLabel* label = new QLabel(mainwindow.data());
10315 label->setMinimumWidth(m_testWidgetSize.width());
10316 mainwindow->setWindowTitle(QStringLiteral("#1 ") + __FUNCTION__);
10317 mainwindow->setCentralWidget(label);
10318 mainwindow->move(m_availableTopLeft + QPoint(100, 100));
10319 mainwindow->setVisible(true);
10320 mainwindow->activateWindow();
10321 QVERIFY(QTest::qWaitForWindowActive(mainwindow.data()));
10322 QVERIFY(mainwindow->isActiveWindow());
10323
10324 // Create second mainwindow and set it active
10325 QScopedPointer<QMainWindow> mainwindow2(new QMainWindow);
10326 mainwindow2->setWindowTitle(QStringLiteral("#2 ") + __FUNCTION__);
10327 QLabel* label2 = new QLabel(mainwindow2.data());
10328 label2->setMinimumWidth(m_testWidgetSize.width());
10329 mainwindow2->setCentralWidget(label2);
10330 mainwindow2->move(mainwindow->geometry().bottomLeft() + QPoint(0, 50));
10331 mainwindow2->setVisible(true);
10332 mainwindow2->activateWindow();
10333 QCoreApplication::processEvents();
10334
10335 QTRY_VERIFY(!mainwindow->isActiveWindow());
10336 QTRY_VERIFY(mainwindow2->isActiveWindow());
10337
10338 // Revert first mainwindow back to visible active
10339 mainwindow->setVisible(true);
10340 mainwindow->activateWindow();
10341 QCoreApplication::processEvents();
10342
10343 QTRY_VERIFY(mainwindow->isActiveWindow());
10344 if (m_platform == QStringLiteral("winrt"))
10345 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
10346 QTRY_VERIFY(!mainwindow2->isActiveWindow());
10347}
10348
10349void tst_QWidget::openModal_taskQTBUG_5804()
10350{
10351 class Widget : public QWidget
10352 {
10353 public:
10354 Widget(QWidget *parent) : QWidget(parent) {}
10355 ~Widget()
10356 {
10357 QMessageBox msgbox;
10358 QTimer::singleShot(msec: 10, receiver: &msgbox, SLOT(accept()));
10359 msgbox.exec(); //open a modal dialog
10360 }
10361 };
10362
10363 QScopedPointer<QWidget> win(new QWidget);
10364 win->resize(m_testWidgetSize);
10365 win->setWindowTitle(__FUNCTION__);
10366 centerOnScreen(w: win.data());
10367
10368 new Widget(win.data());
10369 win->show();
10370 QVERIFY(QTest::qWaitForWindowExposed(win.data()));
10371}
10372
10373/*!
10374 Test that the focus proxy receives focus, and that changing the
10375 focus proxy of a widget that has focus passes focus on correctly.
10376
10377 The test uses a single window, so we can rely on the window's focus
10378 widget and the QApplication focus widget to be the same.
10379*/
10380void tst_QWidget::focusProxy()
10381{
10382 QWidget window;
10383 window.setFocusPolicy(Qt::StrongFocus);
10384 class Container : public QWidget
10385 {
10386 public:
10387 Container()
10388 {
10389 edit = new QLineEdit;
10390 edit->installEventFilter(filterObj: this);
10391 setFocusProxy(edit);
10392 QHBoxLayout *layout = new QHBoxLayout;
10393 layout->addWidget(edit);
10394 setLayout(layout);
10395 }
10396
10397 QLineEdit *edit;
10398 int focusInCount = 0;
10399 int focusOutCount = 0;
10400
10401 protected:
10402 bool eventFilter(QObject *receiver, QEvent *event)
10403 {
10404 if (receiver == edit) {
10405 switch (event->type()) {
10406 case QEvent::FocusIn:
10407 ++focusInCount;
10408 break;
10409 case QEvent::FocusOut:
10410 ++focusOutCount;
10411 break;
10412 default:
10413 break;
10414 }
10415 }
10416
10417 return QWidget::eventFilter(watched: receiver, event);
10418 }
10419 };
10420
10421 auto container1 = new Container;
10422 container1->edit->setObjectName("edit1");
10423 auto container2 = new Container;
10424 container2->edit->setObjectName("edit2");
10425
10426 QHBoxLayout *layout = new QHBoxLayout;
10427 layout->addWidget(container1);
10428 layout->addWidget(container2);
10429 window.setLayout(layout);
10430
10431 window.setFocus();
10432 window.show();
10433 window.activateWindow();
10434 if (!QTest::qWaitForWindowExposed(widget: &window) || !QTest::qWaitForWindowActive(widget: &window))
10435 QSKIP("Window activation failed");
10436
10437 // given a widget without focus proxy
10438 QVERIFY(window.hasFocus());
10439 QCOMPARE(&window, QApplication::focusWidget());
10440 QVERIFY(!container1->hasFocus());
10441 QVERIFY(!container2->hasFocus());
10442 QCOMPARE(container1->focusInCount, 0);
10443 QCOMPARE(container1->focusOutCount, 0);
10444
10445 // setting a (nested) focus proxy moves focus
10446 window.setFocusProxy(container1);
10447 QCOMPARE(window.focusWidget(), container1->edit);
10448 QCOMPARE(window.focusWidget(), QApplication::focusWidget());
10449 QVERIFY(window.hasFocus()); // and redirects hasFocus correctly
10450 QVERIFY(container1->edit->hasFocus());
10451 QCOMPARE(container1->focusInCount, 1);
10452
10453 // changing the focus proxy should not move focus
10454 window.setFocusProxy(container2);
10455 QCOMPARE(window.focusWidget(), container1->edit);
10456 QCOMPARE(window.focusWidget(), QApplication::focusWidget());
10457 QVERIFY(!window.hasFocus());
10458 QCOMPARE(container1->focusOutCount, 0);
10459
10460 // but setting focus again does
10461 window.setFocus();
10462 QCOMPARE(window.focusWidget(), container2->edit);
10463 QCOMPARE(window.focusWidget(), QApplication::focusWidget());
10464 QVERIFY(window.hasFocus());
10465 QVERIFY(!container1->edit->hasFocus());
10466 QVERIFY(container2->edit->hasFocus());
10467 QCOMPARE(container1->focusInCount, 1);
10468 QCOMPARE(container1->focusOutCount, 1);
10469 QCOMPARE(container2->focusInCount, 1);
10470 QCOMPARE(container2->focusOutCount, 0);
10471
10472 // clearing the focus proxy does not move focus
10473 window.setFocusProxy(nullptr);
10474 QCOMPARE(window.focusWidget(), container2->edit);
10475 QCOMPARE(window.focusWidget(), QApplication::focusWidget());
10476 QVERIFY(!window.hasFocus());
10477 QCOMPARE(container1->focusInCount, 1);
10478 QCOMPARE(container1->focusOutCount, 1);
10479 QCOMPARE(container2->focusInCount, 1);
10480 QCOMPARE(container2->focusOutCount, 0);
10481
10482 // but clearing focus does
10483 window.focusWidget()->clearFocus();
10484 QCOMPARE(QApplication::focusWidget(), nullptr);
10485 QVERIFY(!window.hasFocus());
10486 QVERIFY(!container2->hasFocus());
10487 QVERIFY(!container2->edit->hasFocus());
10488 QCOMPARE(container2->focusOutCount, 1);
10489}
10490
10491void tst_QWidget::focusProxyAndInputMethods()
10492{
10493 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
10494 QSKIP("Window activation is not supported.");
10495 QScopedPointer<QWidget> toplevel(new QWidget(nullptr, Qt::X11BypassWindowManagerHint));
10496 toplevel->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10497 toplevel->resize(w: 200, h: 200);
10498 toplevel->setAttribute(Qt::WA_InputMethodEnabled, on: true);
10499
10500 QWidget *child = new QWidget(toplevel.data());
10501 child->setFocusProxy(toplevel.data());
10502 child->setAttribute(Qt::WA_InputMethodEnabled, on: true);
10503
10504 toplevel->setFocusPolicy(Qt::WheelFocus);
10505 child->setFocusPolicy(Qt::WheelFocus);
10506
10507 QVERIFY(!child->hasFocus());
10508 QVERIFY(!toplevel->hasFocus());
10509
10510 toplevel->show();
10511 QVERIFY(QTest::qWaitForWindowExposed(toplevel.data()));
10512 QApplication::setActiveWindow(toplevel.data());
10513 QVERIFY(QTest::qWaitForWindowActive(toplevel.data()));
10514 QVERIFY(toplevel->hasFocus());
10515 QVERIFY(child->hasFocus());
10516 QCOMPARE(qApp->focusObject(), toplevel.data());
10517}
10518
10519#ifdef QT_BUILD_INTERNAL
10520class scrollWidgetWBS : public QWidget
10521{
10522public:
10523 void deleteBackingStore()
10524 {
10525 static_cast<QWidgetPrivate*>(d_ptr.data())->topData()->repaintManager.reset(p: nullptr);
10526 }
10527 void enableBackingStore()
10528 {
10529 if (!static_cast<QWidgetPrivate*>(d_ptr.data())->maybeRepaintManager()) {
10530 static_cast<QWidgetPrivate*>(d_ptr.data())->topData()->repaintManager.reset(p: new QWidgetRepaintManager(this));
10531 static_cast<QWidgetPrivate*>(d_ptr.data())->invalidateBackingStore(this->rect());
10532 update();
10533 }
10534 }
10535};
10536#endif
10537
10538// Test case relies on developer build (AUTOTEST_EXPORT).
10539#ifdef QT_BUILD_INTERNAL
10540void tst_QWidget::scrollWithoutBackingStore()
10541{
10542 scrollWidgetWBS scrollable;
10543 scrollable.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10544 scrollable.resize(w: 200, h: 200);
10545 QLabel child(QString("@"),&scrollable);
10546 child.resize(w: 50,h: 50);
10547 scrollable.show();
10548 QVERIFY(QTest::qWaitForWindowExposed(&scrollable));
10549 scrollable.scroll(dx: 50,dy: 50);
10550 QCOMPARE(child.pos(),QPoint(50,50));
10551 scrollable.deleteBackingStore();
10552 scrollable.scroll(dx: -25,dy: -25);
10553 QCOMPARE(child.pos(),QPoint(25,25));
10554 scrollable.enableBackingStore();
10555 QTRY_COMPARE(child.pos(),QPoint(25,25));
10556}
10557#endif
10558
10559void tst_QWidget::taskQTBUG_7532_tabOrderWithFocusProxy()
10560{
10561 QWidget w;
10562 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10563 w.setFocusPolicy(Qt::TabFocus);
10564 QWidget *fp = new QWidget(&w);
10565 fp->setFocusPolicy(Qt::TabFocus);
10566 w.setFocusProxy(fp);
10567 QWidget::setTabOrder(&w, fp);
10568
10569 // In debug mode, no assertion failure means it's alright.
10570}
10571
10572void tst_QWidget::movedAndResizedAttributes()
10573{
10574 // Use Qt::Tool as fully decorated windows have a minimum width of 160 on
10575 QWidget w(nullptr, Qt::Tool);
10576 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10577 w.show();
10578
10579 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10580 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10581
10582 w.setWindowState(Qt::WindowFullScreen);
10583
10584 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10585 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10586
10587 w.setWindowState(Qt::WindowMaximized);
10588
10589 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10590 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10591
10592 w.setWindowState(Qt::WindowMinimized);
10593
10594 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10595 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10596
10597 w.showNormal();
10598
10599 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10600 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10601
10602 w.showMaximized();
10603
10604 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10605 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10606
10607 w.showFullScreen();
10608
10609 QVERIFY(!w.testAttribute(Qt::WA_Moved));
10610 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10611
10612 w.showNormal();
10613 w.move(ax: 10,ay: 10);
10614 QVERIFY(w.testAttribute(Qt::WA_Moved));
10615 QVERIFY(!w.testAttribute(Qt::WA_Resized));
10616
10617 w.resize(w: 100, h: 100);
10618 QVERIFY(w.testAttribute(Qt::WA_Moved));
10619 QVERIFY(w.testAttribute(Qt::WA_Resized));
10620}
10621
10622void tst_QWidget::childAt()
10623{
10624 QWidget parent(nullptr, Qt::FramelessWindowHint);
10625 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10626 parent.resize(w: 200, h: 200);
10627
10628 QWidget *child = new QWidget(&parent);
10629 child->setPalette(Qt::red);
10630 child->setAutoFillBackground(true);
10631 child->setGeometry(ax: 20, ay: 20, aw: 160, ah: 160);
10632
10633 QWidget *grandChild = new QWidget(child);
10634 grandChild->setPalette(Qt::blue);
10635 grandChild->setAutoFillBackground(true);
10636 grandChild->setGeometry(ax: -20, ay: -20, aw: 220, ah: 220);
10637
10638 QVERIFY(!parent.childAt(19, 19));
10639 QVERIFY(!parent.childAt(180, 180));
10640 QCOMPARE(parent.childAt(20, 20), grandChild);
10641 QCOMPARE(parent.childAt(179, 179), grandChild);
10642
10643 grandChild->setAttribute(Qt::WA_TransparentForMouseEvents);
10644 QCOMPARE(parent.childAt(20, 20), child);
10645 QCOMPARE(parent.childAt(179, 179), child);
10646 grandChild->setAttribute(Qt::WA_TransparentForMouseEvents, on: false);
10647
10648 child->setMask(QRect(50, 50, 60, 60));
10649
10650 QVERIFY(!parent.childAt(69, 69));
10651 QVERIFY(!parent.childAt(130, 130));
10652 QCOMPARE(parent.childAt(70, 70), grandChild);
10653 QCOMPARE(parent.childAt(129, 129), grandChild);
10654
10655 child->setAttribute(Qt::WA_MouseNoMask);
10656 QCOMPARE(parent.childAt(69, 69), grandChild);
10657 QCOMPARE(parent.childAt(130, 130), grandChild);
10658 child->setAttribute(Qt::WA_MouseNoMask, on: false);
10659
10660 grandChild->setAttribute(Qt::WA_TransparentForMouseEvents);
10661 QCOMPARE(parent.childAt(70, 70), child);
10662 QCOMPARE(parent.childAt(129, 129), child);
10663 grandChild->setAttribute(Qt::WA_TransparentForMouseEvents, on: false);
10664
10665 grandChild->setMask(QRect(80, 80, 40, 40));
10666
10667 QCOMPARE(parent.childAt(79, 79), child);
10668 QCOMPARE(parent.childAt(120, 120), child);
10669 QCOMPARE(parent.childAt(80, 80), grandChild);
10670 QCOMPARE(parent.childAt(119, 119), grandChild);
10671
10672 grandChild->setAttribute(Qt::WA_MouseNoMask);
10673
10674 QCOMPARE(parent.childAt(79, 79), grandChild);
10675 QCOMPARE(parent.childAt(120, 120), grandChild);
10676}
10677
10678#ifdef Q_OS_MACOS
10679
10680void tst_QWidget::taskQTBUG_11373()
10681{
10682 QSKIP("QTBUG-52974");
10683
10684 QScopedPointer<QMainWindow> myWindow(new QMainWindow);
10685 QWidget * center = new QWidget();
10686 myWindow -> setCentralWidget(center);
10687 QWidget * drawer = new QWidget(myWindow.data(), Qt::Drawer);
10688 drawer -> hide();
10689 QCOMPARE(drawer->isVisible(), false);
10690 myWindow -> show();
10691 myWindow -> raise();
10692 // The drawer shouldn't be visible now.
10693 QCOMPARE(drawer->isVisible(), false);
10694 myWindow -> setWindowState(Qt::WindowFullScreen);
10695 myWindow -> setWindowState(Qt::WindowNoState);
10696 // The drawer should still not be visible, since we haven't shown it.
10697 QCOMPARE(drawer->isVisible(), false);
10698}
10699
10700#endif
10701
10702void tst_QWidget::taskQTBUG_17333_ResizeInfiniteRecursion()
10703{
10704 QTableView tb;
10705 tb.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10706 const char *s = "border: 1px solid;";
10707 tb.setStyleSheet(s);
10708 tb.show();
10709
10710 QVERIFY(QTest::qWaitForWindowExposed(&tb));
10711 tb.setGeometry(QRect(100, 100, 0, 100));
10712 // No crash, it works.
10713}
10714
10715void tst_QWidget::nativeChildFocus()
10716{
10717 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
10718 QSKIP("Wayland: This fails. Figure out why.");
10719
10720 QWidget w;
10721 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10722 w.setMinimumWidth(m_testWidgetSize.width());
10723 w.setWindowTitle(__FUNCTION__);
10724 QLayout *layout = new QVBoxLayout;
10725 w.setLayout(layout);
10726 QLineEdit *p1 = new QLineEdit;
10727 QLineEdit *p2 = new QLineEdit;
10728 layout->addWidget(w: p1);
10729 layout->addWidget(w: p2);
10730 p1->setObjectName("p1");
10731 p2->setObjectName("p2");
10732 centerOnScreen(w: &w);
10733 w.show();
10734 w.activateWindow();
10735 p1->setFocus();
10736 p1->setAttribute(Qt::WA_NativeWindow);
10737 p2->setAttribute(Qt::WA_NativeWindow);
10738 QVERIFY(QTest::qWaitForWindowExposed(&w));
10739
10740 QCOMPARE(QApplication::activeWindow(), &w);
10741 QCOMPARE(QApplication::focusWidget(), static_cast<QWidget*>(p1));
10742}
10743
10744static bool lenientCompare(const QPixmap &actual, const QPixmap &expected)
10745{
10746 QImage expectedImage = expected.toImage().convertToFormat(f: QImage::Format_RGB32);
10747 QImage actualImage = actual.toImage().convertToFormat(f: QImage::Format_RGB32);
10748
10749 if (expectedImage.size() != actualImage.size()) {
10750 qWarning(msg: "Image size comparison failed: expected: %dx%d, got %dx%d",
10751 expectedImage.size().width(), expectedImage.size().height(),
10752 actualImage.size().width(), actualImage.size().height());
10753 return false;
10754 }
10755
10756 const int size = actual.width() * actual.height();
10757 const int threshold = QPixmap::defaultDepth() == 16 ? 10 : 2;
10758
10759 auto a = reinterpret_cast<const QRgb *>(actualImage.bits());
10760 auto e = reinterpret_cast<const QRgb *>(expectedImage.bits());
10761 for (int i = 0; i < size; ++i) {
10762 const QColor ca(a[i]);
10763 const QColor ce(e[i]);
10764 if (qAbs(t: ca.red() - ce.red()) > threshold
10765 || qAbs(t: ca.green() - ce.green()) > threshold
10766 || qAbs(t: ca.blue() - ce.blue()) > threshold) {
10767 qWarning(msg: "Color mismatch at pixel #%d: Expected: %d,%d,%d, got %d,%d,%d",
10768 i, ce.red(), ce.green(), ce.blue(), ca.red(), ca.green(), ca.blue());
10769 return false;
10770 }
10771 }
10772
10773 return true;
10774}
10775
10776void tst_QWidget::grab()
10777{
10778 for (int opaque = 0; opaque < 2; ++opaque) {
10779 QWidget widget;
10780 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10781 QImage image(128, 128, opaque ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied);
10782 for (int row = 0; row < image.height(); ++row) {
10783 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(row));
10784 for (int col = 0; col < image.width(); ++col)
10785 line[col] = qRgba(r: QRandomGenerator::global()->bounded(highest: 255), g: row, b: col, a: opaque ? 255 : 127);
10786 }
10787
10788 QPalette pal = widget.palette();
10789 pal.setBrush(acr: QPalette::Window, abrush: QBrush(image));
10790 widget.setPalette(pal);
10791 widget.resize(w: 128, h: 128);
10792
10793 QPixmap expected(64, 64);
10794 if (!opaque)
10795 expected.fill(fillColor: Qt::transparent);
10796
10797 QPainter p(&expected);
10798 p.translate(dx: -64, dy: -64);
10799 p.drawTiledPixmap(x: 0, y: 0, w: 128, h: 128, pm: pal.brush(cr: QPalette::Window).texture(), sx: 0, sy: 0);
10800 p.end();
10801
10802 QPixmap actual = grabFromWidget(w: &widget, rect: QRect(64, 64, 64, 64));
10803 QVERIFY(lenientCompare(actual, expected));
10804
10805 actual = grabFromWidget(w: &widget, rect: QRect(64, 64, -1, -1));
10806 QVERIFY(lenientCompare(actual, expected));
10807
10808 // Make sure a widget that is not yet shown is grabbed correctly.
10809 QTreeWidget widget2;
10810 actual = widget2.grab(rectangle: QRect());
10811 widget2.show();
10812 expected = widget2.grab(rectangle: QRect());
10813
10814 QVERIFY(lenientCompare(actual, expected));
10815 }
10816}
10817
10818/* grabMouse() tests whether mouse grab for a widget without window handle works.
10819 * It creates a top level widget with another nested widget inside. The inner widget grabs
10820 * the mouse and a series of mouse presses moving over the top level's window is simulated.
10821 * Only the inner widget should receive events. */
10822
10823static inline QString mouseEventLogEntry(const QString &objectName, QEvent::Type t, const QPoint &p, Qt::MouseButtons b)
10824{
10825 QString result;
10826 QDebug(&result).nospace() << objectName << " Mouse event " << t << " at " << p << " buttons " << b;
10827 return result;
10828}
10829
10830class GrabLoggerWidget : public QWidget
10831{
10832public:
10833 explicit GrabLoggerWidget(QStringList *log, QWidget *parent = nullptr) : QWidget(parent), m_log(log) {}
10834
10835protected:
10836 bool event(QEvent *e) override
10837 {
10838 switch (e->type()) {
10839 case QEvent::MouseButtonPress:
10840 case QEvent::MouseMove:
10841 case QEvent::MouseButtonRelease: {
10842 QMouseEvent *me = static_cast<QMouseEvent *>(e);
10843 m_log->push_back(t: mouseEventLogEntry(objectName: objectName(), t: me->type(), p: me->pos(), b: me->buttons()));
10844 me->accept();
10845 return true;
10846 }
10847 default:
10848 break;
10849 }
10850 return QWidget::event(event: e);
10851 }
10852private:
10853 QStringList *m_log;
10854};
10855
10856void tst_QWidget::grabMouse()
10857{
10858 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
10859 QSKIP("Wayland: This fails. Figure out why.");
10860
10861 QStringList log;
10862 GrabLoggerWidget w(&log);
10863 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10864 w.setObjectName(QLatin1String("tst_qwidget_grabMouse"));
10865 w.setWindowTitle(w.objectName());
10866 QLayout *layout = new QVBoxLayout(&w);
10867 layout->setContentsMargins(left: 50, top: 50, right: 50, bottom: 50);
10868 GrabLoggerWidget *grabber = new GrabLoggerWidget(&log, &w);
10869 const QString grabberObjectName = QLatin1String("tst_qwidget_grabMouse_grabber");
10870 grabber->setObjectName(grabberObjectName);
10871 grabber->setMinimumSize(m_testWidgetSize);
10872 layout->addWidget(w: grabber);
10873 centerOnScreen(w: &w);
10874 w.show();
10875 QApplication::setActiveWindow(&w);
10876 QVERIFY(QTest::qWaitForWindowActive(&w));
10877
10878 QStringList expectedLog;
10879 QPoint mousePos = QPoint(w.width() / 2, 10);
10880 QTest::mouseMove(window: w.windowHandle(), pos: mousePos);
10881 grabber->grabMouse();
10882 const int step = w.height() / 5;
10883 for ( ; mousePos.y() < w.height() ; mousePos.ry() += step) {
10884 QTest::mouseClick(window: w.windowHandle(), button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: mousePos);
10885 // Events should go to the grabber child using its coordinates.
10886 const QPoint expectedPos = grabber->mapFromParent(mousePos);
10887 expectedLog.push_back(t: mouseEventLogEntry(objectName: grabberObjectName, t: QEvent::MouseButtonPress, p: expectedPos, b: Qt::LeftButton));
10888 expectedLog.push_back(t: mouseEventLogEntry(objectName: grabberObjectName, t: QEvent::MouseButtonRelease, p: expectedPos, b: Qt::NoButton));
10889 }
10890 grabber->releaseMouse();
10891 QCOMPARE(log, expectedLog);
10892}
10893
10894void tst_QWidget::grabKeyboard()
10895{
10896 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
10897 QSKIP("Wayland: This fails. Figure out why.");
10898
10899 QWidget w;
10900 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10901 w.setObjectName(QLatin1String("tst_qwidget_grabKeyboard"));
10902 w.setWindowTitle(w.objectName());
10903 QLayout *layout = new QVBoxLayout(&w);
10904 QLineEdit *grabber = new QLineEdit(&w);
10905 grabber->setMinimumWidth(m_testWidgetSize.width());
10906 layout->addWidget(w: grabber);
10907 QLineEdit *nonGrabber = new QLineEdit(&w);
10908 nonGrabber->setMinimumWidth(m_testWidgetSize.width());
10909 layout->addWidget(w: nonGrabber);
10910 centerOnScreen(w: &w);
10911 w.show();
10912 QApplication::setActiveWindow(&w);
10913 QVERIFY(QTest::qWaitForWindowActive(&w));
10914 nonGrabber->setFocus();
10915 grabber->grabKeyboard();
10916 QTest::keyClick(window: w.windowHandle(), key: Qt::Key_A);
10917 grabber->releaseKeyboard();
10918 QCOMPARE(grabber->text().toLower(), QStringLiteral("a"));
10919 QVERIFY2(nonGrabber->text().isEmpty(), qPrintable(nonGrabber->text()));
10920}
10921
10922class TouchMouseWidget : public QWidget {
10923public:
10924 explicit TouchMouseWidget(QWidget *parent = nullptr) : QWidget(parent)
10925 {
10926 resize(w: 200, h: 200);
10927 }
10928
10929 void setAcceptTouch(bool accept)
10930 {
10931 m_acceptTouch = accept;
10932 setAttribute(Qt::WA_AcceptTouchEvents, on: accept);
10933 }
10934
10935 void setAcceptMouse(bool accept)
10936 {
10937 m_acceptMouse = accept;
10938 }
10939
10940protected:
10941 bool event(QEvent *e) override
10942 {
10943 switch (e->type()) {
10944 case QEvent::TouchBegin:
10945 case QEvent::TouchUpdate:
10946 case QEvent::TouchEnd:
10947 if (e->type() == QEvent::TouchBegin)
10948 ++m_touchBeginCount;
10949 else if (e->type() == QEvent::TouchUpdate)
10950 ++m_touchUpdateCount;
10951 else if (e->type() == QEvent::TouchEnd)
10952 ++m_touchEndCount;
10953 ++m_touchEventCount;
10954 if (m_acceptTouch)
10955 e->accept();
10956 else
10957 e->ignore();
10958 return true;
10959 case QEvent::Gesture:
10960 ++m_gestureEventCount;
10961 return true;
10962
10963 case QEvent::MouseButtonPress:
10964 case QEvent::MouseMove:
10965 case QEvent::MouseButtonRelease:
10966 ++m_mouseEventCount;
10967 m_lastMouseEventPos = static_cast<QMouseEvent *>(e)->localPos();
10968 if (m_acceptMouse)
10969 e->accept();
10970 else
10971 e->ignore();
10972 return true;
10973
10974 default:
10975 return QWidget::event(event: e);
10976 }
10977 }
10978
10979public:
10980 int m_touchBeginCount = 0;
10981 int m_touchUpdateCount = 0;
10982 int m_touchEndCount = 0;
10983 int m_touchEventCount = 0;
10984 int m_gestureEventCount = 0;
10985 bool m_acceptTouch = false;
10986 int m_mouseEventCount = 0;
10987 bool m_acceptMouse = true;
10988 QPointF m_lastMouseEventPos;
10989};
10990
10991void tst_QWidget::touchEventSynthesizedMouseEvent()
10992{
10993 {
10994 // Simple case, we ignore the touch events, we get mouse events instead
10995 TouchMouseWidget widget;
10996 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
10997 widget.show();
10998 QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle()));
10999 QCOMPARE(widget.m_touchEventCount, 0);
11000 QCOMPARE(widget.m_mouseEventCount, 0);
11001
11002 QTest::touchEvent(widget: &widget, device: m_touchScreen).press(touchId: 0, pt: QPoint(10, 10), widget: &widget);
11003 QCOMPARE(widget.m_touchEventCount, 0);
11004 QCOMPARE(widget.m_mouseEventCount, 1);
11005 QCOMPARE(widget.m_lastMouseEventPos, QPointF(10, 10));
11006 QTest::touchEvent(widget: &widget, device: m_touchScreen).move(touchId: 0, pt: QPoint(15, 15), widget: &widget);
11007 QCOMPARE(widget.m_touchEventCount, 0);
11008 QCOMPARE(widget.m_mouseEventCount, 2);
11009 QCOMPARE(widget.m_lastMouseEventPos, QPointF(15, 15));
11010 QTest::touchEvent(widget: &widget, device: m_touchScreen).release(touchId: 0, pt: QPoint(20, 20), widget: &widget);
11011 QCOMPARE(widget.m_touchEventCount, 0);
11012 QCOMPARE(widget.m_mouseEventCount, 4); // we receive extra mouse move event
11013 QCOMPARE(widget.m_lastMouseEventPos, QPointF(20, 20));
11014 }
11015
11016 {
11017 // We accept the touch events, no mouse event is generated
11018 TouchMouseWidget widget;
11019 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11020 widget.setAcceptTouch(true);
11021 widget.show();
11022 QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle()));
11023 QCOMPARE(widget.m_touchEventCount, 0);
11024 QCOMPARE(widget.m_mouseEventCount, 0);
11025
11026 QTest::touchEvent(widget: &widget, device: m_touchScreen).press(touchId: 0, pt: QPoint(10, 10), widget: &widget);
11027 QCOMPARE(widget.m_touchEventCount, 1);
11028 QCOMPARE(widget.m_mouseEventCount, 0);
11029 QTest::touchEvent(widget: &widget, device: m_touchScreen).move(touchId: 0, pt: QPoint(15, 15), widget: &widget);
11030 QCOMPARE(widget.m_touchEventCount, 2);
11031 QCOMPARE(widget.m_mouseEventCount, 0);
11032 QTest::touchEvent(widget: &widget, device: m_touchScreen).release(touchId: 0, pt: QPoint(20, 20), widget: &widget);
11033 QCOMPARE(widget.m_touchEventCount, 3);
11034 QCOMPARE(widget.m_mouseEventCount, 0);
11035 }
11036
11037 {
11038 // Parent accepts touch events, child ignore both mouse and touch
11039 // We should see propagation of the TouchBegin
11040 TouchMouseWidget parent;
11041 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11042 parent.setAcceptTouch(true);
11043 TouchMouseWidget child(&parent);
11044 child.move(ax: 5, ay: 5);
11045 child.setAcceptMouse(false);
11046 parent.show();
11047 QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle()));
11048 QCOMPARE(parent.m_touchEventCount, 0);
11049 QCOMPARE(parent.m_mouseEventCount, 0);
11050 QCOMPARE(child.m_touchEventCount, 0);
11051 QCOMPARE(child.m_mouseEventCount, 0);
11052
11053 QTest::touchEvent(widget: parent.window(), device: m_touchScreen).press(touchId: 0, pt: QPoint(10, 10), widget: &child);
11054 QCOMPARE(parent.m_touchEventCount, 1);
11055 QCOMPARE(parent.m_mouseEventCount, 0);
11056 QCOMPARE(child.m_touchEventCount, 0);
11057 QCOMPARE(child.m_mouseEventCount, 0);
11058 }
11059
11060 {
11061 // Parent accepts mouse events, child ignore both mouse and touch
11062 // We should see propagation of the TouchBegin into a MouseButtonPress
11063 TouchMouseWidget parent;
11064 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11065 TouchMouseWidget child(&parent);
11066 const QPoint childPos(5, 5);
11067 child.move(childPos);
11068 child.setAcceptMouse(false);
11069 parent.show();
11070 QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle()));
11071 QCOMPARE(parent.m_touchEventCount, 0);
11072 QCOMPARE(parent.m_mouseEventCount, 0);
11073 QCOMPARE(child.m_touchEventCount, 0);
11074 QCOMPARE(child.m_mouseEventCount, 0);
11075
11076 const QPoint touchPos(20, 20);
11077 QTest::touchEvent(widget: parent.window(), device: m_touchScreen).press(touchId: 0, pt: touchPos, widget: &child);
11078 QCOMPARE(parent.m_touchEventCount, 0);
11079 QCOMPARE(parent.m_mouseEventCount, 1);
11080 QCOMPARE(parent.m_lastMouseEventPos, childPos + touchPos);
11081 QCOMPARE(child.m_touchEventCount, 0);
11082 QCOMPARE(child.m_mouseEventCount, 1); // Attempt at mouse event before propagation
11083 QCOMPARE(child.m_lastMouseEventPos, touchPos);
11084 }
11085}
11086
11087void tst_QWidget::touchUpdateOnNewTouch()
11088{
11089 TouchMouseWidget widget;
11090 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11091 widget.setAcceptTouch(true);
11092 QVBoxLayout *layout = new QVBoxLayout;
11093 layout->addWidget(new QWidget);
11094 widget.setLayout(layout);
11095 widget.show();
11096
11097 QWindow* window = widget.windowHandle();
11098 QVERIFY(QTest::qWaitForWindowExposed(window));
11099 QCOMPARE(widget.m_touchBeginCount, 0);
11100 QCOMPARE(widget.m_touchUpdateCount, 0);
11101 QCOMPARE(widget.m_touchEndCount, 0);
11102 QTest::touchEvent(window, device: m_touchScreen).press(touchId: 0, pt: QPoint(20, 20), window);
11103 QCOMPARE(widget.m_touchBeginCount, 1);
11104 QCOMPARE(widget.m_touchUpdateCount, 0);
11105 QCOMPARE(widget.m_touchEndCount, 0);
11106 QTest::touchEvent(window, device: m_touchScreen).move(touchId: 0, pt: QPoint(25, 25), window);
11107 QCOMPARE(widget.m_touchBeginCount, 1);
11108 QCOMPARE(widget.m_touchUpdateCount, 1);
11109 QCOMPARE(widget.m_touchEndCount, 0);
11110 QTest::touchEvent(window, device: m_touchScreen).stationary(touchId: 0).press(touchId: 1, pt: QPoint(40, 40), window);
11111 QCOMPARE(widget.m_touchBeginCount, 1);
11112 QCOMPARE(widget.m_touchUpdateCount, 2);
11113 QCOMPARE(widget.m_touchEndCount, 0);
11114 QTest::touchEvent(window, device: m_touchScreen).stationary(touchId: 1).release(touchId: 0, pt: QPoint(25, 25), window);
11115 QCOMPARE(widget.m_touchBeginCount, 1);
11116 QCOMPARE(widget.m_touchUpdateCount, 3);
11117 QCOMPARE(widget.m_touchEndCount, 0);
11118 QTest::touchEvent(window, device: m_touchScreen).release(touchId: 1, pt: QPoint(40, 40), window);
11119 QCOMPARE(widget.m_touchBeginCount, 1);
11120 QCOMPARE(widget.m_touchUpdateCount, 3);
11121 QCOMPARE(widget.m_touchEndCount, 1);
11122}
11123
11124void tst_QWidget::touchEventsForGesturePendingWidgets()
11125{
11126 TouchMouseWidget parent;
11127 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11128 TouchMouseWidget child(&parent);
11129 parent.grabGesture(type: Qt::TapAndHoldGesture);
11130 parent.show();
11131
11132 QWindow* window = parent.windowHandle();
11133 QVERIFY(QTest::qWaitForWindowExposed(window));
11134 QTest::qWait(ms: 500); // needed for QApplication::topLevelAt(), which is used by QGestureManager
11135 QCOMPARE(child.m_touchEventCount, 0);
11136 QCOMPARE(child.m_gestureEventCount, 0);
11137 QCOMPARE(parent.m_touchEventCount, 0);
11138 QCOMPARE(parent.m_gestureEventCount, 0);
11139 QTest::touchEvent(window, device: m_touchScreen).press(touchId: 0, pt: QPoint(20, 20), window);
11140 QCOMPARE(child.m_touchEventCount, 0);
11141 QCOMPARE(child.m_gestureEventCount, 0);
11142 QCOMPARE(parent.m_touchBeginCount, 1); // QTapAndHoldGestureRecognizer::create() sets Qt::WA_AcceptTouchEvents
11143 QCOMPARE(parent.m_touchUpdateCount, 0);
11144 QCOMPARE(parent.m_touchEndCount, 0);
11145 QCOMPARE(parent.m_gestureEventCount, 0);
11146 QTest::touchEvent(window, device: m_touchScreen).move(touchId: 0, pt: QPoint(25, 25), window);
11147 QCOMPARE(child.m_touchEventCount, 0);
11148 QCOMPARE(child.m_gestureEventCount, 0);
11149 QCOMPARE(parent.m_touchBeginCount, 1);
11150 QCOMPARE(parent.m_touchUpdateCount, 0);
11151 QCOMPARE(parent.m_touchEndCount, 0);
11152 QCOMPARE(parent.m_gestureEventCount, 0);
11153 QTest::qWait(ms: 1000);
11154 QTest::touchEvent(window, device: m_touchScreen).release(touchId: 0, pt: QPoint(25, 25), window);
11155 QCOMPARE(child.m_touchEventCount, 0);
11156 QCOMPARE(child.m_gestureEventCount, 0);
11157 QCOMPARE(parent.m_touchBeginCount, 1);
11158 QCOMPARE(parent.m_touchUpdateCount, 0);
11159 QCOMPARE(parent.m_touchEndCount, 0);
11160 QVERIFY(parent.m_gestureEventCount > 0);
11161}
11162
11163void tst_QWidget::styleSheetPropagation()
11164{
11165 QTableView tw;
11166 tw.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11167 tw.setStyleSheet("background-color: red;");
11168 for (QObject *child : tw.children()) {
11169 if (QWidget *w = qobject_cast<QWidget *>(o: child))
11170 QCOMPARE(w->style(), tw.style());
11171 }
11172}
11173
11174class DestroyTester : public QObject
11175{
11176 Q_OBJECT
11177public:
11178 explicit DestroyTester(QObject *parent = nullptr) : QObject(parent) { parentDestroyed = 0; }
11179 static int parentDestroyed;
11180public slots:
11181 void parentDestroyedSlot() {
11182 ++parentDestroyed;
11183 }
11184};
11185
11186int DestroyTester::parentDestroyed = 0;
11187
11188void tst_QWidget::destroyedSignal()
11189{
11190 {
11191 QWidget *w = new QWidget;
11192 DestroyTester *t = new DestroyTester(w);
11193 connect(sender: w, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11194 QCOMPARE(DestroyTester::parentDestroyed, 0);
11195 delete w;
11196 QCOMPARE(DestroyTester::parentDestroyed, 1);
11197 }
11198
11199 {
11200 QWidget *w = new QWidget;
11201 DestroyTester *t = new DestroyTester(w);
11202 connect(sender: w, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11203 w->blockSignals(b: true);
11204 QCOMPARE(DestroyTester::parentDestroyed, 0);
11205 delete w;
11206 QCOMPARE(DestroyTester::parentDestroyed, 1);
11207 }
11208
11209 {
11210 QObject *o = new QWidget;
11211 DestroyTester *t = new DestroyTester(o);
11212 connect(sender: o, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11213 QCOMPARE(DestroyTester::parentDestroyed, 0);
11214 delete o;
11215 QCOMPARE(DestroyTester::parentDestroyed, 1);
11216 }
11217
11218 {
11219 QObject *o = new QWidget;
11220 auto t = new DestroyTester;
11221 connect(sender: o, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11222 o->blockSignals(b: true);
11223 QCOMPARE(DestroyTester::parentDestroyed, 0);
11224 delete o;
11225 QCOMPARE(DestroyTester::parentDestroyed, 1);
11226 }
11227
11228 {
11229 QWidget *w = new QWidget;
11230 auto t = new DestroyTester;
11231 connect(sender: w, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11232 QCOMPARE(DestroyTester::parentDestroyed, 0);
11233 delete w;
11234 QCOMPARE(DestroyTester::parentDestroyed, 1);
11235 delete t;
11236 }
11237
11238 {
11239 QWidget *w = new QWidget;
11240 auto t = new DestroyTester;
11241 connect(sender: w, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11242 w->blockSignals(b: true);
11243 QCOMPARE(DestroyTester::parentDestroyed, 0);
11244 delete w;
11245 QCOMPARE(DestroyTester::parentDestroyed, 1);
11246 delete t;
11247 }
11248
11249 {
11250 QObject *o = new QWidget;
11251 auto t = new DestroyTester;
11252 connect(sender: o, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11253 QCOMPARE(DestroyTester::parentDestroyed, 0);
11254 delete o;
11255 QCOMPARE(DestroyTester::parentDestroyed, 1);
11256 delete t;
11257 }
11258
11259 {
11260 QObject *o = new QWidget;
11261 auto t = new DestroyTester;
11262 connect(sender: o, signal: &QObject::destroyed, receiver: t, slot: &DestroyTester::parentDestroyedSlot);
11263 o->blockSignals(b: true);
11264 QCOMPARE(DestroyTester::parentDestroyed, 0);
11265 delete o;
11266 QCOMPARE(DestroyTester::parentDestroyed, 1);
11267 delete t;
11268 }
11269
11270}
11271
11272#ifndef QT_NO_CURSOR
11273void tst_QWidget::underMouse()
11274{
11275 // Move the mouse cursor to a safe location
11276 QCursor::setPos(m_safeCursorPos);
11277
11278 ColorWidget topLevelWidget(nullptr, Qt::FramelessWindowHint, Qt::blue);
11279 topLevelWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11280 ColorWidget childWidget1(&topLevelWidget, Qt::Widget, Qt::yellow);
11281 ColorWidget childWidget2(&topLevelWidget, Qt::Widget, Qt::black);
11282 ColorWidget popupWidget(nullptr, Qt::Popup, Qt::green);
11283
11284 topLevelWidget.setObjectName("topLevelWidget");
11285 childWidget1.setObjectName("childWidget1");
11286 childWidget2.setObjectName("childWidget2");
11287 popupWidget.setObjectName("popupWidget");
11288
11289 topLevelWidget.setGeometry(ax: 100, ay: 100, aw: 300, ah: 300);
11290 childWidget1.setGeometry(ax: 20, ay: 20, aw: 100, ah: 100);
11291 childWidget2.setGeometry(ax: 20, ay: 120, aw: 100, ah: 100);
11292 popupWidget.setGeometry(ax: 50, ay: 100, aw: 50, ah: 50);
11293
11294 topLevelWidget.show();
11295 QVERIFY(QTest::qWaitForWindowExposed(&topLevelWidget));
11296 QWindow *window = topLevelWidget.windowHandle();
11297
11298 QPoint outsideWindowPoint(30, -10);
11299 QPoint inWindowPoint(30, 10);
11300 QPoint child1Point(30, 50);
11301 QPoint child2PointA(30, 150);
11302 QPoint child2PointB(31, 151);
11303
11304 // Outside window
11305 QTest::mouseMove(window, pos: outsideWindowPoint);
11306 QVERIFY(!topLevelWidget.underMouse());
11307 QVERIFY(!childWidget1.underMouse());
11308 QVERIFY(!childWidget2.underMouse());
11309
11310 // Enter window, outside children
11311 // Note: QTest::mouseMove will not generate enter events for windows, so send one explicitly
11312 QWindowSystemInterface::handleEnterEvent(window, local: inWindowPoint, global: window->mapToGlobal(pos: inWindowPoint));
11313 QTest::mouseMove(window, pos: inWindowPoint);
11314 QVERIFY(topLevelWidget.underMouse());
11315 QVERIFY(!childWidget1.underMouse());
11316 QVERIFY(!childWidget2.underMouse());
11317
11318 // In childWidget1
11319 QTest::mouseMove(window, pos: child1Point);
11320 QVERIFY(topLevelWidget.underMouse());
11321 QVERIFY(childWidget1.underMouse());
11322 QVERIFY(!childWidget2.underMouse());
11323
11324 // In childWidget2
11325 QTest::mouseMove(window, pos: child2PointA);
11326 QVERIFY(topLevelWidget.underMouse());
11327 QVERIFY(!childWidget1.underMouse());
11328 QVERIFY(childWidget2.underMouse());
11329
11330 topLevelWidget.resetCounts();
11331 childWidget1.resetCounts();
11332 childWidget2.resetCounts();
11333 popupWidget.resetCounts();
11334
11335 // Throw up a popup window
11336 popupWidget.show();
11337 QVERIFY(QTest::qWaitForWindowExposed(&popupWidget));
11338 QWindow *popupWindow = popupWidget.windowHandle();
11339 QVERIFY(popupWindow);
11340 QCOMPARE(QApplication::activePopupWidget(), &popupWidget);
11341
11342 // Send an artificial leave event for window, as it won't get generated automatically
11343 // due to cursor not actually being over the window.
11344 QWindowSystemInterface::handleLeaveEvent(window);
11345 QApplication::processEvents();
11346
11347 // If there is an active popup, undermouse should not be reported (QTBUG-27478),
11348 // but opening a popup causes leave for widgets under mouse.
11349 QVERIFY(!topLevelWidget.underMouse());
11350 QVERIFY(!childWidget1.underMouse());
11351 QVERIFY(!childWidget2.underMouse());
11352 QVERIFY(!popupWidget.underMouse());
11353 QCOMPARE(popupWidget.enters, 0);
11354 QCOMPARE(popupWidget.leaves, 0);
11355 QCOMPARE(topLevelWidget.enters, 0);
11356 QCOMPARE(topLevelWidget.leaves, 1);
11357 QCOMPARE(childWidget1.enters, 0);
11358 QCOMPARE(childWidget1.leaves, 0);
11359 QCOMPARE(childWidget2.enters, 0);
11360 QCOMPARE(childWidget2.leaves, 1);
11361 topLevelWidget.resetCounts();
11362 childWidget2.resetCounts();
11363
11364 // Moving around while popup active should not change undermouse or cause
11365 // enter and leave events for widgets.
11366 QTest::mouseMove(window: popupWindow, pos: popupWindow->mapFromGlobal(pos: window->mapToGlobal(pos: child2PointB)));
11367 QVERIFY(!topLevelWidget.underMouse());
11368 QVERIFY(!childWidget1.underMouse());
11369 QVERIFY(!childWidget2.underMouse());
11370 QVERIFY(!popupWidget.underMouse());
11371 QCOMPARE(popupWidget.enters, 0);
11372 QCOMPARE(popupWidget.leaves, 0);
11373 QCOMPARE(topLevelWidget.enters, 0);
11374 QCOMPARE(topLevelWidget.leaves, 0);
11375 QCOMPARE(childWidget1.enters, 0);
11376 QCOMPARE(childWidget1.leaves, 0);
11377 QCOMPARE(childWidget2.enters, 0);
11378 QCOMPARE(childWidget2.leaves, 0);
11379
11380 QTest::mouseMove(window: popupWindow, pos: popupWindow->mapFromGlobal(pos: window->mapToGlobal(pos: inWindowPoint)));
11381 QVERIFY(!topLevelWidget.underMouse());
11382 QVERIFY(!childWidget1.underMouse());
11383 QVERIFY(!childWidget2.underMouse());
11384 QVERIFY(!popupWidget.underMouse());
11385 QCOMPARE(popupWidget.enters, 0);
11386 QCOMPARE(popupWidget.leaves, 0);
11387 QCOMPARE(topLevelWidget.enters, 0);
11388 QCOMPARE(topLevelWidget.leaves, 0);
11389 QCOMPARE(childWidget1.enters, 0);
11390 QCOMPARE(childWidget1.leaves, 0);
11391 QCOMPARE(childWidget2.enters, 0);
11392 QCOMPARE(childWidget2.leaves, 0);
11393
11394 QTest::mouseMove(window: popupWindow, pos: popupWindow->mapFromGlobal(pos: window->mapToGlobal(pos: child1Point)));
11395 QVERIFY(!topLevelWidget.underMouse());
11396 QVERIFY(!childWidget1.underMouse());
11397 QVERIFY(!childWidget2.underMouse());
11398 QVERIFY(!popupWidget.underMouse());
11399 QCOMPARE(popupWidget.enters, 0);
11400 QCOMPARE(popupWidget.leaves, 0);
11401 QCOMPARE(topLevelWidget.enters, 0);
11402 QCOMPARE(topLevelWidget.leaves, 0);
11403 QCOMPARE(childWidget1.enters, 0);
11404 QCOMPARE(childWidget1.leaves, 0);
11405 QCOMPARE(childWidget2.enters, 0);
11406 QCOMPARE(childWidget2.leaves, 0);
11407
11408 // Note: Mouse moving off-application while there is an active popup cannot be simulated
11409 // without actually moving the cursor so it is not tested.
11410
11411 // Mouse enters popup, should cause enter to popup.
11412 // Once again, need to create artificial enter event.
11413 const QPoint popupCenter = popupWindow->geometry().center();
11414 QWindowSystemInterface::handleEnterEvent(window: popupWindow, local: popupWindow->mapFromGlobal(pos: popupCenter), global: popupCenter);
11415 QTest::mouseMove(window: popupWindow, pos: popupCenter);
11416 QVERIFY(!topLevelWidget.underMouse());
11417 QVERIFY(!childWidget1.underMouse());
11418 QVERIFY(!childWidget2.underMouse());
11419 QVERIFY(popupWidget.underMouse());
11420 QCOMPARE(popupWidget.enters, 1);
11421 QCOMPARE(popupWidget.leaves, 0);
11422 QCOMPARE(topLevelWidget.enters, 0);
11423 QCOMPARE(topLevelWidget.leaves, 0);
11424 QCOMPARE(childWidget1.enters, 0);
11425 QCOMPARE(childWidget1.leaves, 0);
11426 QCOMPARE(childWidget2.enters, 0);
11427 QCOMPARE(childWidget2.leaves, 0);
11428 popupWidget.resetCounts();
11429
11430 // Mouse moves around inside popup, no changes
11431 QTest::mouseMove(window: popupWindow, pos: QPoint(5, 5));
11432 QVERIFY(!topLevelWidget.underMouse());
11433 QVERIFY(!childWidget1.underMouse());
11434 QVERIFY(!childWidget2.underMouse());
11435 QVERIFY(popupWidget.underMouse());
11436 QCOMPARE(popupWidget.enters, 0);
11437 QCOMPARE(popupWidget.leaves, 0);
11438 QCOMPARE(topLevelWidget.enters, 0);
11439 QCOMPARE(topLevelWidget.leaves, 0);
11440 QCOMPARE(childWidget1.enters, 0);
11441 QCOMPARE(childWidget1.leaves, 0);
11442 QCOMPARE(childWidget2.enters, 0);
11443 QCOMPARE(childWidget2.leaves, 0);
11444
11445 // Mouse leaves popup and enters topLevelWidget, should cause leave for popup
11446 // but no enter to topLevelWidget.
11447#ifdef Q_OS_DARWIN
11448 // Artificial leave event needed for Cocoa.
11449 QWindowSystemInterface::handleLeaveEvent(popupWindow);
11450#endif
11451 QTest::mouseMove(window: popupWindow, pos: popupWindow->mapFromGlobal(pos: window->mapToGlobal(pos: inWindowPoint)));
11452 QApplication::processEvents();
11453 QVERIFY(!topLevelWidget.underMouse());
11454 QVERIFY(!childWidget1.underMouse());
11455 QVERIFY(!childWidget2.underMouse());
11456 QVERIFY(!popupWidget.underMouse());
11457 QCOMPARE(popupWidget.enters, 0);
11458 QCOMPARE(popupWidget.leaves, 1);
11459 QCOMPARE(topLevelWidget.enters, 0);
11460 QCOMPARE(topLevelWidget.leaves, 0);
11461 QCOMPARE(childWidget1.enters, 0);
11462 QCOMPARE(childWidget1.leaves, 0);
11463 QCOMPARE(childWidget2.enters, 0);
11464 QCOMPARE(childWidget2.leaves, 0);
11465}
11466
11467class EnterTestModalDialog : public QDialog
11468{
11469 Q_OBJECT
11470public:
11471 EnterTestModalDialog()
11472 {
11473 setGeometry(ax: 100, ay: 300, aw: 150, ah: 100);
11474 button = new QPushButton(this);
11475 button->setGeometry(ax: 10, ay: 10, aw: 50, ah: 30);
11476 }
11477
11478 QPushButton *button;
11479};
11480
11481class EnterTestMainDialog : public QDialog
11482{
11483 Q_OBJECT
11484public:
11485
11486public slots:
11487 void buttonPressed()
11488 {
11489 qApp->installEventFilter(filterObj: this);
11490 modal = new EnterTestModalDialog();
11491 QTimer::singleShot(msec: 2000, receiver: modal, SLOT(close())); // Failsafe
11492 QTimer::singleShot(msec: 100, receiver: this, SLOT(doMouseMoves()));
11493 modal->exec();
11494 delete modal;
11495 modal = nullptr;
11496 }
11497
11498 void doMouseMoves()
11499 {
11500 QPoint point1(15, 15);
11501 QPoint point2(15, 20);
11502 QPoint point3(20, 20);
11503 QWindow *window = modal->windowHandle();
11504 const QPoint nativePoint1 = QHighDpi::toNativePixels(value: point1, context: window->screen());
11505 QWindowSystemInterface::handleEnterEvent(window, local: nativePoint1);
11506 QTest::mouseMove(window, pos: point1);
11507 QTest::mouseMove(window, pos: point2);
11508 QTest::mouseMove(window, pos: point3);
11509 modal->close();
11510 }
11511
11512 bool eventFilter(QObject *o, QEvent *e) override
11513 {
11514 switch (e->type()) {
11515 case QEvent::Enter:
11516 if (modal && modal->button && o == modal->button)
11517 enters++;
11518 break;
11519 default:
11520 break;
11521 }
11522 return QDialog::eventFilter(o, e);
11523 }
11524
11525public:
11526 EnterTestModalDialog *modal = nullptr;
11527 int enters = 0;
11528};
11529
11530// A modal dialog launched by clicking a button should not trigger excess enter events
11531// when mousing over it.
11532void tst_QWidget::taskQTBUG_27643_enterEvents()
11533{
11534#ifdef Q_OS_MACOS
11535 QSKIP("QTBUG-52974: this test can crash!");
11536#endif
11537 // Move the mouse cursor to a safe location so it won't interfere
11538 QCursor::setPos(m_safeCursorPos);
11539
11540 EnterTestMainDialog dialog;
11541 dialog.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11542 QPushButton button(&dialog);
11543
11544 connect(sender: &button, signal: &QAbstractButton::clicked, receiver: &dialog, slot: &EnterTestMainDialog::buttonPressed);
11545
11546 dialog.setGeometry(ax: 100, ay: 100, aw: 150, ah: 100);
11547 button.setGeometry(ax: 10, ay: 10, aw: 100, ah: 50);
11548 dialog.show();
11549 QVERIFY(QTest::qWaitForWindowExposed(&dialog));
11550
11551 QWindow *window = dialog.windowHandle();
11552 QPoint overButton(25, 25);
11553
11554 QWindowSystemInterface::handleEnterEvent(window, local: overButton, global: window->mapToGlobal(pos: overButton));
11555 QTest::mouseMove(window, pos: overButton);
11556 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: overButton, delay: 0);
11557
11558 // Modal dialog opened in EnterTestMainDialog::buttonPressed()...
11559
11560 // Must only register only single enter on modal dialog's button after all said and done
11561 QCOMPARE(dialog.enters, 1);
11562}
11563#endif // QT_NO_CURSOR
11564
11565class KeyboardWidget : public QWidget
11566{
11567public:
11568 using QWidget::QWidget;
11569 void mousePressEvent(QMouseEvent* ev) override
11570 {
11571 m_modifiers = ev->modifiers();
11572 m_appModifiers = QApplication::keyboardModifiers();
11573 ++m_eventCounter;
11574 }
11575 Qt::KeyboardModifiers m_modifiers;
11576 Qt::KeyboardModifiers m_appModifiers;
11577 int m_eventCounter = 0;
11578};
11579
11580void tst_QWidget::keyboardModifiers()
11581{
11582 KeyboardWidget w;
11583 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11584 w.resize(w: 300, h: 300);
11585 w.show();
11586 QVERIFY(QTest::qWaitForWindowExposed(&w));
11587 QTest::mouseClick(widget: &w, button: Qt::LeftButton, stateKey: Qt::ControlModifier);
11588 QCOMPARE(w.m_eventCounter, 1);
11589 QCOMPARE(int(w.m_modifiers), int(Qt::ControlModifier));
11590 QCOMPARE(int(w.m_appModifiers), int(Qt::ControlModifier));
11591}
11592
11593class DClickWidget : public QWidget
11594{
11595public:
11596 void mouseDoubleClickEvent(QMouseEvent *) override
11597 {
11598 triggered = true;
11599 }
11600 bool triggered = false;
11601};
11602
11603void tst_QWidget::mouseDoubleClickBubbling_QTBUG29680()
11604{
11605 DClickWidget parent;
11606 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11607 QWidget child(&parent);
11608 parent.resize(w: 200, h: 200);
11609 child.resize(w: 200, h: 200);
11610 parent.show();
11611 QVERIFY(QTest::qWaitForWindowExposed(&parent));
11612
11613 QTest::mouseDClick(widget: &child, button: Qt::LeftButton);
11614
11615 QTRY_VERIFY(parent.triggered);
11616}
11617
11618void tst_QWidget::largerThanScreen_QTBUG30142()
11619{
11620 QWidget widget;
11621 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11622 widget.resize(w: 200, h: 4000);
11623 widget.show();
11624 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11625 QVERIFY2(widget.frameGeometry().y() >= 0,
11626 msgComparisonFailed(widget.frameGeometry().y(), " >=", 0));
11627
11628 QWidget widget2;
11629 widget2.resize(w: 10000, h: 400);
11630 widget2.show();
11631 QVERIFY(QTest::qWaitForWindowExposed(&widget2));
11632 QVERIFY2(widget2.frameGeometry().x() >= 0,
11633 msgComparisonFailed(widget.frameGeometry().x(), " >=", 0));
11634}
11635
11636void tst_QWidget::resizeStaticContentsChildWidget_QTBUG35282()
11637{
11638 QWidget widget;
11639 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11640 widget.resize(w: 200,h: 200);
11641
11642 UpdateWidget childWidget(&widget);
11643 childWidget.setAttribute(Qt::WA_StaticContents);
11644 childWidget.setAttribute(Qt::WA_OpaquePaintEvent);
11645 childWidget.setGeometry(ax: 250, ay: 250, aw: 500, ah: 500);
11646
11647 widget.showNormal();
11648 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11649 if (m_platform == QStringLiteral("winrt"))
11650 QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
11651 QCOMPARE(childWidget.numPaintEvents, 0);
11652 childWidget.reset();
11653
11654 widget.resize(w: 1000,h: 1000);
11655 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11656 QGuiApplication::sync();
11657 QVERIFY2(childWidget.numPaintEvents >= 1,
11658 msgComparisonFailed(childWidget.numPaintEvents, ">=", 1));
11659}
11660
11661void tst_QWidget::qmlSetParentHelper()
11662{
11663#ifdef QT_BUILD_INTERNAL
11664 QWidget parent;
11665 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11666 QWidget child;
11667 QVERIFY(QAbstractDeclarativeData::setWidgetParent);
11668 QAbstractDeclarativeData::setWidgetParent(&child, &parent);
11669 QCOMPARE(child.parentWidget(), &parent);
11670 QAbstractDeclarativeData::setWidgetParent(&child, nullptr);
11671 QVERIFY(!child.parentWidget());
11672#else
11673 QSKIP("Needs QT_BUILD_INTERNAL");
11674#endif
11675}
11676
11677void tst_QWidget::testForOutsideWSRangeFlag()
11678{
11679 QSKIP("Test assumes QWindows can have 0x0 size, see QTBUG-61953");
11680
11681 // QTBUG-49445
11682 {
11683 QWidget widget;
11684 widget.resize(w: 0, h: 0);
11685 widget.show();
11686 QTest::qWait(ms: 100); // Wait for a while...
11687 QVERIFY(!widget.windowHandle()->isExposed()); // The window should not be visible
11688 QVERIFY(widget.isVisible()); // The widget should be in visible state
11689 }
11690 {
11691 QWidget widget;
11692
11693 QWidget native(&widget);
11694 native.setAttribute(Qt::WA_NativeWindow);
11695 native.resize(w: 0, h: 0);
11696
11697 widget.show();
11698 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11699 QVERIFY(!native.windowHandle()->isExposed());
11700 }
11701 {
11702 QWidget widget;
11703 QWidget native(&widget);
11704
11705 widget.show();
11706 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11707 QVERIFY(native.isVisible());
11708
11709 native.resize(w: 0, h: 0);
11710 native.setAttribute(Qt::WA_NativeWindow);
11711 QTest::qWait(ms: 100); // Wait for a while...
11712 QVERIFY(!native.windowHandle()->isExposed());
11713 }
11714
11715 // QTBUG-48321
11716 {
11717 QWidget widget;
11718
11719 QWidget native(&widget);
11720 native.setAttribute(Qt::WA_NativeWindow);
11721
11722 widget.show();
11723 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11724 QVERIFY(native.windowHandle()->isExposed());
11725
11726 native.resize(w: 0, h: 0);
11727 QTest::qWait(ms: 100); // Wait for a while...
11728 QVERIFY(!native.windowHandle()->isExposed());
11729 }
11730
11731 // QTBUG-51788
11732 {
11733 QWidget widget;
11734 widget.setLayout(new QGridLayout);
11735 widget.layout()->addWidget(w: new QLineEdit);
11736 widget.resize(w: 0, h: 0);
11737 widget.show();
11738 // The layout should change the size, so the widget must be visible!
11739 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11740 }
11741}
11742
11743class TabletWidget : public QWidget
11744{
11745public:
11746 TabletWidget(QWidget *parent) : QWidget(parent) { }
11747
11748 int tabletEventCount = 0;
11749 int pressEventCount = 0;
11750 int moveEventCount = 0;
11751 int releaseEventCount = 0;
11752 int trackingChangeEventCount = 0;
11753 qint64 uid = -1;
11754
11755protected:
11756 void tabletEvent(QTabletEvent *event) override {
11757 ++tabletEventCount;
11758 uid = event->uniqueId();
11759 switch (event->type()) {
11760 case QEvent::TabletMove:
11761 ++moveEventCount;
11762 break;
11763 case QEvent::TabletPress:
11764 ++pressEventCount;
11765 break;
11766 case QEvent::TabletRelease:
11767 ++releaseEventCount;
11768 break;
11769 default:
11770 break;
11771 }
11772 }
11773
11774 bool event(QEvent *ev) override {
11775 if (ev->type() == QEvent::TabletTrackingChange)
11776 ++trackingChangeEventCount;
11777 return QWidget::event(event: ev);
11778 }
11779};
11780
11781void tst_QWidget::tabletTracking()
11782{
11783 QWidget parent;
11784 parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
11785 parent.resize(w: 200,h: 200);
11786 // QWidgetWindow::handleTabletEvent doesn't deliver tablet events to the window's widget, only to a child.
11787 // So it doesn't do any good to show a TabletWidget directly: it needs a parent.
11788 TabletWidget widget(&parent);
11789 widget.resize(w: 200,h: 200);
11790 parent.showNormal();
11791 QVERIFY(QTest::qWaitForWindowExposed(&parent));
11792 widget.setAttribute(Qt::WA_TabletTracking);
11793 QTRY_COMPARE(widget.trackingChangeEventCount, 1);
11794 QVERIFY(widget.hasTabletTracking());
11795
11796 QWindow *window = parent.windowHandle();
11797 QPointF local(10, 10);
11798 QPointF global = window->mapToGlobal(pos: local.toPoint());
11799 QPointF deviceLocal = QHighDpi::toNativeLocalPosition(value: local, context: window);
11800 QPointF deviceGlobal = QHighDpi::toNativePixels(value: global, context: window->screen());
11801 qint64 uid = 1234UL;
11802
11803 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11804 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::NoButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11805 QCoreApplication::processEvents();
11806 QTRY_COMPARE(widget.moveEventCount, 1);
11807 QCOMPARE(widget.uid, uid);
11808
11809 local += QPoint(10, 10);
11810 deviceLocal += QPoint(10, 10);
11811 deviceGlobal += QPoint(10, 10);
11812 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11813 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::NoButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11814 QCoreApplication::processEvents();
11815 QTRY_COMPARE(widget.moveEventCount, 2);
11816
11817 widget.setTabletTracking(false);
11818 QCoreApplication::processEvents();
11819 QTRY_COMPARE(widget.trackingChangeEventCount, 2);
11820
11821 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11822 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::LeftButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11823 QCoreApplication::processEvents();
11824 QTRY_COMPARE(widget.pressEventCount, 1);
11825
11826 local += QPoint(10, 10);
11827 deviceLocal += QPoint(10, 10);
11828 deviceGlobal += QPoint(10, 10);
11829 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11830 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::LeftButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11831 QCoreApplication::processEvents();
11832 QTRY_COMPARE(widget.moveEventCount, 3);
11833
11834 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11835 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::NoButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11836 QCoreApplication::processEvents();
11837 QTRY_COMPARE(widget.releaseEventCount, 1);
11838
11839 local += QPoint(10, 10);
11840 deviceLocal += QPoint(10, 10);
11841 deviceGlobal += QPoint(10, 10);
11842 QWindowSystemInterface::handleTabletEvent(window, timestamp: ulong(QDateTime::currentMSecsSinceEpoch()), local: deviceLocal, global: deviceGlobal,
11843 device: QTabletEvent::Stylus, pointerType: QTabletEvent::Pen, buttons: Qt::NoButton, pressure: 0, xTilt: 0, yTilt: 0, tangentialPressure: 0, rotation: 0, z: 0, uid, modifiers: Qt::NoModifier);
11844 QCoreApplication::processEvents();
11845 QTRY_COMPARE(widget.moveEventCount, 3);
11846}
11847
11848class CloseCountingWidget : public QWidget
11849{
11850public:
11851 int closeCount = 0;
11852 void closeEvent(QCloseEvent *ev) override;
11853};
11854
11855void CloseCountingWidget::closeEvent(QCloseEvent *ev)
11856{
11857 ++closeCount;
11858 ev->accept();
11859}
11860
11861void tst_QWidget::closeEvent()
11862{
11863 CloseCountingWidget widget;
11864 widget.show();
11865 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11866 // Yes we call the close() function twice. This mimics the behavior of QTBUG-43344 where
11867 // QApplication first closes all windows and then QCocoaApplication flushes window system
11868 // events, triggering more close events.
11869 widget.windowHandle()->close();
11870 widget.windowHandle()->close();
11871 QCOMPARE(widget.closeCount, 1);
11872}
11873
11874void tst_QWidget::closeWithChildWindow()
11875{
11876 QWidget widget;
11877 auto childWidget = new QWidget(&widget);
11878 childWidget->setAttribute(Qt::WA_NativeWindow);
11879 childWidget->windowHandle()->create();
11880 widget.show();
11881 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11882 widget.windowHandle()->close();
11883 widget.show();
11884 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11885 // Check that the child window inside the window is now visible
11886 QVERIFY(childWidget->isVisible());
11887
11888 // Now explicitly hide the childWidget
11889 childWidget->hide();
11890 widget.windowHandle()->close();
11891 widget.show();
11892 QVERIFY(QTest::qWaitForWindowExposed(&widget));
11893 QVERIFY(!childWidget->isVisible());
11894}
11895
11896class WinIdChangeSpy : public QObject
11897{
11898 Q_OBJECT
11899public:
11900 QWidget *widget = nullptr;
11901 WId winId = 0;
11902 explicit WinIdChangeSpy(QWidget *w, QObject *parent = nullptr)
11903 : QObject(parent)
11904 , widget(w)
11905 , winId(widget->winId())
11906 {
11907 }
11908
11909public slots:
11910 bool eventFilter(QObject *obj, QEvent *event) override
11911 {
11912 if (obj == widget) {
11913 if (event->type() == QEvent::WinIdChange) {
11914 winId = widget->winId();
11915 return true;
11916 }
11917 }
11918 return false;
11919 }
11920};
11921
11922void tst_QWidget::winIdAfterClose()
11923{
11924 auto widget = new QWidget;
11925 auto notifier = new QObject(widget);
11926 auto deleteWidget = new QWidget(new QWidget(widget));
11927 auto spy = new WinIdChangeSpy(deleteWidget);
11928 deleteWidget->installEventFilter(filterObj: spy);
11929 connect(sender: notifier, signal: &QObject::destroyed, slot: [&] { delete deleteWidget; });
11930
11931 widget->setAttribute(Qt::WA_NativeWindow);
11932 widget->windowHandle()->create();
11933 widget->show();
11934
11935 QVERIFY(QTest::qWaitForWindowExposed(widget));
11936 QVERIFY(spy->winId);
11937
11938 widget->windowHandle()->close();
11939 delete widget;
11940
11941 QCOMPARE(spy->winId, WId(0));
11942 delete spy;
11943}
11944
11945class LanguageChangeEventWidget : public QWidget
11946{
11947public:
11948 LanguageChangeEventWidget(QWidget *parent = nullptr) : QWidget(parent) {}
11949 int languageChangeCount = 0;
11950protected:
11951 bool event(QEvent *e) override
11952 {
11953 if (e->type() == QEvent::LanguageChange)
11954 languageChangeCount++;
11955 return QWidget::event(event: e);
11956 }
11957};
11958
11959class LanguageChangeEventWindow : public QWindow
11960{
11961public:
11962 LanguageChangeEventWindow(QWindow *parent = nullptr) : QWindow(parent) {}
11963 int languageChangeCount = 0;
11964protected:
11965 bool event(QEvent *e) override
11966 {
11967 if (e->type() == QEvent::LanguageChange)
11968 languageChangeCount++;
11969 return QWindow::event(e);
11970 }
11971};
11972
11973void tst_QWidget::receivesLanguageChangeEvent()
11974{
11975 // Confirm that any QWindow or QWidget only gets a single
11976 // LanguageChange event when a translator is installed
11977 LanguageChangeEventWidget topLevel;
11978 auto childWidget = new LanguageChangeEventWidget(&topLevel);
11979 topLevel.show();
11980 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
11981 LanguageChangeEventWindow ww;
11982 ww.show();
11983 QVERIFY(QTest::qWaitForWindowExposed(&ww));
11984 LanguageChangeEventWidget topLevelNotShown;
11985 QTranslator t;
11986 QVERIFY(t.load("hellotr_la.qm", ":/"));
11987 QVERIFY(qApp->installTranslator(&t));
11988 QCoreApplication::sendPostedEvents(receiver: 0, event_type: QEvent::LanguageChange);
11989 QCOMPARE(topLevel.languageChangeCount, 1);
11990 QCOMPARE(topLevelNotShown.languageChangeCount, 1);
11991 QCOMPARE(childWidget->languageChangeCount, 1);
11992 QCOMPARE(ww.languageChangeCount, 1);
11993}
11994
11995class DeleteOnCloseEventWidget : public QWidget
11996{
11997protected:
11998 virtual void closeEvent(QCloseEvent *e) override
11999 {
12000 e->accept();
12001 delete this;
12002 }
12003};
12004
12005void tst_QWidget::deleteWindowInCloseEvent()
12006{
12007 // Just checking if closing this widget causes a crash
12008 auto widget = new DeleteOnCloseEventWidget;
12009 widget->close();
12010 QVERIFY(true);
12011}
12012
12013void tst_QWidget::activateWhileModalHidden()
12014{
12015 QDialog dialog;
12016 dialog.setWindowModality(Qt::ApplicationModal);
12017 dialog.show();
12018 QVERIFY(QTest::qWaitForWindowActive(&dialog));
12019 QVERIFY(dialog.isActiveWindow());
12020 QCOMPARE(QApplication::activeWindow(), &dialog);
12021
12022 dialog.hide();
12023 QTRY_VERIFY(!dialog.isVisible());
12024
12025 QMainWindow window;
12026 window.show();
12027 QVERIFY(QTest::qWaitForWindowActive(&window));
12028 QVERIFY(window.isActiveWindow());
12029 QCOMPARE(QApplication::activeWindow(), &window);
12030}
12031
12032QTEST_MAIN(tst_QWidget)
12033#include "tst_qwidget.moc"
12034

source code of qtbase/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp