| 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 | |
| 85 | using 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 | |
| 95 | static 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> |
| 115 | bool 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) |
| 125 | static inline void setWindowsAnimationsEnabled(bool enabled) |
| 126 | { |
| 127 | ANIMATIONINFO animation = { sizeof(ANIMATIONINFO), enabled }; |
| 128 | SystemParametersInfo(SPI_SETANIMATION, 0, &animation, 0); |
| 129 | } |
| 130 | |
| 131 | static 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 |
| 138 | inline void setWindowsAnimationsEnabled(bool) {} |
| 139 | static inline bool windowsAnimationsEnabled() { return false; } |
| 140 | #endif // !Q_OS_WIN || Q_OS_WINRT |
| 141 | |
| 142 | template <class T> |
| 143 | static 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 | |
| 150 | class tst_QWidget : public QObject |
| 151 | { |
| 152 | Q_OBJECT |
| 153 | |
| 154 | public: |
| 155 | tst_QWidget(); |
| 156 | virtual ~tst_QWidget(); |
| 157 | |
| 158 | public slots: |
| 159 | void initTestCase(); |
| 160 | void cleanup(); |
| 161 | private 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 | |
| 429 | private: |
| 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 | |
| 441 | bool 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 |
| 448 | void 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 | |
| 592 | tst_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 | |
| 612 | tst_QWidget::~tst_QWidget() |
| 613 | { |
| 614 | if (m_windowsAnimationsEnabled) |
| 615 | setWindowsAnimationsEnabled(m_windowsAnimationsEnabled); |
| 616 | } |
| 617 | |
| 618 | void 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 | |
| 637 | void tst_QWidget::cleanup() |
| 638 | { |
| 639 | QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); |
| 640 | } |
| 641 | |
| 642 | void 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 | |
| 740 | class QPropagationTestWidget : public QWidget |
| 741 | { |
| 742 | Q_OBJECT |
| 743 | public: |
| 744 | using QWidget::QWidget; |
| 745 | }; |
| 746 | |
| 747 | void 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 | |
| 834 | void 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 | */ |
| 851 | void 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 | |
| 906 | void 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 | |
| 943 | void 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 | */ |
| 1055 | void 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 | |
| 1112 | void 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 | |
| 1156 | void 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 | |
| 1168 | void 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) |
| 1204 | void 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 | |
| 1257 | void 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 | |
| 1281 | void 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 | |
| 1322 | void 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 | |
| 1355 | void 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 | |
| 1382 | void 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 | |
| 1398 | void 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 | |
| 1425 | void 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 | |
| 1466 | void 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 | |
| 1497 | void 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 | |
| 1558 | void 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 | |
| 1712 | void 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 | |
| 1760 | void 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 | |
| 1785 | class Container : public QWidget |
| 1786 | { |
| 1787 | public: |
| 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 | |
| 1807 | class Composite : public QFrame |
| 1808 | { |
| 1809 | public: |
| 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 | |
| 1826 | public: |
| 1827 | QLineEdit *lineEdit1; |
| 1828 | QLineEdit *lineEdit2; |
| 1829 | QLineEdit *lineEdit3; |
| 1830 | }; |
| 1831 | |
| 1832 | void 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 | |
| 1889 | void 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 | |
| 1951 | void 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 | |
| 2012 | void 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 | |
| 2063 | void 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 | |
| 2154 | static 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 |
| 2167 | static 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 | |
| 2184 | void 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 | |
| 2229 | void 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 | |
| 2247 | void 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 | |
| 2299 | void 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 | |
| 2326 | void 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 | |
| 2354 | void 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 | |
| 2380 | void 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) |
| 2412 | void 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 | |
| 2442 | void 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 | |
| 2576 | void 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 | |
| 2655 | void 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 | |
| 2742 | class ResizeWidget : public QWidget { |
| 2743 | public: |
| 2744 | explicit ResizeWidget(QWidget *p = nullptr) : QWidget(p) |
| 2745 | { |
| 2746 | setObjectName(QLatin1String("ResizeWidget" )); |
| 2747 | setWindowTitle(objectName()); |
| 2748 | } |
| 2749 | protected: |
| 2750 | void resizeEvent(QResizeEvent *e) override |
| 2751 | { |
| 2752 | QCOMPARE(size(), e->size()); |
| 2753 | ++m_resizeEventCount; |
| 2754 | } |
| 2755 | |
| 2756 | public: |
| 2757 | int m_resizeEventCount = 0; |
| 2758 | }; |
| 2759 | |
| 2760 | void 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 | |
| 2801 | void 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 | |
| 2856 | void 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 | |
| 2995 | void 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. |
| 3045 | void 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 | |
| 3070 | void 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 | |
| 3111 | void 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 | |
| 3220 | void 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 | |
| 3251 | void 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 | |
| 3283 | void 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 | |
| 3314 | class UpdateWidget : public QWidget |
| 3315 | { |
| 3316 | public: |
| 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 | |
| 3371 | void 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 | |
| 3387 | void 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 | |
| 3492 | void 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 | |
| 3553 | void 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 | |
| 3634 | void 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 | |
| 3648 | class ContentsPropagationWidget : public QWidget |
| 3649 | { |
| 3650 | Q_OBJECT |
| 3651 | public: |
| 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 | |
| 3669 | protected: |
| 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. |
| 3680 | static 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 | |
| 3692 | void 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 | |
| 3720 | void 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 | |
| 3860 | void 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 | */ |
| 3882 | void 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 | |
| 3958 | void 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 | |
| 4035 | void 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 | |
| 4053 | class Widget : public QWidget |
| 4054 | { |
| 4055 | public: |
| 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 | |
| 4072 | void 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 |
| 4164 | class MaskedPainter : public QWidget |
| 4165 | { |
| 4166 | public: |
| 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 | */ |
| 4185 | bool 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 | |
| 4196 | void 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 | |
| 4218 | class StaticWidget : public QWidget |
| 4219 | { |
| 4220 | Q_OBJECT |
| 4221 | public: |
| 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 | */ |
| 4252 | void 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 | |
| 4334 | void 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 | |
| 4382 | class SiblingDeleter : public QWidget |
| 4383 | { |
| 4384 | public: |
| 4385 | inline SiblingDeleter(QWidget *sibling, QWidget *parent) |
| 4386 | : QWidget(parent), sibling(sibling) {} |
| 4387 | inline ~SiblingDeleter() { delete sibling; } |
| 4388 | |
| 4389 | private: |
| 4390 | QPointer<QWidget> sibling; |
| 4391 | }; |
| 4392 | |
| 4393 | |
| 4394 | void 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 | |
| 4406 | void 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 | |
| 4433 | void 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 | |
| 4452 | void 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 | |
| 4489 | void 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 | |
| 4535 | class WinIdChangeWidget : public QWidget |
| 4536 | { |
| 4537 | public: |
| 4538 | using QWidget::QWidget; |
| 4539 | protected: |
| 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 | } |
| 4548 | public: |
| 4549 | QList<WId> m_winIdList; |
| 4550 | int winIdChangeEventCount() const { return m_winIdList.count(); } |
| 4551 | }; |
| 4552 | |
| 4553 | class CreateDestroyWidget : public WinIdChangeWidget |
| 4554 | { |
| 4555 | public: |
| 4556 | void create() { QWidget::create(); } |
| 4557 | void destroy() { QWidget::destroy(); } |
| 4558 | }; |
| 4559 | |
| 4560 | void 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 | |
| 4605 | void 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 | |
| 4671 | void 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 | |
| 4718 | void 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 * = 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 | |
| 4733 | void 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 | |
| 4746 | class ShowHideEventWidget : public QWidget |
| 4747 | { |
| 4748 | public: |
| 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 | |
| 4770 | void 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 | |
| 4816 | void 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 | |
| 4837 | void 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 | |
| 4860 | void 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 | |
| 4886 | void 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 |
| 5048 | static inline bool isOpaque(QWidget *widget) |
| 5049 | { |
| 5050 | if (!widget) |
| 5051 | return false; |
| 5052 | return qt_widget_private(widget)->isOpaque; |
| 5053 | } |
| 5054 | #endif |
| 5055 | |
| 5056 | void 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 | */ |
| 5134 | void 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. |
| 5201 | void 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 | |
| 5224 | class DestroyedSlotChecker : public QObject |
| 5225 | { |
| 5226 | Q_OBJECT |
| 5227 | |
| 5228 | public: |
| 5229 | bool wasQWidget = false; |
| 5230 | |
| 5231 | public 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 | */ |
| 5242 | void 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 | |
| 5257 | using Rects = QVector<QRect>; |
| 5258 | |
| 5259 | void 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 | |
| 5316 | void 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) |
| 5454 | void 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 | |
| 5479 | void tst_QWidget::windowMoveResize_data() |
| 5480 | { |
| 5481 | setWindowGeometry_data(); |
| 5482 | } |
| 5483 | |
| 5484 | void 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 | |
| 5676 | class ColorWidget : public QWidget |
| 5677 | { |
| 5678 | public: |
| 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 | |
| 5714 | static 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 | |
| 5720 | static 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 | |
| 5729 | bool verifyColor(QWidget &child, const QRegion ®ion, 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 | |
| 5781 | void 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 | |
| 5791 | void 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 | |
| 5840 | void 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 | |
| 5880 | void 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 | |
| 5918 | void 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 | |
| 5928 | class TopLevelFocusCheck: public QWidget |
| 5929 | { |
| 5930 | Q_OBJECT |
| 5931 | public: |
| 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 | |
| 5940 | public 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 | |
| 5957 | void 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 | |
| 6014 | class FocusWidget: public QWidget |
| 6015 | { |
| 6016 | protected: |
| 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 | |
| 6040 | public: |
| 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 | |
| 6056 | void 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 | |
| 6296 | template<class T> class EventSpy : public QObject |
| 6297 | { |
| 6298 | public: |
| 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 | |
| 6310 | protected: |
| 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 | |
| 6318 | private: |
| 6319 | T *m_widget; |
| 6320 | const QEvent::Type eventToSpy; |
| 6321 | int m_count = 0; |
| 6322 | }; |
| 6323 | |
| 6324 | #ifndef QT_NO_CURSOR |
| 6325 | void 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 | |
| 6447 | void 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> (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 * = 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 | |
| 6501 | void 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 | |
| 6609 | void 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 | |
| 6644 | class ShowHideShowWidget : public QWidget, public QAbstractNativeEventFilter |
| 6645 | { |
| 6646 | Q_OBJECT |
| 6647 | |
| 6648 | int state = 0; |
| 6649 | public: |
| 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 | |
| 6704 | signals: |
| 6705 | void done(); |
| 6706 | }; |
| 6707 | |
| 6708 | void 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 | |
| 6729 | void 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 | |
| 6762 | class EventRecorder : public QObject |
| 6763 | { |
| 6764 | Q_OBJECT |
| 6765 | |
| 6766 | public: |
| 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 | |
| 6794 | private: |
| 6795 | static inline void formatEventList(const EventList &l, QDebug &d); |
| 6796 | |
| 6797 | EventList events; |
| 6798 | }; |
| 6799 | |
| 6800 | void 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 | |
| 6812 | QByteArray 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 | |
| 6824 | void 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 | |
| 7076 | class RenderWidget : public QWidget |
| 7077 | { |
| 7078 | public: |
| 7079 | RenderWidget(QWidget *source) |
| 7080 | : source(source), ellipse(false) {} |
| 7081 | |
| 7082 | void setEllipseEnabled(bool enable = true) |
| 7083 | { |
| 7084 | ellipse = enable; |
| 7085 | update(); |
| 7086 | } |
| 7087 | |
| 7088 | protected: |
| 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 | |
| 7103 | private: |
| 7104 | QWidget *source; |
| 7105 | bool ellipse; |
| 7106 | }; |
| 7107 | |
| 7108 | void 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. |
| 7202 | static 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 |
| 7222 | void 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 | |
| 7430 | void 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 | |
| 7534 | void 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 | |
| 7544 | void 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 | |
| 7577 | void 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. |
| 7596 | void 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 | |
| 7672 | void 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 | |
| 7761 | void 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 | |
| 7772 | void 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 | |
| 7856 | void 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); |
| 7868 | void 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 | |
| 7956 | void 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 | |
| 7967 | void 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 | |
| 8057 | void 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 | |
| 8076 | void 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 | |
| 8087 | void 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 | |
| 8123 | void 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 |
| 8151 | void 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(). |
| 8186 | void 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 | |
| 8237 | class PaintOnScreenWidget: public QWidget |
| 8238 | { |
| 8239 | public: |
| 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 | |
| 8247 | void 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 | |
| 8492 | using WidgetAttributes = QVector<Qt::WidgetAttribute>; |
| 8493 | |
| 8494 | void 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 | |
| 8506 | void 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 | |
| 8526 | class ASWidget : public QWidget |
| 8527 | { |
| 8528 | public: |
| 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 | |
| 8560 | void 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 | |
| 8613 | void 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 | |
| 8644 | class TestLayout : public QVBoxLayout |
| 8645 | { |
| 8646 | Q_OBJECT |
| 8647 | public: |
| 8648 | using QVBoxLayout::QVBoxLayout; |
| 8649 | |
| 8650 | void invalidate() override |
| 8651 | { |
| 8652 | invalidated = true; |
| 8653 | } |
| 8654 | |
| 8655 | bool invalidated = false; |
| 8656 | }; |
| 8657 | |
| 8658 | void 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 | |
| 8702 | void 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 | |
| 8744 | void 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 | |
| 8760 | void 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 | |
| 8802 | void 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 | |
| 8824 | void 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 | |
| 8864 | class MaskSetWidget : public QWidget |
| 8865 | { |
| 8866 | Q_OBJECT |
| 8867 | public: |
| 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 | |
| 8886 | public slots: |
| 8887 | void resizeDown() { setGeometry(QRect(0, 50, 50, 50)); } |
| 8888 | void resizeUp() { setGeometry(QRect(0, 50, 150, 50)); } |
| 8889 | }; |
| 8890 | |
| 8891 | void 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 | |
| 8929 | class MoveInResizeWidget : public QWidget |
| 8930 | { |
| 8931 | Q_OBJECT |
| 8932 | public: |
| 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 | |
| 8950 | public slots: |
| 8951 | void resizeMe() { |
| 8952 | resize(w: 100, h: 100); |
| 8953 | } |
| 8954 | }; |
| 8955 | |
| 8956 | void 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 |
| 8969 | void 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 | |
| 8995 | void 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 | |
| 9012 | void 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 | |
| 9037 | class CustomWidget : public QWidget |
| 9038 | { |
| 9039 | public: |
| 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 | |
| 9051 | void 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 | |
| 9066 | void 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 | |
| 9100 | void 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 | |
| 9136 | void 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) |
| 9151 | class GDIWidget : public QDialog |
| 9152 | { |
| 9153 | Q_OBJECT |
| 9154 | public: |
| 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 | |
| 9184 | private 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 | |
| 9192 | public: |
| 9193 | QColor color; |
| 9194 | QTimer timer; |
| 9195 | }; |
| 9196 | |
| 9197 | void tst_QWidget::gdiPainting() |
| 9198 | { |
| 9199 | GDIWidget w; |
| 9200 | w.exec(); |
| 9201 | |
| 9202 | QCOMPARE(w.color, QColor(255, 0, 0)); |
| 9203 | |
| 9204 | } |
| 9205 | |
| 9206 | void 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 | |
| 9219 | void 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 | |
| 9304 | void 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 | |
| 9333 | class ColorRedWidget : public QWidget |
| 9334 | { |
| 9335 | public: |
| 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 | |
| 9348 | void 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 | |
| 9387 | class MaskResizeTestWidget : public QWidget |
| 9388 | { |
| 9389 | Q_OBJECT |
| 9390 | public: |
| 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 | |
| 9407 | public 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 | |
| 9420 | void 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 | |
| 9574 | void 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 |
| 9724 | void 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 |
| 9830 | void 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 | |
| 9924 | void 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 | |
| 9936 | void 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 | |
| 9958 | class MyEvilObject : public QObject |
| 9959 | { |
| 9960 | Q_OBJECT |
| 9961 | public: |
| 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 | |
| 9969 | private slots: |
| 9970 | void beEvil(QObject *) { widget->update(ax: 0, ay: 0, aw: 150, ah: 150); } |
| 9971 | }; |
| 9972 | |
| 9973 | void 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 | |
| 9991 | void 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 | |
| 10006 | void 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 |
| 10024 | void 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 |
| 10052 | QWidgetRepaintManager* repaintManager(QWidget &widget) |
| 10053 | { |
| 10054 | QWidgetRepaintManager *repaintManager = nullptr; |
| 10055 | #ifdef QT_BUILD_INTERNAL |
| 10056 | if (QTLWExtra * = 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. |
| 10063 | void 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 | |
| 10107 | void 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 | |
| 10154 | class TestGraphicsEffect : public QGraphicsEffect |
| 10155 | { |
| 10156 | public: |
| 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 | } |
| 10175 | protected: |
| 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 | |
| 10189 | static 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 | } |
| 10198 | static 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 | } |
| 10208 | static 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 | } |
| 10224 | static 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 | |
| 10241 | void 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 | |
| 10255 | void 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 | |
| 10304 | void 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 | |
| 10349 | void 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 | */ |
| 10380 | void 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 | |
| 10491 | void 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 |
| 10520 | class scrollWidgetWBS : public QWidget |
| 10521 | { |
| 10522 | public: |
| 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 |
| 10540 | void 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 | |
| 10559 | void 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 | |
| 10572 | void 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 | |
| 10622 | void 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 | |
| 10680 | void 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 | |
| 10702 | void 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 | |
| 10715 | void 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 | |
| 10744 | static 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 | |
| 10776 | void 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 | |
| 10823 | static 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 | |
| 10830 | class GrabLoggerWidget : public QWidget |
| 10831 | { |
| 10832 | public: |
| 10833 | explicit GrabLoggerWidget(QStringList *log, QWidget *parent = nullptr) : QWidget(parent), m_log(log) {} |
| 10834 | |
| 10835 | protected: |
| 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 | } |
| 10852 | private: |
| 10853 | QStringList *m_log; |
| 10854 | }; |
| 10855 | |
| 10856 | void 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 | |
| 10894 | void 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 | |
| 10922 | class TouchMouseWidget : public QWidget { |
| 10923 | public: |
| 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 | |
| 10940 | protected: |
| 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 | |
| 10979 | public: |
| 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 | |
| 10991 | void 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 | |
| 11087 | void 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 | |
| 11124 | void 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 | |
| 11163 | void 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 | |
| 11174 | class DestroyTester : public QObject |
| 11175 | { |
| 11176 | Q_OBJECT |
| 11177 | public: |
| 11178 | explicit DestroyTester(QObject *parent = nullptr) : QObject(parent) { parentDestroyed = 0; } |
| 11179 | static int parentDestroyed; |
| 11180 | public slots: |
| 11181 | void parentDestroyedSlot() { |
| 11182 | ++parentDestroyed; |
| 11183 | } |
| 11184 | }; |
| 11185 | |
| 11186 | int DestroyTester::parentDestroyed = 0; |
| 11187 | |
| 11188 | void 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 |
| 11273 | void 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 (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 * = 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 = 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 | |
| 11467 | class EnterTestModalDialog : public QDialog |
| 11468 | { |
| 11469 | Q_OBJECT |
| 11470 | public: |
| 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 | |
| 11481 | class EnterTestMainDialog : public QDialog |
| 11482 | { |
| 11483 | Q_OBJECT |
| 11484 | public: |
| 11485 | |
| 11486 | public 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 | |
| 11525 | public: |
| 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. |
| 11532 | void 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 | |
| 11565 | class KeyboardWidget : public QWidget |
| 11566 | { |
| 11567 | public: |
| 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 | |
| 11580 | void 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 | |
| 11593 | class DClickWidget : public QWidget |
| 11594 | { |
| 11595 | public: |
| 11596 | void mouseDoubleClickEvent(QMouseEvent *) override |
| 11597 | { |
| 11598 | triggered = true; |
| 11599 | } |
| 11600 | bool triggered = false; |
| 11601 | }; |
| 11602 | |
| 11603 | void 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 | |
| 11618 | void 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 | |
| 11636 | void 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 | |
| 11661 | void 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 | |
| 11677 | void 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 | |
| 11743 | class TabletWidget : public QWidget |
| 11744 | { |
| 11745 | public: |
| 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 | |
| 11755 | protected: |
| 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 | |
| 11781 | void 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 | |
| 11848 | class CloseCountingWidget : public QWidget |
| 11849 | { |
| 11850 | public: |
| 11851 | int closeCount = 0; |
| 11852 | void closeEvent(QCloseEvent *ev) override; |
| 11853 | }; |
| 11854 | |
| 11855 | void CloseCountingWidget::closeEvent(QCloseEvent *ev) |
| 11856 | { |
| 11857 | ++closeCount; |
| 11858 | ev->accept(); |
| 11859 | } |
| 11860 | |
| 11861 | void 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 | |
| 11874 | void 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 | |
| 11896 | class WinIdChangeSpy : public QObject |
| 11897 | { |
| 11898 | Q_OBJECT |
| 11899 | public: |
| 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 | |
| 11909 | public 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 | |
| 11922 | void 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 | |
| 11945 | class LanguageChangeEventWidget : public QWidget |
| 11946 | { |
| 11947 | public: |
| 11948 | LanguageChangeEventWidget(QWidget *parent = nullptr) : QWidget(parent) {} |
| 11949 | int languageChangeCount = 0; |
| 11950 | protected: |
| 11951 | bool event(QEvent *e) override |
| 11952 | { |
| 11953 | if (e->type() == QEvent::LanguageChange) |
| 11954 | languageChangeCount++; |
| 11955 | return QWidget::event(event: e); |
| 11956 | } |
| 11957 | }; |
| 11958 | |
| 11959 | class LanguageChangeEventWindow : public QWindow |
| 11960 | { |
| 11961 | public: |
| 11962 | LanguageChangeEventWindow(QWindow *parent = nullptr) : QWindow(parent) {} |
| 11963 | int languageChangeCount = 0; |
| 11964 | protected: |
| 11965 | bool event(QEvent *e) override |
| 11966 | { |
| 11967 | if (e->type() == QEvent::LanguageChange) |
| 11968 | languageChangeCount++; |
| 11969 | return QWindow::event(e); |
| 11970 | } |
| 11971 | }; |
| 11972 | |
| 11973 | void 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 | |
| 11995 | class DeleteOnCloseEventWidget : public QWidget |
| 11996 | { |
| 11997 | protected: |
| 11998 | virtual void closeEvent(QCloseEvent *e) override |
| 11999 | { |
| 12000 | e->accept(); |
| 12001 | delete this; |
| 12002 | } |
| 12003 | }; |
| 12004 | |
| 12005 | void 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 | |
| 12013 | void 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 | |
| 12032 | QTEST_MAIN(tst_QWidget) |
| 12033 | #include "tst_qwidget.moc" |
| 12034 | |