| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "../../../shared/highdpi.h" |
| |
| #include <qboxlayout.h> |
| #include <qapplication.h> |
| #include <qbitmap.h> |
| #include <qdebug.h> |
| #include <qeventloop.h> |
| #include <qlabel.h> |
| #include <qlayout.h> |
| #include <qlineedit.h> |
| #include <qlistview.h> |
| #include <qmessagebox.h> |
| #include <qpainter.h> |
| #include <qpoint.h> |
| #include <qpushbutton.h> |
| #include <qstyle.h> |
| #include <qwidget.h> |
| #include <qstylefactory.h> |
| #include <qdesktopwidget.h> |
| #include <private/qwidget_p.h> |
| #include <private/qwidgetrepaintmanager_p.h> |
| #include <private/qapplication_p.h> |
| #include <private/qhighdpiscaling_p.h> |
| #include <qcalendarwidget.h> |
| #include <qmainwindow.h> |
| #include <qdockwidget.h> |
| #include <qrandom.h> |
| #include <qstylehints.h> |
| #include <qtoolbar.h> |
| #include <qtoolbutton.h> |
| #include <QtCore/qoperatingsystemversion.h> |
| #include <QtGui/qpaintengine.h> |
| #include <QtGui/qbackingstore.h> |
| #include <QtGui/qguiapplication.h> |
| #include <QtGui/qpa/qplatformwindow.h> |
| #include <QtGui/qscreen.h> |
| #include <qmenubar.h> |
| #include <qcompleter.h> |
| #include <qtableview.h> |
| #include <qtreewidget.h> |
| #include <qabstractnativeeventfilter.h> |
| #include <qproxystyle.h> |
| #include <QtWidgets/QGraphicsView> |
| #include <QtWidgets/QGraphicsProxyWidget> |
| #include <QtGui/qwindow.h> |
| #include <qtimer.h> |
| |
| #if defined(Q_OS_OSX) |
| #include "tst_qwidget_mac_helpers.h" // Abstract the ObjC stuff out so not everyone must run an ObjC++ compile. |
| #endif |
| |
| #include <QtTest/QTest> |
| #include <QtTest/private/qtesthelpers_p.h> |
| |
| using namespace QTestPrivate; |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| # include <QtCore/qt_windows.h> |
| # include <QtGui/private/qguiapplication_p.h> |
| #include <qpa/qplatformnativeinterface.h> |
| #include <qpa/qplatformintegration.h> |
| |
| #include <algorithm> |
| |
| static HWND winHandleOf(const QWidget *w) |
| { |
| static QPlatformNativeInterface *nativeInterface |
| = QGuiApplicationPrivate::platformIntegration()->nativeInterface(); |
| if (void *handle = nativeInterface->nativeResourceForWindow("handle", w->window()->windowHandle())) |
| return reinterpret_cast<HWND>(handle); |
| qWarning() << "Cannot obtain native handle for " << w; |
| return nullptr; |
| } |
| |
| # define Q_CHECK_PAINTEVENTS \ |
| if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ |
| QSKIP("desktop is not visible, this test would fail"); |
| |
| #else // Q_OS_WIN && !Q_OS_WINRT |
| # define Q_CHECK_PAINTEVENTS |
| #endif |
| |
| #ifdef Q_OS_OSX |
| #include <Security/AuthSession.h> |
| bool macHasAccessToWindowsServer() |
| { |
| SecuritySessionId mySession; |
| SessionAttributeBits sessionInfo; |
| SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo); |
| return (sessionInfo & sessionHasGraphicAccess); |
| } |
| #endif |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| static inline void setWindowsAnimationsEnabled(bool enabled) |
| { |
| ANIMATIONINFO animation = { sizeof(ANIMATIONINFO), enabled }; |
| SystemParametersInfo(SPI_SETANIMATION, 0, &animation, 0); |
| } |
| |
| static inline bool windowsAnimationsEnabled() |
| { |
| ANIMATIONINFO animation = { sizeof(ANIMATIONINFO), 0 }; |
| SystemParametersInfo(SPI_GETANIMATION, 0, &animation, 0); |
| return animation.iMinAnimate; |
| } |
| #else // Q_OS_WIN && !Q_OS_WINRT |
| inline void setWindowsAnimationsEnabled(bool) {} |
| static inline bool windowsAnimationsEnabled() { return false; } |
| #endif // !Q_OS_WIN || Q_OS_WINRT |
| |
| template <class T> |
| static QByteArray msgComparisonFailed(T v1, const char *op, T v2) |
| { |
| QString s; |
| QDebug(&s) << v1 << op << v2; |
| return s.toLocal8Bit(); |
| } |
| |
| class tst_QWidget : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| tst_QWidget(); |
| virtual ~tst_QWidget(); |
| |
| public slots: |
| void initTestCase(); |
| void cleanup(); |
| private slots: |
| void getSetCheck(); |
| void fontPropagation(); |
| void fontPropagation2(); |
| void fontPropagation3(); |
| void palettePropagation(); |
| void palettePropagation2(); |
| void enabledPropagation(); |
| void ignoreKeyEventsWhenDisabled_QTBUG27417(); |
| void properTabHandlingWhenDisabled_QTBUG27417(); |
| #if QT_CONFIG(draganddrop) |
| void acceptDropsPropagation(); |
| #endif |
| void isEnabledTo(); |
| void visible(); |
| void visible_setWindowOpacity(); |
| void isVisibleTo(); |
| void isHidden(); |
| void fonts(); |
| void mapFromAndTo_data(); |
| void mapFromAndTo(); |
| void focusChainOnHide(); |
| void focusChainOnReparent(); |
| void defaultTabOrder(); |
| void reverseTabOrder(); |
| void tabOrderWithProxy(); |
| void tabOrderWithCompoundWidgets(); |
| void tabOrderNoChange(); |
| void tabOrderNoChange2(); |
| void appFocusWidgetWithFocusProxyLater(); |
| void appFocusWidgetWhenLosingFocusProxy(); |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| void activation(); |
| #endif |
| void reparent(); |
| void windowState(); |
| void showMaximized(); |
| void showFullScreen(); |
| void showMinimized(); |
| void showMinimizedKeepsFocus(); |
| void icon(); |
| void hideWhenFocusWidgetIsChild(); |
| void normalGeometry(); |
| void setGeometry(); |
| void setGeometryHidden(); |
| void windowOpacity(); |
| void raise(); |
| void lower(); |
| void stackUnder(); |
| void testContentsPropagation(); |
| void saveRestoreGeometry(); |
| void restoreVersion1Geometry_data(); |
| void restoreVersion1Geometry(); |
| |
| void widgetAt(); |
| #ifdef Q_OS_OSX |
| void setMask(); |
| #endif |
| void optimizedResizeMove(); |
| void optimizedResize_topLevel(); |
| void resizeEvent(); |
| void task110173(); |
| |
| void testDeletionInEventHandlers(); |
| |
| void childDeletesItsSibling(); |
| |
| void setMinimumSize(); |
| void setMaximumSize(); |
| void setFixedSize(); |
| |
| void ensureCreated(); |
| void createAndDestroy(); |
| void winIdChangeEvent(); |
| void persistentWinId(); |
| void showNativeChild(); |
| void transientParent(); |
| void qobject_castInDestroyedSlot(); |
| |
| void showHideEvent_data(); |
| void showHideEvent(); |
| void showHideEventWhileMinimize(); |
| void showHideChildrenWhileMinimize_QTBUG50589(); |
| |
| void lostUpdatesOnHide(); |
| |
| void update(); |
| void isOpaque(); |
| |
| #ifndef Q_OS_OSX |
| void scroll(); |
| void scrollNativeChildren(); |
| #endif |
| |
| // tests QWidget::setGeometry() |
| void setWindowGeometry_data(); |
| void setWindowGeometry(); |
| |
| // tests QWidget::move() and resize() |
| void windowMoveResize_data(); |
| void windowMoveResize(); |
| |
| void moveChild_data(); |
| void moveChild(); |
| void showAndMoveChild(); |
| |
| void subtractOpaqueSiblings(); |
| |
| #if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) |
| void setGeometry_win(); |
| #endif |
| |
| void setLocale(); |
| void propagateLocale(); |
| void deleteStyle(); |
| void multipleToplevelFocusCheck(); |
| void setFocus(); |
| #ifndef QT_NO_CURSOR |
| void setCursor(); |
| #endif |
| void setToolTip(); |
| void testWindowIconChangeEventPropagation(); |
| |
| void minAndMaxSizeWithX11BypassWindowManagerHint(); |
| void showHideShowX11(); |
| void clean_qt_x11_enforce_cursor(); |
| |
| void childEvents(); |
| void render(); |
| void renderInvisible(); |
| void renderWithPainter(); |
| void render_task188133(); |
| void render_task211796(); |
| void render_task217815(); |
| void render_windowOpacity(); |
| void render_systemClip(); |
| void render_systemClip2_data(); |
| void render_systemClip2(); |
| void render_systemClip3_data(); |
| void render_systemClip3(); |
| void render_task252837(); |
| void render_worldTransform(); |
| |
| void setContentsMargins(); |
| |
| void moveWindowInShowEvent_data(); |
| void moveWindowInShowEvent(); |
| |
| void repaintWhenChildDeleted(); |
| void hideOpaqueChildWhileHidden(); |
| void updateWhileMinimized(); |
| void alienWidgets(); |
| void adjustSize(); |
| void adjustSize_data(); |
| void updateGeometry(); |
| void updateGeometry_data(); |
| void sendUpdateRequestImmediately(); |
| void doubleRepaint(); |
| void resizeInPaintEvent(); |
| void opaqueChildren(); |
| |
| void setMaskInResizeEvent(); |
| void moveInResizeEvent(); |
| |
| #ifdef QT_BUILD_INTERNAL |
| void immediateRepaintAfterInvalidateBackingStore(); |
| #endif |
| |
| void effectiveWinId(); |
| void effectiveWinId2(); |
| void customDpi(); |
| void customDpiProperty(); |
| |
| void quitOnCloseAttribute(); |
| void moveRect(); |
| |
| #if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) |
| void gdiPainting(); |
| void paintOnScreenPossible(); |
| #endif |
| void reparentStaticWidget(); |
| void QTBUG6883_reparentStaticWidget2(); |
| |
| void translucentWidget(); |
| |
| void setClearAndResizeMask(); |
| void maskedUpdate(); |
| #ifndef QT_NO_CURSOR |
| void syntheticEnterLeave(); |
| void taskQTBUG_4055_sendSyntheticEnterLeave(); |
| void underMouse(); |
| void taskQTBUG_27643_enterEvents(); |
| #endif |
| void windowFlags(); |
| void initialPosForDontShowOnScreenWidgets(); |
| void updateOnDestroyedSignal(); |
| void toplevelLineEditFocus(); |
| |
| void focusWidget_task254563(); |
| void rectOutsideCoordinatesLimit_task144779(); |
| void setGraphicsEffect(); |
| |
| #ifdef QT_BUILD_INTERNAL |
| void destroyBackingStore(); |
| #endif |
| |
| void activateWindow(); |
| |
| void openModal_taskQTBUG_5804(); |
| |
| void focusProxyAndInputMethods(); |
| #ifdef QT_BUILD_INTERNAL |
| void scrollWithoutBackingStore(); |
| #endif |
| |
| void taskQTBUG_7532_tabOrderWithFocusProxy(); |
| void movedAndResizedAttributes(); |
| void childAt(); |
| #ifdef Q_OS_OSX |
| void taskQTBUG_11373(); |
| #endif |
| void taskQTBUG_17333_ResizeInfiniteRecursion(); |
| |
| void nativeChildFocus(); |
| void grab(); |
| void grabMouse(); |
| void grabKeyboard(); |
| |
| void touchEventSynthesizedMouseEvent(); |
| void touchUpdateOnNewTouch(); |
| void touchEventsForGesturePendingWidgets(); |
| |
| void styleSheetPropagation(); |
| |
| void destroyedSignal(); |
| |
| void keyboardModifiers(); |
| void mouseDoubleClickBubbling_QTBUG29680(); |
| void largerThanScreen_QTBUG30142(); |
| |
| void resizeStaticContentsChildWidget_QTBUG35282(); |
| |
| void qmlSetParentHelper(); |
| |
| void testForOutsideWSRangeFlag(); |
| |
| void tabletTracking(); |
| |
| void closeEvent(); |
| void closeWithChildWindow(); |
| |
| private: |
| bool ensureScreenSize(int width, int height); |
| |
| const QString m_platform; |
| QSize m_testWidgetSize; |
| QPoint m_availableTopLeft; |
| QPoint m_safeCursorPos; |
| const bool m_windowsAnimationsEnabled; |
| QTouchDevice *m_touchScreen; |
| const int m_fuzz; |
| }; |
| |
| bool tst_QWidget::ensureScreenSize(int width, int height) |
| { |
| const QSize available = QGuiApplication::primaryScreen()->availableGeometry().size(); |
| return (available.width() >= width && available.height() >= height); |
| } |
| |
| // Testing get/set functions |
| void tst_QWidget::getSetCheck() |
| { |
| QWidget obj1; |
| QWidget child1(&obj1); |
| // QStyle * QWidget::style() |
| // void QWidget::setStyle(QStyle *) |
| QScopedPointer<QStyle> var1(QStyleFactory::create(QLatin1String("Windows"))); |
| obj1.setStyle(var1.data()); |
| QCOMPARE(static_cast<QStyle *>(var1.data()), obj1.style()); |
| obj1.setStyle(nullptr); |
| QVERIFY(var1.data() != obj1.style()); |
| QVERIFY(obj1.style() != nullptr); // style can never be 0 for a widget |
| |
| // int QWidget::minimumWidth() |
| // void QWidget::setMinimumWidth(int) |
| obj1.setMinimumWidth(0); |
| QCOMPARE(obj1.minimumWidth(), 0); |
| obj1.setMinimumWidth(INT_MIN); |
| QCOMPARE(obj1.minimumWidth(), 0); // A widgets width can never be less than 0 |
| obj1.setMinimumWidth(INT_MAX); |
| |
| child1.setMinimumWidth(0); |
| QCOMPARE(child1.minimumWidth(), 0); |
| child1.setMinimumWidth(INT_MIN); |
| QCOMPARE(child1.minimumWidth(), 0); // A widgets width can never be less than 0 |
| child1.setMinimumWidth(INT_MAX); |
| QCOMPARE(child1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium |
| |
| // int QWidget::minimumHeight() |
| // void QWidget::setMinimumHeight(int) |
| obj1.setMinimumHeight(0); |
| QCOMPARE(obj1.minimumHeight(), 0); |
| obj1.setMinimumHeight(INT_MIN); |
| QCOMPARE(obj1.minimumHeight(), 0); // A widgets height can never be less than 0 |
| obj1.setMinimumHeight(INT_MAX); |
| |
| child1.setMinimumHeight(0); |
| QCOMPARE(child1.minimumHeight(), 0); |
| child1.setMinimumHeight(INT_MIN); |
| QCOMPARE(child1.minimumHeight(), 0); // A widgets height can never be less than 0 |
| child1.setMinimumHeight(INT_MAX); |
| QCOMPARE(child1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium |
| |
| // int QWidget::maximumWidth() |
| // void QWidget::setMaximumWidth(int) |
| obj1.setMaximumWidth(0); |
| QCOMPARE(obj1.maximumWidth(), 0); |
| obj1.setMaximumWidth(INT_MIN); |
| QCOMPARE(obj1.maximumWidth(), 0); // A widgets width can never be less than 0 |
| obj1.setMaximumWidth(INT_MAX); |
| QCOMPARE(obj1.maximumWidth(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX |
| |
| // int QWidget::maximumHeight() |
| // void QWidget::setMaximumHeight(int) |
| obj1.setMaximumHeight(0); |
| QCOMPARE(obj1.maximumHeight(), 0); |
| obj1.setMaximumHeight(INT_MIN); |
| QCOMPARE(obj1.maximumHeight(), 0); // A widgets height can never be less than 0 |
| obj1.setMaximumHeight(INT_MAX); |
| QCOMPARE(obj1.maximumHeight(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX |
| |
| // back to normal |
| obj1.setMinimumWidth(0); |
| obj1.setMinimumHeight(0); |
| obj1.setMaximumWidth(QWIDGETSIZE_MAX); |
| obj1.setMaximumHeight(QWIDGETSIZE_MAX); |
| |
| // const QPalette & QWidget::palette() |
| // void QWidget::setPalette(const QPalette &) |
| QPalette var6; |
| obj1.setPalette(var6); |
| QCOMPARE(var6, obj1.palette()); |
| obj1.setPalette(QPalette()); |
| QCOMPARE(QPalette(), obj1.palette()); |
| |
| // const QFont & QWidget::font() |
| // void QWidget::setFont(const QFont &) |
| QFont var7; |
| obj1.setFont(var7); |
| QCOMPARE(var7, obj1.font()); |
| obj1.setFont(QFont()); |
| QCOMPARE(QFont(), obj1.font()); |
| |
| // qreal QWidget::windowOpacity() |
| // void QWidget::setWindowOpacity(qreal) |
| obj1.setWindowOpacity(0.0); |
| QCOMPARE(0.0, obj1.windowOpacity()); |
| obj1.setWindowOpacity(1.1); |
| QCOMPARE(1.0, obj1.windowOpacity()); // 1.0 is the fullest opacity possible |
| |
| // QWidget * QWidget::focusProxy() |
| // void QWidget::setFocusProxy(QWidget *) |
| { |
| QScopedPointer<QWidget> var9(new QWidget()); |
| obj1.setFocusProxy(var9.data()); |
| QCOMPARE(var9.data(), obj1.focusProxy()); |
| obj1.setFocusProxy(nullptr); |
| QCOMPARE(nullptr, obj1.focusProxy()); |
| } |
| |
| // const QRect & QWidget::geometry() |
| // void QWidget::setGeometry(const QRect &) |
| QCoreApplication::processEvents(); |
| QRect var10(10, 10, 100, 100); |
| obj1.setGeometry(var10); |
| QCoreApplication::processEvents(); |
| qDebug() << obj1.geometry(); |
| QCOMPARE(var10, obj1.geometry()); |
| obj1.setGeometry(QRect(0,0,0,0)); |
| qDebug() << obj1.geometry(); |
| QCOMPARE(QRect(0,0,0,0), obj1.geometry()); |
| |
| // QLayout * QWidget::layout() |
| // void QWidget::setLayout(QLayout *) |
| QBoxLayout *var11 = new QBoxLayout(QBoxLayout::LeftToRight); |
| obj1.setLayout(var11); |
| QCOMPARE(static_cast<QLayout *>(var11), obj1.layout()); |
| obj1.setLayout(nullptr); |
| QCOMPARE(static_cast<QLayout *>(var11), obj1.layout()); // You cannot set a 0-pointer layout, that keeps the current |
| delete var11; // This will remove the layout from the widget |
| QCOMPARE(nullptr, obj1.layout()); |
| |
| // bool QWidget::acceptDrops() |
| // void QWidget::setAcceptDrops(bool) |
| obj1.setAcceptDrops(false); |
| QCOMPARE(false, obj1.acceptDrops()); |
| obj1.setAcceptDrops(true); |
| QCOMPARE(true, obj1.acceptDrops()); |
| |
| // bool QWidget::autoFillBackground() |
| // void QWidget::setAutoFillBackground(bool) |
| obj1.setAutoFillBackground(false); |
| QCOMPARE(false, obj1.autoFillBackground()); |
| obj1.setAutoFillBackground(true); |
| QCOMPARE(true, obj1.autoFillBackground()); |
| |
| var1.reset(); |
| #if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) |
| obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); |
| const HWND handle = reinterpret_cast<HWND>(obj1.winId()); // explicitly create window handle |
| QVERIFY(GetWindowLong(handle, GWL_STYLE) & LONG(WS_POPUP)); |
| #endif |
| } |
| |
| tst_QWidget::tst_QWidget() |
| : m_platform(QGuiApplication::platformName().toLower()) |
| , m_safeCursorPos(0, 0) |
| , m_windowsAnimationsEnabled(windowsAnimationsEnabled()) |
| , m_touchScreen(QTest::createTouchDevice()) |
| , m_fuzz(int(QGuiApplication::primaryScreen()->devicePixelRatio())) |
| { |
| if (m_windowsAnimationsEnabled) // Disable animations which can interfere with screen grabbing in moveChild(), showAndMoveChild() |
| setWindowsAnimationsEnabled(false); |
| QFont font; |
| font.setBold(true); |
| font.setPointSize(42); |
| QApplication::setFont(font, "QPropagationTestWidget"); |
| |
| QPalette palette; |
| palette.setColor(QPalette::ToolTipBase, QColor(12, 13, 14)); |
| palette.setColor(QPalette::Text, QColor(21, 22, 23)); |
| QApplication::setPalette(palette, "QPropagationTestWidget"); |
| } |
| |
| tst_QWidget::~tst_QWidget() |
| { |
| if (m_windowsAnimationsEnabled) |
| setWindowsAnimationsEnabled(m_windowsAnimationsEnabled); |
| } |
| |
| void tst_QWidget::initTestCase() |
| { |
| // Size of reference widget, 200 for < 2000, scale up for larger screens |
| // to avoid Windows warnings about minimum size for decorated windows. |
| int width = 200; |
| const QScreen *screen = QGuiApplication::primaryScreen(); |
| const QRect availableGeometry = screen->availableGeometry(); |
| m_availableTopLeft = availableGeometry.topLeft(); |
| // XCB: Determine "safe" cursor position at bottom/right corner of screen. |
| // Pushing the mouse rapidly to the top left corner can trigger KDE / KWin's |
| // "Present all Windows" (Ctrl+F9) feature also programmatically. |
| if (m_platform == QLatin1String("xcb")) |
| m_safeCursorPos = availableGeometry.bottomRight() - QPoint(40, 40); |
| const int screenWidth = screen->geometry().width(); |
| if (screenWidth > 2000) |
| width = 100 * ((screenWidth + 500) / 1000); |
| m_testWidgetSize = QSize(width, width); |
| } |
| |
| void tst_QWidget::cleanup() |
| { |
| QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); |
| } |
| |
| void tst_QWidget::fontPropagation() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| QFont font = testWidget->font(); |
| QWidget* childWidget = new QWidget( testWidget.data() ); |
| childWidget->show(); |
| QCOMPARE( font, childWidget->font() ); |
| |
| font.setBold( true ); |
| testWidget->setFont( font ); |
| QCOMPARE( font, testWidget->font() ); |
| QCOMPARE( font, childWidget->font() ); |
| |
| QFont newFont = font; |
| newFont.setItalic( true ); |
| childWidget->setFont( newFont ); |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QCOMPARE( font, testWidget->font() ); |
| QCOMPARE( newFont, grandChildWidget->font() ); |
| |
| font.setUnderline( true ); |
| testWidget->setFont( font ); |
| |
| // the child and grand child should now have merged bold and |
| // underline |
| newFont.setUnderline( true ); |
| |
| QCOMPARE( newFont, childWidget->font() ); |
| QCOMPARE( newFont, grandChildWidget->font() ); |
| |
| // make sure font propagation continues working after reparenting |
| font = testWidget->font(); |
| font.setPointSize(font.pointSize() + 2); |
| testWidget->setFont(font); |
| |
| QWidget *one = new QWidget(testWidget.data()); |
| QWidget *two = new QWidget(one); |
| QWidget *three = new QWidget(two); |
| QWidget *four = new QWidget(two); |
| |
| four->setParent(three); |
| four->move(QPoint(0,0)); |
| |
| font.setPointSize(font.pointSize() + 2); |
| testWidget->setFont(font); |
| |
| QCOMPARE(testWidget->font(), one->font()); |
| QCOMPARE(one->font(), two->font()); |
| QCOMPARE(two->font(), three->font()); |
| QCOMPARE(three->font(), four->font()); |
| |
| QVERIFY(testWidget->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! one->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! two->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! three->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! four->testAttribute(Qt::WA_SetFont)); |
| |
| font.setPointSize(font.pointSize() + 2); |
| one->setFont(font); |
| |
| QCOMPARE(one->font(), two->font()); |
| QCOMPARE(two->font(), three->font()); |
| QCOMPARE(three->font(), four->font()); |
| |
| QVERIFY(one->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! two->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! three->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! four->testAttribute(Qt::WA_SetFont)); |
| |
| font.setPointSize(font.pointSize() + 2); |
| two->setFont(font); |
| |
| QCOMPARE(two->font(), three->font()); |
| QCOMPARE(three->font(), four->font()); |
| |
| QVERIFY(two->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! three->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! four->testAttribute(Qt::WA_SetFont)); |
| |
| font.setPointSize(font.pointSize() + 2); |
| three->setFont(font); |
| |
| QCOMPARE(three->font(), four->font()); |
| |
| QVERIFY(three->testAttribute(Qt::WA_SetFont)); |
| QVERIFY(! four->testAttribute(Qt::WA_SetFont)); |
| |
| font.setPointSize(font.pointSize() + 2); |
| four->setFont(font); |
| |
| QVERIFY(four->testAttribute(Qt::WA_SetFont)); |
| } |
| |
| class QPropagationTestWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| using QWidget::QWidget; |
| }; |
| |
| void tst_QWidget::fontPropagation2() |
| { |
| // ! Note, the code below is executed in tst_QWidget's constructor. |
| // QFont font; |
| // font.setBold(true); |
| // font.setPointSize(42); |
| // QApplication::setFont(font, "QPropagationTestWidget"); |
| |
| QScopedPointer<QWidget> root(new QWidget); |
| root->setObjectName(QLatin1String("fontPropagation2")); |
| root->setWindowTitle(root->objectName()); |
| root->resize(200, 200); |
| |
| QWidget *child0 = new QWidget(root.data()); |
| QWidget *child1 = new QWidget(child0); |
| QWidget *child2 = new QPropagationTestWidget(child1); |
| QWidget *child3 = new QWidget(child2); |
| QWidget *child4 = new QWidget(child3); |
| QWidget *child5 = new QWidget(child4); |
| root->show(); |
| |
| // Check that only the application fonts apply. |
| QCOMPARE(root->font(), QApplication::font()); |
| QCOMPARE(child0->font(), QApplication::font()); |
| QCOMPARE(child1->font(), QApplication::font()); |
| QCOMPARE(child2->font().pointSize(), 42); |
| QVERIFY(child2->font().bold()); |
| QCOMPARE(child3->font().pointSize(), 42); |
| QVERIFY(child3->font().bold()); |
| QCOMPARE(child4->font().pointSize(), 42); |
| QVERIFY(child4->font().bold()); |
| QCOMPARE(child5->font().pointSize(), 42); |
| QVERIFY(child5->font().bold()); |
| |
| // Set child0's font size to 15, and remove bold on child4. |
| QFont font; |
| font.setPointSize(15); |
| child0->setFont(font); |
| QFont unboldFont; |
| unboldFont.setBold(false); |
| child4->setFont(unboldFont); |
| |
| // Check that the above settings propagate correctly. |
| QCOMPARE(root->font(), QApplication::font()); |
| QCOMPARE(child0->font().pointSize(), 15); |
| QVERIFY(!child0->font().bold()); |
| QCOMPARE(child1->font().pointSize(), 15); |
| QVERIFY(!child1->font().bold()); |
| QCOMPARE(child2->font().pointSize(), 15); |
| QVERIFY(child2->font().bold()); |
| QCOMPARE(child3->font().pointSize(), 15); |
| QVERIFY(child3->font().bold()); |
| QCOMPARE(child4->font().pointSize(), 15); |
| QVERIFY(!child4->font().bold()); |
| QCOMPARE(child5->font().pointSize(), 15); |
| QVERIFY(!child5->font().bold()); |
| |
| // Replace the app font for child2. Italic should propagate |
| // but the size should still be ignored. The previous bold |
| // setting is gone. |
| QFont italicSizeFont; |
| italicSizeFont.setItalic(true); |
| italicSizeFont.setPointSize(33); |
| QApplication::setFont(italicSizeFont, "QPropagationTestWidget"); |
| |
| // Check that this propagates correctly. |
| QCOMPARE(root->font(), QApplication::font()); |
| QCOMPARE(child0->font().pointSize(), 15); |
| QVERIFY(!child0->font().bold()); |
| QVERIFY(!child0->font().italic()); |
| QCOMPARE(child1->font().pointSize(), 15); |
| QVERIFY(!child1->font().bold()); |
| QVERIFY(!child1->font().italic()); |
| QCOMPARE(child2->font().pointSize(), 15); |
| QVERIFY(!child2->font().bold()); |
| QVERIFY(child2->font().italic()); |
| QCOMPARE(child3->font().pointSize(), 15); |
| QVERIFY(!child3->font().bold()); |
| QVERIFY(child3->font().italic()); |
| QCOMPARE(child4->font().pointSize(), 15); |
| QVERIFY(!child4->font().bold()); |
| QVERIFY(child4->font().italic()); |
| QCOMPARE(child5->font().pointSize(), 15); |
| QVERIFY(!child5->font().bold()); |
| QVERIFY(child5->font().italic()); |
| } |
| |
| void tst_QWidget::fontPropagation3() |
| { |
| QWidget parent; |
| QWidget *child = new QWidget(&parent); |
| parent.setFont(QFont("Monospace", 9)); |
| QImage image(32, 32, QImage::Format_RGB32); |
| QPainter p(&image); |
| p.setFont(child->font()); |
| QCOMPARE(p.font().family(), child->font().family()); |
| QCOMPARE(p.font().pointSize(), child->font().pointSize()); |
| } |
| |
| void tst_QWidget::palettePropagation() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| |
| QPalette palette = testWidget->palette(); |
| QWidget* childWidget = new QWidget( testWidget.data() ); |
| childWidget->show(); |
| QCOMPARE( palette, childWidget->palette() ); |
| |
| palette.setColor( QPalette::Base, Qt::red ); |
| testWidget->setPalette( palette ); |
| QCOMPARE( palette, testWidget->palette() ); |
| QCOMPARE( palette, childWidget->palette() ); |
| |
| QPalette newPalette = palette; |
| newPalette.setColor( QPalette::Highlight, Qt::green ); |
| childWidget->setPalette( newPalette ); |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QCOMPARE( palette, testWidget->palette() ); |
| QCOMPARE( newPalette, grandChildWidget->palette() ); |
| |
| palette.setColor( QPalette::Text, Qt::blue ); |
| testWidget->setPalette( palette ); |
| |
| // the child and grand child should now have merged green |
| // highlight and blue text |
| newPalette.setColor( QPalette::Text, Qt::blue); |
| |
| QCOMPARE( newPalette, childWidget->palette() ); |
| QCOMPARE( newPalette, grandChildWidget->palette() ); |
| } |
| |
| void tst_QWidget::palettePropagation2() |
| { |
| // ! Note, the code below is executed in tst_QWidget's constructor. |
| // QPalette palette; |
| // font.setColor(QPalette::ToolTipBase, QColor(12, 13, 14)); |
| // font.setColor(QPalette::Text, QColor(21, 22, 23)); |
| // qApp->setPalette(palette, "QPropagationTestWidget"); |
| |
| QScopedPointer<QWidget> root(new QWidget); |
| root->setObjectName(QLatin1String("palettePropagation2")); |
| root->setWindowTitle(root->objectName()); |
| root->resize(200, 200); |
| QWidget *child0 = new QWidget(root.data()); |
| QWidget *child1 = new QWidget(child0); |
| QWidget *child2 = new QPropagationTestWidget(child1); |
| QWidget *child3 = new QWidget(child2); |
| QWidget *child4 = new QWidget(child3); |
| QWidget *child5 = new QWidget(child4); |
| root->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(root.data())); |
| |
| // These colors are unlikely to be imposed on the default palette of |
| // QWidget ;-). |
| QColor sysPalText(21, 22, 23); |
| QColor sysPalToolTipBase(12, 13, 14); |
| QColor overridePalText(42, 43, 44); |
| QColor overridePalToolTipBase(45, 46, 47); |
| QColor sysPalButton(99, 98, 97); |
| |
| // Check that only the application fonts apply. |
| QPalette appPal = QApplication::palette(); |
| QCOMPARE(root->palette(), appPal); |
| QCOMPARE(child0->palette(), appPal); |
| QCOMPARE(child1->palette(), appPal); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child2->palette().color(QPalette::Text), sysPalText); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child3->palette().color(QPalette::Text), sysPalText); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child4->palette().color(QPalette::Text), sysPalText); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child5->palette().color(QPalette::Text), sysPalText); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| |
| // Set child0's Text, and set ToolTipBase on child4. |
| QPalette textPalette; |
| textPalette.setColor(QPalette::Text, overridePalText); |
| child0->setPalette(textPalette); |
| QPalette toolTipPalette; |
| toolTipPalette.setColor(QPalette::ToolTipBase, overridePalToolTipBase); |
| child4->setPalette(toolTipPalette); |
| |
| // Check that the above settings propagate correctly. |
| QCOMPARE(root->palette(), appPal); |
| QCOMPARE(child0->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child0->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child1->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child1->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child1->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child2->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child3->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipBase), sysPalToolTipBase); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child4->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child5->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| |
| // Replace the app palette for child2. Button should propagate but Text |
| // should still be ignored. The previous ToolTipBase setting is gone. |
| QPalette buttonPalette; |
| buttonPalette.setColor(QPalette::ToolTipText, sysPalButton); |
| QApplication::setPalette(buttonPalette, "QPropagationTestWidget"); |
| |
| // Check that the above settings propagate correctly. |
| QCOMPARE(root->palette(), appPal); |
| QCOMPARE(child0->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child0->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child0->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child1->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child1->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child1->palette().color(QPalette::ToolTipText), appPal.color(QPalette::ToolTipText)); |
| QCOMPARE(child2->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child2->palette().color(QPalette::ToolTipText), sysPalButton); |
| QCOMPARE(child3->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipBase), appPal.color(QPalette::ToolTipBase)); |
| QCOMPARE(child3->palette().color(QPalette::ToolTipText), sysPalButton); |
| QCOMPARE(child4->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); |
| QCOMPARE(child4->palette().color(QPalette::ToolTipText), sysPalButton); |
| QCOMPARE(child5->palette().color(QPalette::Text), overridePalText); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipBase), overridePalToolTipBase); |
| QCOMPARE(child5->palette().color(QPalette::ToolTipText), sysPalButton); |
| } |
| |
| void tst_QWidget::enabledPropagation() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| QWidget* childWidget = new QWidget( testWidget.data() ); |
| childWidget->show(); |
| QVERIFY( testWidget->isEnabled() ); |
| QVERIFY( childWidget->isEnabled() ); |
| |
| testWidget->setEnabled( false ); |
| QVERIFY( !testWidget->isEnabled() ); |
| QVERIFY( !childWidget->isEnabled() ); |
| |
| testWidget->setDisabled( false ); |
| QVERIFY( testWidget->isEnabled() ); |
| QVERIFY( childWidget->isEnabled() ); |
| |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QVERIFY( grandChildWidget->isEnabled() ); |
| |
| testWidget->setDisabled( true ); |
| QVERIFY( !testWidget->isEnabled() ); |
| QVERIFY( !childWidget->isEnabled() ); |
| QVERIFY( !grandChildWidget->isEnabled() ); |
| |
| grandChildWidget->setEnabled( false ); |
| testWidget->setEnabled( true ); |
| QVERIFY( testWidget->isEnabled() ); |
| QVERIFY( childWidget->isEnabled() ); |
| QVERIFY( !grandChildWidget->isEnabled() ); |
| |
| grandChildWidget->setEnabled( true ); |
| testWidget->setEnabled( false ); |
| childWidget->setDisabled( true ); |
| testWidget->setEnabled( true ); |
| QVERIFY( testWidget->isEnabled() ); |
| QVERIFY( !childWidget->isEnabled() ); |
| QVERIFY( !grandChildWidget->isEnabled() ); |
| } |
| |
| void tst_QWidget::ignoreKeyEventsWhenDisabled_QTBUG27417() |
| { |
| QLineEdit lineEdit; |
| lineEdit.setWindowTitle(__FUNCTION__); |
| lineEdit.setMinimumWidth(m_testWidgetSize.width()); |
| centerOnScreen(&lineEdit); |
| lineEdit.setDisabled(true); |
| lineEdit.show(); |
| QTest::keyClick(&lineEdit, Qt::Key_A); |
| QTRY_VERIFY(lineEdit.text().isEmpty()); |
| } |
| |
| void tst_QWidget::properTabHandlingWhenDisabled_QTBUG27417() |
| { |
| QWidget widget; |
| widget.setWindowTitle(__FUNCTION__); |
| widget.setMinimumWidth(m_testWidgetSize.width()); |
| centerOnScreen(&widget); |
| QVBoxLayout *layout = new QVBoxLayout(); |
| QLineEdit *lineEdit = new QLineEdit(); |
| layout->addWidget(lineEdit); |
| QLineEdit *lineEdit2 = new QLineEdit(); |
| layout->addWidget(lineEdit2); |
| QLineEdit *lineEdit3 = new QLineEdit(); |
| layout->addWidget(lineEdit3); |
| widget.setLayout(layout); |
| widget.show(); |
| |
| lineEdit->setFocus(); |
| QTRY_VERIFY(lineEdit->hasFocus()); |
| QTest::keyClick(&widget, Qt::Key_Tab); |
| QTRY_VERIFY(lineEdit2->hasFocus()); |
| QTest::keyClick(&widget, Qt::Key_Tab); |
| QTRY_VERIFY(lineEdit3->hasFocus()); |
| |
| lineEdit2->setDisabled(true); |
| lineEdit->setFocus(); |
| QTRY_VERIFY(lineEdit->hasFocus()); |
| QTest::keyClick(&widget, Qt::Key_Tab); |
| QTRY_VERIFY(!lineEdit2->hasFocus()); |
| QVERIFY(lineEdit3->hasFocus()); |
| } |
| |
| // Drag'n drop disabled in this build. |
| #if QT_CONFIG(draganddrop) |
| void tst_QWidget::acceptDropsPropagation() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| QWidget *childWidget = new QWidget(testWidget.data()); |
| childWidget->show(); |
| QVERIFY(!testWidget->acceptDrops()); |
| QVERIFY(!childWidget->acceptDrops()); |
| |
| testWidget->setAcceptDrops(true); |
| QVERIFY(testWidget->acceptDrops()); |
| QVERIFY(!childWidget->acceptDrops()); |
| QVERIFY(childWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| |
| testWidget->setAcceptDrops(false); |
| QVERIFY(!testWidget->acceptDrops()); |
| QVERIFY(!childWidget->acceptDrops()); |
| QVERIFY(!childWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| |
| QWidget *grandChildWidget = new QWidget(childWidget); |
| QVERIFY(!grandChildWidget->acceptDrops()); |
| QVERIFY(!grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| |
| testWidget->setAcceptDrops(true); |
| QVERIFY(testWidget->acceptDrops()); |
| QVERIFY(!childWidget->acceptDrops()); |
| QVERIFY(childWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| QVERIFY(!grandChildWidget->acceptDrops()); |
| QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| |
| grandChildWidget->setAcceptDrops(true); |
| testWidget->setAcceptDrops(false); |
| QVERIFY(!testWidget->acceptDrops()); |
| QVERIFY(!childWidget->acceptDrops()); |
| QVERIFY(grandChildWidget->acceptDrops()); |
| QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| |
| grandChildWidget->setAcceptDrops(false); |
| QVERIFY(!grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| testWidget->setAcceptDrops(true); |
| childWidget->setAcceptDrops(true); |
| testWidget->setAcceptDrops(false); |
| QVERIFY(!testWidget->acceptDrops()); |
| QVERIFY(childWidget->acceptDrops()); |
| QVERIFY(!grandChildWidget->acceptDrops()); |
| QVERIFY(grandChildWidget->testAttribute(Qt::WA_DropSiteRegistered)); |
| } |
| #endif |
| |
| void tst_QWidget::isEnabledTo() |
| { |
| QWidget testWidget; |
| testWidget.resize(m_testWidgetSize); |
| testWidget.setWindowTitle(__FUNCTION__); |
| centerOnScreen(&testWidget); |
| testWidget.show(); |
| QWidget* childWidget = new QWidget( &testWidget ); |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| |
| QVERIFY( childWidget->isEnabledTo( &testWidget ) ); |
| QVERIFY( grandChildWidget->isEnabledTo( &testWidget ) ); |
| |
| childWidget->setEnabled( false ); |
| QVERIFY( !childWidget->isEnabledTo( &testWidget ) ); |
| QVERIFY( grandChildWidget->isEnabledTo( childWidget ) ); |
| QVERIFY( !grandChildWidget->isEnabledTo( &testWidget ) ); |
| |
| QScopedPointer<QMainWindow> childDialog(new QMainWindow(&testWidget)); |
| testWidget.setEnabled(false); |
| QVERIFY(!childDialog->isEnabled()); |
| QVERIFY(childDialog->isEnabledTo(nullptr)); |
| } |
| |
| void tst_QWidget::visible() |
| { |
| // Ensure that the testWidget is hidden for this test at the |
| // start |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| QVERIFY( !testWidget->isVisible() ); |
| QWidget* childWidget = new QWidget( testWidget.data() ); |
| QVERIFY( !childWidget->isVisible() ); |
| |
| testWidget->show(); |
| QVERIFY(testWidget->screen()); |
| QVERIFY( testWidget->isVisible() ); |
| QVERIFY( childWidget->isVisible() ); |
| |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QVERIFY( !grandChildWidget->isVisible() ); |
| grandChildWidget->show(); |
| QVERIFY( grandChildWidget->isVisible() ); |
| |
| grandChildWidget->hide(); |
| testWidget->hide(); |
| testWidget->show(); |
| QVERIFY( !grandChildWidget->isVisible() ); |
| QVERIFY( testWidget->isVisible() ); |
| QVERIFY( childWidget->isVisible() ); |
| |
| grandChildWidget->show(); |
| childWidget->hide(); |
| testWidget->hide(); |
| testWidget->show(); |
| QVERIFY( testWidget->isVisible() ); |
| QVERIFY( !childWidget->isVisible() ); |
| QVERIFY( !grandChildWidget->isVisible() ); |
| |
| grandChildWidget->show(); |
| QVERIFY( !grandChildWidget->isVisible() ); |
| } |
| |
| void tst_QWidget::setLocale() |
| { |
| QWidget w; |
| QCOMPARE(w.locale(), QLocale()); |
| |
| w.setLocale(QLocale::Italian); |
| QCOMPARE(w.locale(), QLocale(QLocale::Italian)); |
| |
| QWidget child1(&w); |
| QCOMPARE(child1.locale(), QLocale(QLocale::Italian)); |
| |
| w.unsetLocale(); |
| QCOMPARE(w.locale(), QLocale()); |
| QCOMPARE(child1.locale(), QLocale()); |
| |
| w.setLocale(QLocale::French); |
| QCOMPARE(w.locale(), QLocale(QLocale::French)); |
| QCOMPARE(child1.locale(), QLocale(QLocale::French)); |
| |
| child1.setLocale(QLocale::Italian); |
| QCOMPARE(w.locale(), QLocale(QLocale::French)); |
| QCOMPARE(child1.locale(), QLocale(QLocale::Italian)); |
| |
| child1.unsetLocale(); |
| QCOMPARE(w.locale(), QLocale(QLocale::French)); |
| QCOMPARE(child1.locale(), QLocale(QLocale::French)); |
| |
| QWidget child2; |
| QCOMPARE(child2.locale(), QLocale()); |
| child2.setParent(&w); |
| QCOMPARE(child2.locale(), QLocale(QLocale::French)); |
| } |
| |
| void tst_QWidget::propagateLocale() |
| { |
| QWidget parent; |
| parent.setLocale(QLocale::French); |
| // Non-window widget; propagates locale: |
| QWidget *child = new QWidget(&parent); |
| QVERIFY(!child->isWindow()); |
| QVERIFY(!child->testAttribute(Qt::WA_WindowPropagation)); |
| QCOMPARE(child->locale(), QLocale(QLocale::French)); |
| parent.setLocale(QLocale::Italian); |
| QCOMPARE(child->locale(), QLocale(QLocale::Italian)); |
| delete child; |
| // Window: doesn't propagate locale: |
| child = new QWidget(&parent, Qt::Window); |
| QVERIFY(child->isWindow()); |
| QVERIFY(!child->testAttribute(Qt::WA_WindowPropagation)); |
| QCOMPARE(child->locale(), QLocale()); |
| parent.setLocale(QLocale::French); |
| QCOMPARE(child->locale(), QLocale()); |
| // ... unless we tell it to: |
| child->setAttribute(Qt::WA_WindowPropagation, true); |
| QVERIFY(child->testAttribute(Qt::WA_WindowPropagation)); |
| QCOMPARE(child->locale(), QLocale(QLocale::French)); |
| parent.setLocale(QLocale::Italian); |
| QCOMPARE(child->locale(), QLocale(QLocale::Italian)); |
| } |
| |
| void tst_QWidget::visible_setWindowOpacity() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->winId(); |
| |
| QVERIFY( !testWidget->isVisible() ); |
| testWidget->setWindowOpacity(0.5); |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| QVERIFY(!::IsWindowVisible(winHandleOf(testWidget.data()))); |
| #endif |
| testWidget->setWindowOpacity(1.0); |
| } |
| |
| void tst_QWidget::isVisibleTo() |
| { |
| // Ensure that the testWidget is hidden for this test at the |
| // start |
| QWidget testWidget; |
| testWidget.resize(m_testWidgetSize); |
| testWidget.setWindowTitle(__FUNCTION__); |
| centerOnScreen(&testWidget); |
| |
| QWidget* childWidget = new QWidget( &testWidget ); |
| QVERIFY( childWidget->isVisibleTo( &testWidget ) ); |
| childWidget->hide(); |
| QVERIFY( !childWidget->isVisibleTo( &testWidget ) ); |
| |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QVERIFY( !grandChildWidget->isVisibleTo( &testWidget ) ); |
| QVERIFY( grandChildWidget->isVisibleTo( childWidget ) ); |
| |
| testWidget.show(); |
| childWidget->show(); |
| |
| QVERIFY( childWidget->isVisibleTo( &testWidget ) ); |
| grandChildWidget->hide(); |
| QVERIFY( !grandChildWidget->isVisibleTo( childWidget ) ); |
| QVERIFY( !grandChildWidget->isVisibleTo( &testWidget ) ); |
| } |
| |
| void tst_QWidget::isHidden() |
| { |
| // Ensure that the testWidget is hidden for this test at the |
| // start |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| |
| QVERIFY( testWidget->isHidden() ); |
| QWidget* childWidget = new QWidget( testWidget.data() ); |
| QVERIFY( !childWidget->isHidden() ); |
| |
| testWidget->show(); |
| QVERIFY( !testWidget->isHidden() ); |
| QVERIFY( !childWidget->isHidden() ); |
| |
| QWidget* grandChildWidget = new QWidget( childWidget ); |
| QVERIFY( grandChildWidget->isHidden() ); |
| grandChildWidget->show(); |
| QVERIFY( !grandChildWidget->isHidden() ); |
| |
| grandChildWidget->hide(); |
| testWidget->hide(); |
| testWidget->show(); |
| QVERIFY( grandChildWidget->isHidden() ); |
| QVERIFY( !testWidget->isHidden() ); |
| QVERIFY( !childWidget->isHidden() ); |
| |
| grandChildWidget->show(); |
| childWidget->hide(); |
| testWidget->hide(); |
| testWidget->show(); |
| QVERIFY( !testWidget->isHidden() ); |
| QVERIFY( childWidget->isHidden() ); |
| QVERIFY( !grandChildWidget->isHidden() ); |
| |
| grandChildWidget->show(); |
| QVERIFY( !grandChildWidget->isHidden() ); |
| } |
| |
| void tst_QWidget::fonts() |
| { |
| QWidget testWidget; |
| testWidget.resize(m_testWidgetSize); |
| testWidget.setWindowTitle(__FUNCTION__); |
| centerOnScreen(&testWidget); |
| testWidget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); |
| |
| // Tests setFont(), ownFont() and unsetFont() |
| QWidget* cleanTestWidget = new QWidget( &testWidget ); |
| QFont originalFont = cleanTestWidget->font(); |
| |
| QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) ); |
| cleanTestWidget->setFont(QFont()); |
| QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) ); |
| |
| QFont newFont( "times", 18 ); |
| cleanTestWidget->setFont( newFont ); |
| newFont = newFont.resolve( testWidget.font() ); |
| |
| QVERIFY( cleanTestWidget->testAttribute(Qt::WA_SetFont) ); |
| QVERIFY2( cleanTestWidget->font() == newFont, |
| msgComparisonFailed(cleanTestWidget->font(), "==", newFont)); |
| |
| cleanTestWidget->setFont(QFont()); |
| QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) ); |
| QVERIFY2( cleanTestWidget->font() == originalFont, |
| msgComparisonFailed(cleanTestWidget->font(), "==", originalFont)); |
| } |
| |
| void tst_QWidget::mapFromAndTo_data() |
| { |
| QTest::addColumn<bool>("windowHidden"); |
| QTest::addColumn<bool>("subWindow1Hidden"); |
| QTest::addColumn<bool>("subWindow2Hidden"); |
| QTest::addColumn<bool>("subSubWindowHidden"); |
| QTest::addColumn<bool>("windowMinimized"); |
| QTest::addColumn<bool>("subWindow1Minimized"); |
| |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 1") << false << false << false << false << false << false; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 1") << true << false << false << false << false << false; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 1") << false << true << false << false << false << false; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 1") << true << true << false << false << false << false; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 1") << false << false << true << false << false << false; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 1") << true << false << true << false << false << false; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 1") << false << true << true << false << false << false; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 1") << true << true << true << false << false << false; |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 0") << false << false << false << true << false << false; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 0") << true << false << false << true << false << false; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 0") << false << true << false << true << false << false; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 0") << true << true << false << true << false << false; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 0") << false << false << true << true << false << false; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 0") << true << false << true << true << false << false; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 0") << false << true << true << true << false << false; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 0") << true << true << true << true << false << false; |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 1 windowMinimized") << false << false << false << false << true << false; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 1 windowMinimized") << true << false << false << false << true << false; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 1 windowMinimized") << false << true << false << false << true << false; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 1 windowMinimized") << true << true << false << false << true << false; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 1 windowMinimized") << false << false << true << false << true << false; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 1 windowMinimized") << true << false << true << false << true << false; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 1 windowMinimized") << false << true << true << false << true << false; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 1 windowMinimized") << true << true << true << false << true << false; |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 0 windowMinimized") << false << false << false << true << true << false; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 0 windowMinimized") << true << false << false << true << true << false; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 0 windowMinimized") << false << true << false << true << true << false; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 0 windowMinimized") << true << true << false << true << true << false; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 0 windowMinimized") << false << false << true << true << true << false; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 0 windowMinimized") << true << false << true << true << true << false; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 0 windowMinimized") << false << true << true << true << true << false; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 0 windowMinimized") << true << true << true << true << true << false; |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 1 subWindow1Minimized") << false << false << false << false << false << true; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 1 subWindow1Minimized") << true << false << false << false << false << true; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 1 subWindow1Minimized") << false << true << false << false << false << true; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 1 subWindow1Minimized") << true << true << false << false << false << true; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 1 subWindow1Minimized") << false << false << true << false << false << true; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 1 subWindow1Minimized") << true << false << true << false << false << true; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 1 subWindow1Minimized") << false << true << true << false << false << true; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 1 subWindow1Minimized") << true << true << true << false << false << true; |
| QTest::newRow("window 1 sub1 1 sub2 1 subsub 0 subWindow1Minimized") << false << false << false << true << false << true; |
| QTest::newRow("window 0 sub1 1 sub2 1 subsub 0 subWindow1Minimized") << true << false << false << true << false << true; |
| QTest::newRow("window 1 sub1 0 sub2 1 subsub 0 subWindow1Minimized") << false << true << false << true << false << true; |
| QTest::newRow("window 0 sub1 0 sub2 1 subsub 0 subWindow1Minimized") << true << true << false << true << false << true; |
| QTest::newRow("window 1 sub1 1 sub2 0 subsub 0 subWindow1Minimized") << false << false << true << true << false << true; |
| QTest::newRow("window 0 sub1 1 sub2 0 subsub 0 subWindow1Minimized") << true << false << true << true << false << true; |
| QTest::newRow("window 1 sub1 0 sub2 0 subsub 0 subWindow1Minimized") << false << true << true << true << false << true; |
| QTest::newRow("window 0 sub1 0 sub2 0 subsub 0 subWindow1Minimized") << true << true << true << true << false << true; |
| |
| |
| } |
| |
| void tst_QWidget::mapFromAndTo() |
| { |
| QFETCH(bool, windowHidden); |
| QFETCH(bool, subWindow1Hidden); |
| QFETCH(bool, subWindow2Hidden); |
| QFETCH(bool, subSubWindowHidden); |
| QFETCH(bool, windowMinimized); |
| QFETCH(bool, subWindow1Minimized); |
| |
| // create a toplevel and two overlapping siblings |
| QWidget window; |
| window.setObjectName(QStringLiteral("mapFromAndTo")); |
| window.setWindowTitle(window.objectName()); |
| window.setWindowFlags(window.windowFlags() | Qt::X11BypassWindowManagerHint); |
| QWidget *subWindow1 = new QWidget(&window); |
| QWidget *subWindow2 = new QWidget(&window); |
| QWidget *subSubWindow = new QWidget(subWindow1); |
| |
| // set their geometries |
| window.setGeometry(100, 100, 100, 100); |
| subWindow1->setGeometry(50, 50, 100, 100); |
| subWindow2->setGeometry(75, 75, 100, 100); |
| subSubWindow->setGeometry(10, 10, 10, 10); |
| |
| #if !defined(Q_OS_QNX) && !defined(Q_OS_WINRT) |
| //update visibility |
| if (windowMinimized) { |
| if (!windowHidden) { |
| window.showMinimized(); |
| QVERIFY(window.isMinimized()); |
| } |
| } else { |
| window.setVisible(!windowHidden); |
| } |
| if (subWindow1Minimized) { |
| subWindow1->hide(); |
| subWindow1->showMinimized(); |
| QVERIFY(subWindow1->isMinimized()); |
| } else { |
| subWindow1->setVisible(!subWindow1Hidden); |
| } |
| #else |
| Q_UNUSED(windowHidden); |
| Q_UNUSED(subWindow1Hidden); |
| Q_UNUSED(windowMinimized); |
| Q_UNUSED(subWindow1Minimized); |
| #endif |
| |
| subWindow2->setVisible(!subWindow2Hidden); |
| subSubWindow->setVisible(!subSubWindowHidden); |
| |
| // window |
| QCOMPARE(window.mapToGlobal(QPoint(0, 0)), QPoint(100, 100)); |
| QCOMPARE(window.mapToGlobal(QPoint(10, 0)), QPoint(110, 100)); |
| QCOMPARE(window.mapToGlobal(QPoint(0, 10)), QPoint(100, 110)); |
| QCOMPARE(window.mapToGlobal(QPoint(-10, 0)), QPoint(90, 100)); |
| QCOMPARE(window.mapToGlobal(QPoint(0, -10)), QPoint(100, 90)); |
| QCOMPARE(window.mapToGlobal(QPoint(100, 100)), QPoint(200, 200)); |
| QCOMPARE(window.mapToGlobal(QPoint(110, 100)), QPoint(210, 200)); |
| QCOMPARE(window.mapToGlobal(QPoint(100, 110)), QPoint(200, 210)); |
| QCOMPARE(window.mapFromGlobal(QPoint(100, 100)), QPoint(0, 0)); |
| QCOMPARE(window.mapFromGlobal(QPoint(110, 100)), QPoint(10, 0)); |
| QCOMPARE(window.mapFromGlobal(QPoint(100, 110)), QPoint(0, 10)); |
| QCOMPARE(window.mapFromGlobal(QPoint(90, 100)), QPoint(-10, 0)); |
| QCOMPARE(window.mapFromGlobal(QPoint(100, 90)), QPoint(0, -10)); |
| QCOMPARE(window.mapFromGlobal(QPoint(200, 200)), QPoint(100, 100)); |
| QCOMPARE(window.mapFromGlobal(QPoint(210, 200)), QPoint(110, 100)); |
| QCOMPARE(window.mapFromGlobal(QPoint(200, 210)), QPoint(100, 110)); |
| QCOMPARE(window.mapToParent(QPoint(0, 0)), QPoint(100, 100)); |
| QCOMPARE(window.mapToParent(QPoint(10, 0)), QPoint(110, 100)); |
| QCOMPARE(window.mapToParent(QPoint(0, 10)), QPoint(100, 110)); |
| QCOMPARE(window.mapToParent(QPoint(-10, 0)), QPoint(90, 100)); |
| QCOMPARE(window.mapToParent(QPoint(0, -10)), QPoint(100, 90)); |
| QCOMPARE(window.mapToParent(QPoint(100, 100)), QPoint(200, 200)); |
| QCOMPARE(window.mapToParent(QPoint(110, 100)), QPoint(210, 200)); |
| QCOMPARE(window.mapToParent(QPoint(100, 110)), QPoint(200, 210)); |
| QCOMPARE(window.mapFromParent(QPoint(100, 100)), QPoint(0, 0)); |
| QCOMPARE(window.mapFromParent(QPoint(110, 100)), QPoint(10, 0)); |
| QCOMPARE(window.mapFromParent(QPoint(100, 110)), QPoint(0, 10)); |
| QCOMPARE(window.mapFromParent(QPoint(90, 100)), QPoint(-10, 0)); |
| QCOMPARE(window.mapFromParent(QPoint(100, 90)), QPoint(0, -10)); |
| QCOMPARE(window.mapFromParent(QPoint(200, 200)), QPoint(100, 100)); |
| QCOMPARE(window.mapFromParent(QPoint(210, 200)), QPoint(110, 100)); |
| QCOMPARE(window.mapFromParent(QPoint(200, 210)), QPoint(100, 110)); |
| |
| // first subwindow |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(0, 0)), QPoint(150, 150)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(10, 0)), QPoint(160, 150)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(0, 10)), QPoint(150, 160)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(-10, 0)), QPoint(140, 150)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(0, -10)), QPoint(150, 140)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(100, 100)), QPoint(250, 250)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(110, 100)), QPoint(260, 250)); |
| QCOMPARE(subWindow1->mapToGlobal(QPoint(100, 110)), QPoint(250, 260)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 150)), QPoint(0, 0)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(160, 150)), QPoint(10, 0)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 160)), QPoint(0, 10)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(140, 150)), QPoint(-10, 0)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(150, 140)), QPoint(0, -10)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(250, 250)), QPoint(100, 100)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(260, 250)), QPoint(110, 100)); |
| QCOMPARE(subWindow1->mapFromGlobal(QPoint(250, 260)), QPoint(100, 110)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(0, 0)), QPoint(50, 50)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(10, 0)), QPoint(60, 50)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(0, 10)), QPoint(50, 60)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(-10, 0)), QPoint(40, 50)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(0, -10)), QPoint(50, 40)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(100, 100)), QPoint(150, 150)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(110, 100)), QPoint(160, 150)); |
| QCOMPARE(subWindow1->mapToParent(QPoint(100, 110)), QPoint(150, 160)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(50, 50)), QPoint(0, 0)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(60, 50)), QPoint(10, 0)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(50, 60)), QPoint(0, 10)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(40, 50)), QPoint(-10, 0)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(50, 40)), QPoint(0, -10)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(150, 150)), QPoint(100, 100)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(160, 150)), QPoint(110, 100)); |
| QCOMPARE(subWindow1->mapFromParent(QPoint(150, 160)), QPoint(100, 110)); |
| |
| // subsubwindow |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, 0)), QPoint(160, 160)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(10, 0)), QPoint(170, 160)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, 10)), QPoint(160, 170)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(-10, 0)), QPoint(150, 160)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(0, -10)), QPoint(160, 150)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(100, 100)), QPoint(260, 260)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(110, 100)), QPoint(270, 260)); |
| QCOMPARE(subSubWindow->mapToGlobal(QPoint(100, 110)), QPoint(260, 270)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 160)), QPoint(0, 0)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(170, 160)), QPoint(10, 0)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 170)), QPoint(0, 10)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(150, 160)), QPoint(-10, 0)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(160, 150)), QPoint(0, -10)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(260, 260)), QPoint(100, 100)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(270, 260)), QPoint(110, 100)); |
| QCOMPARE(subSubWindow->mapFromGlobal(QPoint(260, 270)), QPoint(100, 110)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(0, 0)), QPoint(10, 10)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(10, 0)), QPoint(20, 10)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(0, 10)), QPoint(10, 20)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(-10, 0)), QPoint(0, 10)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(0, -10)), QPoint(10, 0)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(100, 100)), QPoint(110, 110)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(110, 100)), QPoint(120, 110)); |
| QCOMPARE(subSubWindow->mapToParent(QPoint(100, 110)), QPoint(110, 120)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 10)), QPoint(0, 0)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(20, 10)), QPoint(10, 0)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 20)), QPoint(0, 10)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(0, 10)), QPoint(-10, 0)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(10, 0)), QPoint(0, -10)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(110, 110)), QPoint(100, 100)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(120, 110)), QPoint(110, 100)); |
| QCOMPARE(subSubWindow->mapFromParent(QPoint(110, 120)), QPoint(100, 110)); |
| } |
| |
| void tst_QWidget::focusChainOnReparent() |
| { |
| QWidget window; |
| QWidget *child1 = new QWidget(&window); |
| QWidget *child2 = new QWidget(&window); |
| QWidget *child3 = new QWidget(&window); |
| QWidget *child21 = new QWidget(child2); |
| QWidget *child22 = new QWidget(child2); |
| QWidget *child4 = new QWidget(&window); |
| |
| QWidget *expectedOriginalChain[8] = {&window, child1, child2, child3, child21, child22, child4, &window}; |
| QWidget *w = &window; |
| for (auto expectedOriginal : expectedOriginalChain) { |
| QCOMPARE(w, expectedOriginal); |
| w = w->nextInFocusChain(); |
| } |
| for (int i = 7; i >= 0; --i) { |
| w = w->previousInFocusChain(); |
| QCOMPARE(w, expectedOriginalChain[i]); |
| } |
| |
| QWidget window2; |
| child2->setParent(&window2); |
| |
| QWidget *expectedNewChain[5] = {&window2, child2, child21, child22, &window2}; |
| w = &window2; |
| for (auto expectedNew : expectedNewChain) { |
| QCOMPARE(w, expectedNew); |
| w = w->nextInFocusChain(); |
| } |
| for (int i = 4; i >= 0; --i) { |
| w = w->previousInFocusChain(); |
| QCOMPARE(w, expectedNewChain[i]); |
| } |
| |
| QWidget *expectedOldChain[5] = {&window, child1, child3, child4, &window}; |
| w = &window; |
| for (auto expectedOld : expectedOldChain) { |
| QCOMPARE(w, expectedOld); |
| w = w->nextInFocusChain(); |
| } |
| for (int i = 4; i >= 0; --i) { |
| w = w->previousInFocusChain(); |
| QCOMPARE(w, expectedOldChain[i]); |
| } |
| } |
| |
| |
| void tst_QWidget::focusChainOnHide() |
| { |
| // focus should move to the next widget in the focus chain when we hide it. |
| QScopedPointer<QWidget> parent(new QWidget()); |
| parent->setObjectName(QLatin1String("focusChainOnHide")); |
| parent->resize(200, 200); |
| parent->setWindowTitle(parent->objectName()); |
| parent->setFocusPolicy(Qt::StrongFocus); |
| QWidget *child = new QWidget(parent.data()); |
| child->setObjectName(QLatin1String("child")); |
| child->setFocusPolicy(Qt::StrongFocus); |
| QWidget::setTabOrder(child, parent.data()); |
| |
| parent->show(); |
| QApplication::setActiveWindow(parent->window()); |
| child->activateWindow(); |
| child->setFocus(); |
| |
| QTRY_VERIFY(child->hasFocus()); |
| child->hide(); |
| |
| QTRY_VERIFY(parent->hasFocus()); |
| QCOMPARE(parent.data(), QApplication::focusWidget()); |
| } |
| |
| class Container : public QWidget |
| { |
| public: |
| QVBoxLayout* box; |
| |
| Container() |
| { |
| box = new QVBoxLayout(this); |
| //(new QVBoxLayout(this))->setAutoAdd(true); |
| } |
| |
| void tab() |
| { |
| focusNextPrevChild(true); |
| } |
| |
| void backTab() |
| { |
| focusNextPrevChild(false); |
| } |
| }; |
| |
| class Composite : public QFrame |
| { |
| public: |
| explicit Composite(QWidget *parent = nullptr, const QString &name = QString()) |
| : QFrame(parent) |
| { |
| setObjectName(name); |
| |
| lineEdit1 = new QLineEdit; |
| lineEdit2 = new QLineEdit; |
| lineEdit3 = new QLineEdit; |
| lineEdit3->setEnabled(false); |
| |
| QHBoxLayout* hbox = new QHBoxLayout(this); |
| hbox->addWidget(lineEdit1); |
| hbox->addWidget(lineEdit2); |
| hbox->addWidget(lineEdit3); |
| } |
| |
| public: |
| QLineEdit *lineEdit1; |
| QLineEdit *lineEdit2; |
| QLineEdit *lineEdit3; |
| }; |
| |
| void tst_QWidget::defaultTabOrder() |
| { |
| const int compositeCount = 2; |
| Container container; |
| Composite *composite[compositeCount]; |
| |
| QLineEdit *firstEdit = new QLineEdit; |
| container.box->addWidget(firstEdit); |
| |
| for (int i = 0; i < compositeCount; i++) { |
| composite[i] = new Composite(); |
| container.box->addWidget(composite[i]); |
| } |
| |
| QLineEdit *lastEdit = new QLineEdit(); |
| container.box->addWidget(lastEdit); |
| |
| container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| container.show(); |
| container.activateWindow(); |
| QApplication::setActiveWindow(&container); |
| QVERIFY(QTest::qWaitForWindowActive(&container)); |
| |
| QTRY_VERIFY(firstEdit->hasFocus()); |
| |
| // Check that focus moves between the line edits when we tab forward |
| for (int i = 0; i < compositeCount; ++i) { |
| container.tab(); |
| QVERIFY(composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(!composite[i]->lineEdit2->hasFocus()); |
| container.tab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.tab(); |
| QVERIFY(lastEdit->hasFocus()); |
| |
| // Check that focus moves between the line edits in reverse |
| // order when we tab backwards |
| for (int i = compositeCount - 1; i >= 0; --i) { |
| container.backTab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| |
| container.backTab(); |
| QVERIFY(composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(!composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.backTab(); |
| QVERIFY(firstEdit->hasFocus()); |
| } |
| |
| void tst_QWidget::reverseTabOrder() |
| { |
| const int compositeCount = 2; |
| Container container; |
| container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| Composite* composite[compositeCount]; |
| |
| QLineEdit *firstEdit = new QLineEdit(); |
| container.box->addWidget(firstEdit); |
| |
| for (int i = 0; i < compositeCount; i++) { |
| composite[i] = new Composite(); |
| container.box->addWidget(composite[i]); |
| } |
| |
| QLineEdit *lastEdit = new QLineEdit(); |
| container.box->addWidget(lastEdit); |
| |
| // Reverse tab order inside each composite |
| for (int i = 0; i < compositeCount; ++i) |
| QWidget::setTabOrder(composite[i]->lineEdit2, composite[i]->lineEdit1); |
| |
| container.show(); |
| container.activateWindow(); |
| QApplication::setActiveWindow(&container); |
| QVERIFY(QTest::qWaitForWindowActive(&container)); |
| |
| QTRY_VERIFY(firstEdit->hasFocus()); |
| |
| // Check that focus moves in reverse order when tabbing inside the composites |
| // (but in the correct order when tabbing between them) |
| for (int i = 0; i < compositeCount; ++i) { |
| container.tab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| container.tab(); |
| QVERIFY(composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(!composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.tab(); |
| QVERIFY(lastEdit->hasFocus()); |
| |
| // Check that focus moves in "normal" order when tabbing backwards inside the |
| // composites (since backwards of reversed order cancels each other out), |
| // but in the reverse order when tabbing between them. |
| for (int i = compositeCount - 1; i >= 0; --i) { |
| container.backTab(); |
| QVERIFY(composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(!composite[i]->lineEdit2->hasFocus()); |
| container.backTab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.backTab(); |
| QVERIFY(firstEdit->hasFocus()); |
| } |
| |
| void tst_QWidget::tabOrderWithProxy() |
| { |
| const int compositeCount = 2; |
| Container container; |
| container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| Composite* composite[compositeCount]; |
| |
| QLineEdit *firstEdit = new QLineEdit(); |
| container.box->addWidget(firstEdit); |
| |
| for (int i = 0; i < compositeCount; i++) { |
| composite[i] = new Composite(); |
| container.box->addWidget(composite[i]); |
| |
| // Set second child as focus proxy |
| composite[i]->setFocusPolicy(Qt::StrongFocus); |
| composite[i]->setFocusProxy(composite[i]->lineEdit2); |
| } |
| |
| QLineEdit *lastEdit = new QLineEdit(); |
| container.box->addWidget(lastEdit); |
| |
| container.show(); |
| container.activateWindow(); |
| QApplication::setActiveWindow(&container); |
| QVERIFY(QTest::qWaitForWindowActive(&container)); |
| |
| QTRY_VERIFY(firstEdit->hasFocus()); |
| |
| // Check that focus moves between the second line edits |
| // (the focus proxies) when we tab forward |
| for (int i = 0; i < compositeCount; ++i) { |
| container.tab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.tab(); |
| QVERIFY(lastEdit->hasFocus()); |
| |
| // Check that focus moves between the line edits |
| // in reverse order when we tab backwards. |
| // Note that in this case, the focus proxies should not |
| // be taken into consideration, since they only take |
| // effect when tabbing forward |
| for (int i = compositeCount - 1; i >= 0; --i) { |
| container.backTab(); |
| QVERIFY(!composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(composite[i]->lineEdit2->hasFocus()); |
| container.backTab(); |
| QVERIFY(composite[i]->lineEdit1->hasFocus()); |
| QVERIFY(!composite[i]->lineEdit2->hasFocus()); |
| } |
| |
| container.backTab(); |
| QVERIFY(firstEdit->hasFocus()); |
| } |
| |
| void tst_QWidget::tabOrderWithCompoundWidgets() |
| { |
| const int compositeCount = 4; |
| Container container; |
| container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| Composite *composite[compositeCount]; |
| |
| QLineEdit *firstEdit = new QLineEdit(); |
| container.box->addWidget(firstEdit); |
| |
| for (int i = 0; i < compositeCount; i++) { |
| composite[i] = new Composite(nullptr, QStringLiteral("Composite: ") + QString::number(i)); |
| container.box->addWidget(composite[i]); |
| |
| // Let the composite handle focus, and set a child as focus proxy (use the second child, just |
| // to ensure that we don't just tab to the first child by coinsidence). This will make the |
| // composite "compound". Also enable the last line edit to have a bit more data to check when |
| // tabbing forwards. |
| composite[i]->setFocusPolicy(Qt::StrongFocus); |
| composite[i]->setFocusProxy(composite[i]->lineEdit2); |
| composite[i]->lineEdit3->setEnabled(true); |
| } |
| |
| QLineEdit *lastEdit = new QLineEdit(); |
| container.box->addWidget(lastEdit); |
| |
| // Reverse tab order between each composite |
| // (but not inside them), including first and last line edit. |
| // The result should not affect local tab order inside each |
| // composite, only between them. |
| QWidget::setTabOrder(lastEdit, composite[compositeCount - 1]); |
| for (int i = compositeCount - 1; i >= 1; --i) |
| QWidget::setTabOrder(composite[i], composite[i-1]); |
| QWidget::setTabOrder(composite[0], firstEdit); |
| |
| container.show(); |
| container.activateWindow(); |
| QApplication::setActiveWindow(&container); |
| QVERIFY(QTest::qWaitForWindowActive(&container)); |
| |
| lastEdit->setFocus(); |
| QTRY_VERIFY(lastEdit->hasFocus()); |
| |
| // Check that focus moves between the line edits in the normal |
| // order when tabbing inside each compound, but in the reverse |
| // order when tabbing between them. Since the composites have |
| // lineEdit2 as focus proxy, lineEdit2 will be the first with focus |
| // when the compound gets focus, and lineEdit1 will therefore be skipped. |
| for (int i = compositeCount - 1; i >= 0; --i) { |
| container.tab(); |
| Composite *c = composite[i]; |
| QVERIFY(!c->lineEdit1->hasFocus()); |
| QVERIFY(c->lineEdit2->hasFocus()); |
| QVERIFY(!c->lineEdit3->hasFocus()); |
| container.tab(); |
| QVERIFY(!c->lineEdit1->hasFocus()); |
| QVERIFY(!c->lineEdit2->hasFocus()); |
| QVERIFY(c->lineEdit3->hasFocus()); |
| } |
| |
| container.tab(); |
| QVERIFY(firstEdit->hasFocus()); |
| |
| // Check that focus moves in reverse order when backTab inside the composites, but |
| // in the 'correct' order when backTab between them (since the composites are in reverse tab |
| // order from before, which cancels it out). Note that when we backtab into a compound, we start |
| // at lineEdit3 rather than the focus proxy, since that is the reverse of what happens when we tab |
| // forward. And this time we will also backtab to lineEdit1, since there is no focus proxy that interferes. |
| for (int i = 0; i < compositeCount; ++i) { |
| container.backTab(); |
| Composite *c = composite[i]; |
| QVERIFY(!c->lineEdit1->hasFocus()); |
| QVERIFY(!c->lineEdit2->hasFocus()); |
| QVERIFY(c->lineEdit3->hasFocus()); |
| container.backTab(); |
| QVERIFY(!c->lineEdit1->hasFocus()); |
| QVERIFY(c->lineEdit2->hasFocus()); |
| QVERIFY(!c->lineEdit3->hasFocus()); |
| container.backTab(); |
| QVERIFY(c->lineEdit1->hasFocus()); |
| QVERIFY(!c->lineEdit2->hasFocus()); |
| QVERIFY(!c->lineEdit3->hasFocus()); |
| } |
| |
| container.backTab(); |
| QVERIFY(lastEdit->hasFocus()); |
| } |
| |
| static QVector<QWidget*> getFocusChain(QWidget *start, bool bForward) |
| { |
| QVector<QWidget*> ret; |
| QWidget *cur = start; |
| do { |
| ret += cur; |
| auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur)); |
| cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; |
| } while (cur != start); |
| return ret; |
| } |
| |
| //#define DEBUG_FOCUS_CHAIN |
| static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr) |
| { |
| #ifdef DEBUG_FOCUS_CHAIN |
| qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc; |
| QWidget *cur = start; |
| do { |
| qDebug() << cur; |
| auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur)); |
| cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; |
| } while (cur != start); |
| #else |
| Q_UNUSED(start) |
| Q_UNUSED(bForward) |
| Q_UNUSED(desc) |
| #endif |
| } |
| |
| void tst_QWidget::tabOrderNoChange() |
| { |
| QWidget w; |
| auto *verticalLayout = new QVBoxLayout(&w); |
| auto *tabWidget = new QTabWidget(&w); |
| auto *tv = new QTreeView(tabWidget); |
| tabWidget->addTab(tv, QStringLiteral("Tab 1")); |
| verticalLayout->addWidget(tabWidget); |
| |
| const auto focusChainForward = getFocusChain(&w, true); |
| const auto focusChainBackward = getFocusChain(&w, false); |
| dumpFocusChain(&w, true); |
| QWidget::setTabOrder(tabWidget, tv); |
| dumpFocusChain(&w, true); |
| QCOMPARE(focusChainForward, getFocusChain(&w, true)); |
| QCOMPARE(focusChainBackward, getFocusChain(&w, false)); |
| } |
| |
| void tst_QWidget::tabOrderNoChange2() |
| { |
| QWidget w; |
| auto *verticalLayout = new QVBoxLayout(&w); |
| auto *tabWidget = new QTabWidget(&w); |
| tabWidget->setObjectName("tabWidget"); |
| verticalLayout->addWidget(tabWidget); |
| |
| auto *tab1 = new QWidget(tabWidget); |
| tab1->setObjectName("tab1"); |
| auto *vLay1 = new QVBoxLayout(tab1); |
| auto *le1 = new QLineEdit(tab1); |
| le1->setObjectName("le1"); |
| auto *le2 = new QLineEdit(tab1); |
| le2->setObjectName("le2"); |
| vLay1->addWidget(le1); |
| vLay1->addWidget(le2); |
| tabWidget->addTab(tab1, QStringLiteral("Tab 1")); |
| |
| auto *tab2 = new QWidget(tabWidget); |
| tab2->setObjectName("tab2"); |
| auto *vLay2 = new QVBoxLayout(tab2); |
| auto *le3 = new QLineEdit(tab2); |
| le3->setObjectName("le3"); |
| auto *le4 = new QLineEdit(tab2); |
| le4->setObjectName("le4"); |
| vLay2->addWidget(le3); |
| vLay2->addWidget(le4); |
| tabWidget->addTab(tab2, QStringLiteral("Tab 2")); |
| |
| const auto focusChainForward = getFocusChain(&w, true); |
| const auto focusChainBackward = getFocusChain(&w, false); |
| dumpFocusChain(&w, true); |
| dumpFocusChain(&w, false); |
| // this will screw up the focus chain order without visible changes, |
| // so don't call it here for the simplicity of the test |
| //QWidget::setTabOrder(tabWidget, le1); |
| |
| QWidget::setTabOrder(le1, le2); |
| dumpFocusChain(&w, true, "QWidget::setTabOrder(le1, le2)"); |
| QWidget::setTabOrder(le2, le3); |
| dumpFocusChain(&w, true, "QWidget::setTabOrder(le2, le3)"); |
| QWidget::setTabOrder(le3, le4); |
| dumpFocusChain(&w, true, "QWidget::setTabOrder(le3, le4)"); |
| QWidget::setTabOrder(le4, tabWidget); |
| dumpFocusChain(&w, true, "QWidget::setTabOrder(le4, tabWidget)"); |
| dumpFocusChain(&w, false); |
| |
| QCOMPARE(focusChainForward, getFocusChain(&w, true)); |
| QCOMPARE(focusChainBackward, getFocusChain(&w, false)); |
| } |
| |
| void tst_QWidget::appFocusWidgetWithFocusProxyLater() |
| { |
| // Given a lineedit without a focus proxy |
| QWidget window; |
| window.setWindowTitle(QTest::currentTestFunction()); |
| QLineEdit *lineEditFocusProxy = new QLineEdit(&window); |
| QLineEdit *lineEdit = new QLineEdit(&window); |
| lineEdit->setFocus(); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| QCOMPARE(QApplication::focusWidget(), lineEdit); |
| |
| // When setting a focus proxy for the focus widget (like QWebEngineView does) |
| lineEdit->setFocusProxy(lineEditFocusProxy); |
| |
| // Then the focus widget should be updated |
| QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); |
| |
| // So that deleting the lineEdit and later the window, doesn't crash |
| delete lineEdit; |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| void tst_QWidget::appFocusWidgetWhenLosingFocusProxy() |
| { |
| // Given a lineedit with a focus proxy |
| QWidget window; |
| window.setWindowTitle(QTest::currentTestFunction()); |
| QLineEdit *lineEditFocusProxy = new QLineEdit(&window); |
| QLineEdit *lineEdit = new QLineEdit(&window); |
| lineEdit->setFocusProxy(lineEditFocusProxy); |
| lineEdit->setFocus(); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); |
| |
| // When unsetting the focus proxy |
| lineEdit->setFocusProxy(nullptr); |
| |
| // Then the application focus widget should be back to the lineedit |
| QCOMPARE(QApplication::focusWidget(), lineEdit); |
| } |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| void tst_QWidget::activation() |
| { |
| Q_CHECK_PAINTEVENTS |
| |
| QWidget widget1; |
| widget1.setObjectName("activation-Widget1"); |
| widget1.setWindowTitle(widget1.objectName()); |
| |
| QWidget widget2; |
| widget1.setObjectName("activation-Widget2"); |
| widget1.setWindowTitle(widget2.objectName()); |
| |
| widget1.show(); |
| widget2.show(); |
| |
| QTRY_COMPARE(QApplication::activeWindow(), &widget2); |
| widget2.showMinimized(); |
| |
| QTRY_COMPARE(QApplication::activeWindow(), &widget1); |
| widget2.showMaximized(); |
| QTRY_COMPARE(QApplication::activeWindow(), &widget2); |
| widget2.showMinimized(); |
| QTRY_COMPARE(QApplication::activeWindow(), &widget1); |
| widget2.showNormal(); |
| QTRY_COMPARE(QApplication::activeWindow(), &widget2); |
| widget2.hide(); |
| QTRY_COMPARE(QApplication::activeWindow(), &widget1); |
| } |
| #endif // Q_OS_WIN |
| |
| void tst_QWidget::windowState() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("X11: Many window managers do not support window state properly, which causes this test to fail."); |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| |
| QPoint pos; |
| QSize size = m_testWidgetSize; |
| if (QGuiApplicationPrivate::platformIntegration()->defaultWindowState(Qt::Widget) |
| == Qt::WindowFullScreen |
| || m_platform == QStringLiteral("winrt")) { |
| size = QGuiApplication::primaryScreen()->size(); |
| } else { |
| pos = QPoint(10, 10); |
| } |
| |
| QWidget widget1; |
| widget1.move(pos); |
| widget1.resize(size); |
| QCOMPARE(widget1.pos(), pos); |
| QCOMPARE(widget1.size(), size); |
| QTest::qWait(100); |
| widget1.setObjectName(QStringLiteral("windowState-Widget1")); |
| widget1.setWindowTitle(widget1.objectName()); |
| QCOMPARE(widget1.pos(), pos); |
| QCOMPARE(widget1.size(), size); |
| |
| #define VERIFY_STATE(s) \ |
| QCOMPARE(int(widget1.windowState() & stateMask), int(s)); \ |
| QCOMPARE(int(widget1.windowHandle()->windowStates() & stateMask), int(s)) |
| |
| const auto stateMask = Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen; |
| |
| widget1.setWindowState(Qt::WindowMaximized); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowMaximized); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized); |
| |
| widget1.setVisible(true); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowMaximized); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(100); |
| QVERIFY(!(widget1.windowState() & Qt::WindowMaximized)); |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos))); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState); |
| |
| widget1.setWindowState(Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowMinimized); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized); |
| |
| widget1.setWindowState(widget1.windowState() | Qt::WindowMaximized); |
| QTest::qWait(100); |
| VERIFY_STATE((Qt::WindowMinimized|Qt::WindowMaximized)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowMaximized); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(100); |
| QVERIFY(!(widget1.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized))); |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos))); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState); |
| |
| widget1.setWindowState(Qt::WindowFullScreen); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowFullScreen); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMinimized)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowFullScreen); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen); |
| |
| widget1.setWindowState(Qt::WindowNoState); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowNoState); |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos))); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState); |
| |
| widget1.setWindowState(Qt::WindowFullScreen); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowFullScreen); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(100); |
| VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized|Qt::WindowMinimized)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMinimized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(100); |
| VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowFullScreen); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowFullScreen); |
| QTest::qWait(100); |
| VERIFY_STATE(Qt::WindowMaximized); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowMaximized); |
| |
| widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(100); |
| QVERIFY(!(widget1.windowState() & stateMask)); |
| QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState); |
| |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos))); |
| QTRY_COMPARE(widget1.size(), size); |
| } |
| |
| void tst_QWidget::showMaximized() |
| { |
| QWidget plain; |
| QHBoxLayout *layout; |
| layout = new QHBoxLayout; |
| QWidget layouted; |
| QLineEdit le; |
| QLineEdit le2; |
| QLineEdit le3; |
| |
| layout->addWidget(&le); |
| layout->addWidget(&le2); |
| layout->addWidget(&le3); |
| |
| layouted.setLayout(layout); |
| |
| plain.showMaximized(); |
| QVERIFY(plain.windowState() & Qt::WindowMaximized); |
| |
| plain.showNormal(); |
| QVERIFY(!(plain.windowState() & Qt::WindowMaximized)); |
| |
| layouted.showMaximized(); |
| QVERIFY(layouted.windowState() & Qt::WindowMaximized); |
| |
| layouted.showNormal(); |
| QVERIFY(!(layouted.windowState() & Qt::WindowMaximized)); |
| |
| // ### fixme: embedded may choose a different size to fit on the screen. |
| if (layouted.size() != layouted.sizeHint()) |
| QEXPECT_FAIL("", "QTBUG-22326", Continue); |
| QCOMPARE(layouted.size(), layouted.sizeHint()); |
| |
| layouted.showMaximized(); |
| QVERIFY(layouted.isMaximized()); |
| QVERIFY(layouted.isVisible()); |
| |
| layouted.hide(); |
| QVERIFY(layouted.isMaximized()); |
| QVERIFY(!layouted.isVisible()); |
| |
| layouted.showMaximized(); |
| QVERIFY(layouted.isMaximized()); |
| QVERIFY(layouted.isVisible()); |
| |
| layouted.showMinimized(); |
| QVERIFY(layouted.isMinimized()); |
| QVERIFY(layouted.isMaximized()); |
| |
| layouted.showMaximized(); |
| QVERIFY(!layouted.isMinimized()); |
| QVERIFY(layouted.isMaximized()); |
| QVERIFY(layouted.isVisible()); |
| |
| layouted.showMinimized(); |
| QVERIFY(layouted.isMinimized()); |
| QVERIFY(layouted.isMaximized()); |
| |
| layouted.showMaximized(); |
| QVERIFY(!layouted.isMinimized()); |
| QVERIFY(layouted.isMaximized()); |
| QVERIFY(layouted.isVisible()); |
| |
| { |
| QWidget frame; |
| QWidget widget(&frame); |
| widget.showMaximized(); |
| QVERIFY(widget.isMaximized()); |
| } |
| |
| { |
| QWidget widget; |
| setFrameless(&widget); |
| widget.setGeometry(0, 0, 10, 10); |
| widget.showMaximized(); |
| QTRY_VERIFY(widget.size().width() > 20 && widget.size().height() > 20); |
| } |
| } |
| |
| void tst_QWidget::showFullScreen() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT: This fails. QTBUG-68297"); |
| QWidget plain; |
| QHBoxLayout *layout; |
| QWidget layouted; |
| QLineEdit le; |
| QLineEdit le2; |
| QLineEdit le3; |
| layout = new QHBoxLayout; |
| |
| layout->addWidget(&le); |
| layout->addWidget(&le2); |
| layout->addWidget(&le3); |
| |
| layouted.setLayout(layout); |
| |
| plain.showFullScreen(); |
| QVERIFY(plain.windowState() & Qt::WindowFullScreen); |
| QVERIFY(plain.windowHandle()); |
| QVERIFY(plain.windowHandle()->screen()); |
| const QRect expectedFullScreenGeometry = plain.windowHandle()->screen()->geometry(); |
| QTRY_COMPARE(plain.geometry(), expectedFullScreenGeometry); |
| |
| plain.showNormal(); |
| QVERIFY(!(plain.windowState() & Qt::WindowFullScreen)); |
| |
| layouted.showFullScreen(); |
| QVERIFY(layouted.windowState() & Qt::WindowFullScreen); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| |
| layouted.showNormal(); |
| QVERIFY(!(layouted.windowState() & Qt::WindowFullScreen)); |
| |
| // ### fixme: embedded may choose a different size to fit on the screen. |
| if (layouted.size() != layouted.sizeHint()) |
| QEXPECT_FAIL("", "QTBUG-22326", Continue); |
| QCOMPARE(layouted.size(), layouted.sizeHint()); |
| |
| layouted.showFullScreen(); |
| QVERIFY(layouted.isFullScreen()); |
| QVERIFY(layouted.isVisible()); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| |
| layouted.hide(); |
| QVERIFY(layouted.isFullScreen()); |
| QVERIFY(!layouted.isVisible()); |
| |
| layouted.showFullScreen(); |
| QVERIFY(layouted.isFullScreen()); |
| QVERIFY(layouted.isVisible()); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| |
| layouted.showMinimized(); |
| QVERIFY(layouted.isMinimized()); |
| QVERIFY(layouted.isFullScreen()); |
| |
| layouted.showFullScreen(); |
| QVERIFY(!layouted.isMinimized()); |
| QVERIFY(layouted.isFullScreen()); |
| QVERIFY(layouted.isVisible()); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| |
| layouted.showMinimized(); |
| QVERIFY(layouted.isMinimized()); |
| QVERIFY(layouted.isFullScreen()); |
| |
| layouted.showFullScreen(); |
| QVERIFY(!layouted.isMinimized()); |
| QVERIFY(layouted.isFullScreen()); |
| QVERIFY(layouted.isVisible()); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| |
| { |
| QWidget frame; |
| QWidget widget(&frame); |
| widget.showFullScreen(); |
| QVERIFY(widget.isFullScreen()); |
| QTRY_COMPARE(layouted.geometry(), expectedFullScreenGeometry); |
| } |
| } |
| |
| class ResizeWidget : public QWidget { |
| public: |
| explicit ResizeWidget(QWidget *p = nullptr) : QWidget(p) |
| { |
| setObjectName(QLatin1String("ResizeWidget")); |
| setWindowTitle(objectName()); |
| } |
| protected: |
| void resizeEvent(QResizeEvent *e) override |
| { |
| QCOMPARE(size(), e->size()); |
| ++m_resizeEventCount; |
| } |
| |
| public: |
| int m_resizeEventCount = 0; |
| }; |
| |
| void tst_QWidget::resizeEvent() |
| { |
| { |
| QWidget wParent; |
| wParent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| wParent.resize(m_testWidgetSize); |
| ResizeWidget wChild(&wParent); |
| wParent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&wParent)); |
| QCOMPARE (wChild.m_resizeEventCount, 1); // initial resize event before paint |
| wParent.hide(); |
| QSize safeSize(640,480); |
| if (wChild.size() == safeSize) |
| safeSize.setWidth(639); |
| wChild.resize(safeSize); |
| QCOMPARE (wChild.m_resizeEventCount, 1); |
| wParent.show(); |
| QCOMPARE (wChild.m_resizeEventCount, 2); |
| } |
| |
| { |
| ResizeWidget wTopLevel; |
| wTopLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| wTopLevel.resize(m_testWidgetSize); |
| wTopLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel)); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support resize", Abort); |
| QCOMPARE (wTopLevel.m_resizeEventCount, 1); // initial resize event before paint for toplevels |
| wTopLevel.hide(); |
| QSize safeSize(640,480); |
| if (wTopLevel.size() == safeSize) |
| safeSize.setWidth(639); |
| wTopLevel.resize(safeSize); |
| QCOMPARE (wTopLevel.m_resizeEventCount, 1); |
| wTopLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel)); |
| QCOMPARE (wTopLevel.m_resizeEventCount, 2); |
| } |
| } |
| |
| void tst_QWidget::showMinimized() |
| { |
| if (m_platform == QStringLiteral("wayland")) { |
| QSKIP("Wayland: Neither xdg_shell, wl_shell or ivi_application support " |
| "letting a client know whether it's minimized. So on these shells " |
| "Qt Wayland will always report that it's unmimized."); |
| } |
| |
| QWidget plain; |
| plain.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| plain.move(100, 100); |
| plain.resize(200, 200); |
| QPoint pos = plain.pos(); |
| |
| plain.showMinimized(); |
| QVERIFY(plain.isMinimized()); |
| QVERIFY(plain.isVisible()); |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "Winrt does not support move and resize", Abort); |
| #endif |
| QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos))); |
| |
| plain.showNormal(); |
| QVERIFY(!plain.isMinimized()); |
| QVERIFY(plain.isVisible()); |
| QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos))); |
| |
| plain.showMinimized(); |
| QVERIFY(plain.isMinimized()); |
| QVERIFY(plain.isVisible()); |
| QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos))); |
| |
| plain.hide(); |
| QVERIFY(plain.isMinimized()); |
| QVERIFY(!plain.isVisible()); |
| |
| plain.showMinimized(); |
| QVERIFY(plain.isMinimized()); |
| QVERIFY(plain.isVisible()); |
| |
| plain.setGeometry(200, 200, 300, 300); |
| plain.showNormal(); |
| QCOMPARE(plain.geometry(), QRect(200, 200, 300, 300)); |
| |
| { |
| QWidget frame; |
| QWidget widget(&frame); |
| widget.showMinimized(); |
| QVERIFY(widget.isMinimized()); |
| } |
| } |
| |
| void tst_QWidget::showMinimizedKeepsFocus() |
| { |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) |
| QSKIP("Window activation is not supported."); |
| if (m_platform == QStringLiteral("offscreen")) |
| QSKIP("Platform offscreen does not support showMinimized()"); |
| |
| //here we test that minimizing a widget and restoring it doesn't change the focus inside of it |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget child1(&window), child2(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| child2.setFocus(); |
| |
| QTRY_COMPARE(window.focusWidget(), &child2); |
| QTRY_COMPARE(QApplication::focusWidget(), &child2); |
| |
| window.showMinimized(); |
| QTRY_VERIFY(window.isMinimized()); |
| QTRY_COMPARE(window.focusWidget(), &child2); |
| |
| window.showNormal(); |
| QTest::qWait(10); |
| QTRY_COMPARE(window.focusWidget(), &child2); |
| } |
| |
| //testing deletion of the focusWidget |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget *child = new QWidget(&window); |
| child->setFocusPolicy(Qt::StrongFocus); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| child->setFocus(); |
| QTRY_COMPARE(window.focusWidget(), child); |
| QTRY_COMPARE(QApplication::focusWidget(), child); |
| |
| delete child; |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| //testing reparenting the focus widget |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget *child = new QWidget(&window); |
| child->setFocusPolicy(Qt::StrongFocus); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| child->setFocus(); |
| QTRY_COMPARE(window.focusWidget(), child); |
| QTRY_COMPARE(QApplication::focusWidget(), child); |
| |
| child->setParent(nullptr); |
| QScopedPointer<QWidget> childGuard(child); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| //testing setEnabled(false) |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget *child = new QWidget(&window); |
| child->setFocusPolicy(Qt::StrongFocus); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| child->setFocus(); |
| QTRY_COMPARE(window.focusWidget(), child); |
| QTRY_COMPARE(QApplication::focusWidget(), child); |
| |
| child->setEnabled(false); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| //testing clearFocus |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget *firstchild = new QWidget(&window); |
| firstchild->setFocusPolicy(Qt::StrongFocus); |
| QWidget *child = new QWidget(&window); |
| child->setFocusPolicy(Qt::StrongFocus); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| child->setFocus(); |
| QTRY_COMPARE(window.focusWidget(), child); |
| QTRY_COMPARE(QApplication::focusWidget(), child); |
| |
| child->clearFocus(); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| window.showMinimized(); |
| QTest::qWait(30); |
| QTRY_VERIFY(window.isMinimized()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QTRY_COMPARE(QApplication::focusWidget(), nullptr); |
| |
| window.showNormal(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| #ifdef Q_OS_OSX |
| if (!macHasAccessToWindowsServer()) |
| QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue); |
| #elif defined(Q_OS_WINRT) |
| QEXPECT_FAIL("", "Winrt fails here - QTBUG-68297", Continue); |
| #endif |
| QTRY_COMPARE(window.focusWidget(), firstchild); |
| #ifdef Q_OS_OSX |
| if (!macHasAccessToWindowsServer()) |
| QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue); |
| #elif defined(Q_OS_WINRT) |
| QEXPECT_FAIL("", "Winrt fails here - QTBUG-68297", Continue); |
| #endif |
| QTRY_COMPARE(QApplication::focusWidget(), firstchild); |
| } |
| } |
| |
| |
| void tst_QWidget::reparent() |
| { |
| QWidget parent; |
| parent.setWindowTitle(QStringLiteral("Toplevel ") + __FUNCTION__); |
| const QPoint parentPosition = m_availableTopLeft + QPoint(300, 300); |
| parent.setGeometry(QRect(parentPosition, m_testWidgetSize)); |
| |
| QWidget child; |
| child.setObjectName("child"); |
| child.setGeometry(10, 10, 180, 130); |
| QPalette pal1; |
| pal1.setColor(child.backgroundRole(), Qt::white); |
| child.setPalette(pal1); |
| |
| QWidget childTLW(&child, Qt::Window); |
| childTLW.setObjectName(QStringLiteral("childTLW ") + __FUNCTION__); |
| childTLW.setWindowTitle(childTLW.objectName()); |
| childTLW.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize)); |
| QPalette pal2; |
| pal2.setColor(childTLW.backgroundRole(), Qt::yellow); |
| childTLW.setPalette(pal2); |
| |
| parent.show(); |
| childTLW.show(); |
| #ifdef Q_OS_WINRT |
| QEXPECT_FAIL("", "WinRT does not support more than 1 top level widget", Abort); |
| #endif |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| |
| parent.move(parentPosition); |
| |
| QPoint childPos = parent.mapToGlobal(child.pos()); |
| QPoint tlwPos = childTLW.pos(); |
| |
| child.setParent(nullptr, child.windowFlags() & ~Qt::WindowType_Mask); |
| child.setGeometry(childPos.x(), childPos.y(), child.width(), child.height()); |
| child.show(); |
| |
| #if 0 // QTBUG-26424 |
| if (m_platform == QStringLiteral("xcb")) |
| QEXPECT_FAIL("", "On X11, the window manager will apply NorthWestGravity rules to 'child', which" |
| " means the top-left corner of the window frame will be placed at 'childPos'" |
| " causing this test to fail.", Continue); |
| #endif |
| |
| QCOMPARE(child.geometry().topLeft(), childPos); |
| QTRY_COMPARE(childTLW.pos(), tlwPos); |
| } |
| |
| // Qt/Embedded does it differently. |
| void tst_QWidget::icon() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| QPixmap p(20,20); |
| p.fill(Qt::red); |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| testWidget->setWindowIcon(p); |
| |
| QVERIFY(!testWidget->windowIcon().isNull()); |
| testWidget->show(); |
| QVERIFY(!testWidget->windowIcon().isNull()); |
| testWidget->showFullScreen(); |
| QVERIFY(!testWidget->windowIcon().isNull()); |
| testWidget->showNormal(); |
| QVERIFY(!testWidget->windowIcon().isNull()); |
| } |
| |
| void tst_QWidget::hideWhenFocusWidgetIsChild() |
| { |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) |
| QSKIP("Window activation is not supported."); |
| |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->setWindowTitle(__FUNCTION__); |
| testWidget->resize(m_testWidgetSize); |
| centerOnScreen(testWidget.data()); |
| QWidget *parentWidget(new QWidget(testWidget.data())); |
| parentWidget->setObjectName("parentWidget"); |
| parentWidget->setGeometry(0, 0, 100, 100); |
| QLineEdit *edit = new QLineEdit(parentWidget); |
| edit->setObjectName("edit1"); |
| QLineEdit *edit3 = new QLineEdit(parentWidget); |
| edit3->setObjectName("edit3"); |
| edit3->move(0,50); |
| QLineEdit *edit2 = new QLineEdit(testWidget.data()); |
| edit2->setObjectName("edit2"); |
| edit2->move(110, 100); |
| edit->setFocus(); |
| testWidget->show(); |
| testWidget->activateWindow(); |
| QVERIFY(QTest::qWaitForWindowActive(testWidget.data())); |
| |
| QString actualFocusWidget, expectedFocusWidget; |
| if (!QApplication::focusWidget() && m_platform == QStringLiteral("xcb")) |
| QSKIP("X11: Your window manager is too broken for this test"); |
| |
| QVERIFY(QApplication::focusWidget()); |
| actualFocusWidget = QString::asprintf("%p %s %s", QApplication::focusWidget(), QApplication::focusWidget()->objectName().toLatin1().constData(), QApplication::focusWidget()->metaObject()->className()); |
| expectedFocusWidget = QString::asprintf("%p %s %s", edit, edit->objectName().toLatin1().constData(), edit->metaObject()->className()); |
| QCOMPARE(actualFocusWidget, expectedFocusWidget); |
| |
| parentWidget->hide(); |
| QCoreApplication::processEvents(); |
| actualFocusWidget = QString::asprintf("%p %s %s", QApplication::focusWidget(), QApplication::focusWidget()->objectName().toLatin1().constData(), QApplication::focusWidget()->metaObject()->className()); |
| expectedFocusWidget = QString::asprintf("%p %s %s", edit2, edit2->objectName().toLatin1().constData(), edit2->metaObject()->className()); |
| QCOMPARE(actualFocusWidget, expectedFocusWidget); |
| } |
| |
| void tst_QWidget::normalGeometry() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| else if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT: This fails. Figure out why - QTBUG-68297."); |
| QWidget parent; |
| parent.setWindowTitle("NormalGeometry parent"); |
| QWidget *child = new QWidget(&parent); |
| |
| QCOMPARE(parent.normalGeometry(), parent.geometry()); |
| QCOMPARE(child->normalGeometry(), QRect()); |
| |
| const QRect testGeom = QRect(m_availableTopLeft + QPoint(100 ,100), m_testWidgetSize); |
| parent.setGeometry(testGeom); |
| parent.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| QApplication::processEvents(); |
| |
| QRect geom = parent.geometry(); |
| // ### the window manager places the top-left corner at |
| // ### 100,100... making geom something like 102,124 (offset by |
| // ### the frame/frame)... this indicates a rather large different |
| // ### between how X11 and Windows works |
| // QCOMPARE(geom, QRect(100, 100, 200, 200)); |
| QCOMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); |
| QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); |
| QTRY_VERIFY(parent.geometry() != geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); |
| QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); |
| QTRY_COMPARE(parent.geometry(), geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.showMaximized(); |
| QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); |
| QTRY_VERIFY(parent.geometry() != geom); |
| QCOMPARE(parent.normalGeometry(), geom); |
| |
| parent.showNormal(); |
| QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); |
| QTRY_COMPARE(parent.geometry(), geom); |
| QCOMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(10); |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(10); |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| QTRY_VERIFY(parent.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized)); |
| // ### when minimized and maximized at the same time, the geometry |
| // ### does *NOT* have to be the normal geometry, it could be the |
| // ### maximized geometry. |
| // QCOMPARE(parent.geometry(), geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized); |
| QTest::qWait(10); |
| QTRY_VERIFY(!(parent.windowState() & Qt::WindowMinimized)); |
| QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); |
| QTRY_VERIFY(parent.geometry() != geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(10); |
| QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); |
| QTRY_COMPARE(parent.geometry(), geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen); |
| QTest::qWait(10); |
| QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen); |
| QTRY_VERIFY(parent.geometry() != geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen); |
| QTest::qWait(10); |
| QVERIFY(!(parent.windowState() & Qt::WindowFullScreen)); |
| QTRY_COMPARE(parent.geometry(), geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.showFullScreen(); |
| QTest::qWait(10); |
| QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen); |
| QTRY_VERIFY(parent.geometry() != geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.showNormal(); |
| QTest::qWait(10); |
| QTRY_VERIFY(!(parent.windowState() & Qt::WindowFullScreen)); |
| QTRY_COMPARE(parent.geometry(), geom); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| |
| parent.showNormal(); |
| parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized); |
| parent.setWindowState(Qt::WindowMinimized | Qt:: WindowFullScreen | Qt::WindowMaximized); |
| parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized); |
| QTest::qWait(10); |
| QTRY_COMPARE(parent.normalGeometry(), geom); |
| } |
| |
| void tst_QWidget::setGeometry() |
| { |
| QWidget tlw; |
| tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget child(&tlw); |
| |
| const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); |
| const QSize initialSize = 2 * m_testWidgetSize; |
| QRect tr(topLeft + QPoint(100,100), initialSize); |
| QRect cr(50,50,50,50); |
| tlw.setGeometry(tr); |
| child.setGeometry(cr); |
| tlw.showNormal(); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support setGeometry", Abort); |
| QTRY_COMPARE(tlw.geometry().size(), tr.size()); |
| QCOMPARE(child.geometry(), cr); |
| |
| tlw.setParent(nullptr, Qt::Window|Qt::FramelessWindowHint); |
| tr = QRect(topLeft, initialSize / 2); |
| tlw.setGeometry(tr); |
| QCOMPARE(tlw.geometry(), tr); |
| tlw.showNormal(); |
| QTest::qWait(50); |
| if (tlw.frameGeometry() != tlw.geometry()) |
| QSKIP("Your window manager is too broken for this test"); |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| QCOMPARE(tlw.geometry(), tr); |
| } |
| |
| void tst_QWidget::setGeometryHidden() |
| { |
| if (QGuiApplication::styleHints()->showIsMaximized()) |
| QSKIP("Platform does not support QWidget::setGeometry() - skipping"); |
| |
| QWidget tlw; |
| tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget child(&tlw); |
| |
| const QRect tr(m_availableTopLeft + QPoint(100, 100), 2 * m_testWidgetSize); |
| const QRect cr(QPoint(50, 50), m_testWidgetSize); |
| tlw.setGeometry(tr); |
| child.setGeometry(cr); |
| tlw.showNormal(); |
| |
| tlw.hide(); |
| QTRY_VERIFY(tlw.isHidden()); |
| tlw.setGeometry(cr); |
| QVERIFY(tlw.testAttribute(Qt::WA_PendingMoveEvent)); |
| QVERIFY(tlw.testAttribute(Qt::WA_PendingResizeEvent)); |
| QImage img(tlw.size(), QImage::Format_ARGB32); // just needed to call QWidget::render() |
| tlw.render(&img); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent)); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent)); |
| tlw.setGeometry(cr); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent)); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent)); |
| tlw.resize(cr.size()); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingMoveEvent)); |
| QVERIFY(!tlw.testAttribute(Qt::WA_PendingResizeEvent)); |
| } |
| |
| void tst_QWidget::windowOpacity() |
| { |
| QWidget widget; |
| QWidget child(&widget); |
| |
| // Initial value should be 1.0 |
| QCOMPARE(widget.windowOpacity(), 1.0); |
| // children should always return 1.0 |
| QCOMPARE(child.windowOpacity(), 1.0); |
| |
| widget.setWindowOpacity(0.0); |
| QCOMPARE(widget.windowOpacity(), 0.0); |
| child.setWindowOpacity(0.0); |
| QCOMPARE(child.windowOpacity(), 1.0); |
| |
| widget.setWindowOpacity(1.0); |
| QCOMPARE(widget.windowOpacity(), 1.0); |
| child.setWindowOpacity(1.0); |
| QCOMPARE(child.windowOpacity(), 1.0); |
| |
| widget.setWindowOpacity(2.0); |
| QCOMPARE(widget.windowOpacity(), 1.0); |
| child.setWindowOpacity(2.0); |
| QCOMPARE(child.windowOpacity(), 1.0); |
| |
| widget.setWindowOpacity(-1.0); |
| QCOMPARE(widget.windowOpacity(), 0.0); |
| child.setWindowOpacity(-1.0); |
| QCOMPARE(child.windowOpacity(), 1.0); |
| } |
| |
| class UpdateWidget : public QWidget |
| { |
| public: |
| explicit UpdateWidget(QWidget *parent = nullptr) : QWidget(parent) |
| { |
| setObjectName(QLatin1String("UpdateWidget")); |
| reset(); |
| } |
| |
| void paintEvent(QPaintEvent *e) override |
| { |
| paintedRegion += e->region(); |
| ++numPaintEvents; |
| if (resizeInPaintEvent) { |
| resizeInPaintEvent = false; |
| resize(size() + QSize(2, 2)); |
| } |
| } |
| |
| bool event(QEvent *event) override |
| { |
| switch (event->type()) { |
| case QEvent::ZOrderChange: |
| ++numZOrderChangeEvents; |
| break; |
| case QEvent::UpdateRequest: |
| ++numUpdateRequestEvents; |
| break; |
| case QEvent::ActivationChange: |
| case QEvent::FocusIn: |
| case QEvent::FocusOut: |
| case QEvent::WindowActivate: |
| case QEvent::WindowDeactivate: |
| if (!updateOnActivationChangeAndFocusIn) |
| return true; // Filter out to avoid update() calls in QWidget. |
| break; |
| default: |
| break; |
| } |
| return QWidget::event(event); |
| } |
| |
| void reset() |
| { |
| numPaintEvents = numZOrderChangeEvents = numUpdateRequestEvents = 0; |
| updateOnActivationChangeAndFocusIn = resizeInPaintEvent = false; |
| paintedRegion = QRegion(); |
| } |
| |
| int numPaintEvents; |
| int numZOrderChangeEvents; |
| int numUpdateRequestEvents; |
| bool updateOnActivationChangeAndFocusIn; |
| bool resizeInPaintEvent; |
| QRegion paintedRegion; |
| }; |
| |
| void tst_QWidget::lostUpdatesOnHide() |
| { |
| #ifndef Q_OS_OSX |
| UpdateWidget widget; |
| widget.setAttribute(Qt::WA_DontShowOnScreen); |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.show(); |
| widget.hide(); |
| QTest::qWait(50); |
| widget.show(); |
| QTest::qWait(50); |
| |
| QCOMPARE(widget.numPaintEvents, 1); |
| #endif |
| } |
| |
| void tst_QWidget::raise() |
| { |
| QScopedPointer<QWidget> parentPtr(new QWidget); |
| parentPtr->resize(200, 200); |
| parentPtr->setObjectName(QLatin1String("raise")); |
| parentPtr->setWindowTitle(parentPtr->objectName()); |
| QList<UpdateWidget *> allChildren; |
| |
| UpdateWidget *child1 = new UpdateWidget(parentPtr.data()); |
| child1->setAutoFillBackground(true); |
| allChildren.append(child1); |
| |
| UpdateWidget *child2 = new UpdateWidget(parentPtr.data()); |
| child2->setAutoFillBackground(true); |
| allChildren.append(child2); |
| |
| UpdateWidget *child3 = new UpdateWidget(parentPtr.data()); |
| child3->setAutoFillBackground(true); |
| allChildren.append(child3); |
| |
| UpdateWidget *child4 = new UpdateWidget(parentPtr.data()); |
| child4->setAutoFillBackground(true); |
| allChildren.append(child4); |
| |
| parentPtr->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(parentPtr.data())); |
| |
| #ifdef Q_OS_OSX |
| if (child1->internalWinId()) { |
| QSKIP("Cocoa has no Z-Order for views, we hack it, but it results in paint events."); |
| } |
| #endif |
| |
| QObjectList list1{child1, child2, child3, child4}; |
| QCOMPARE(parentPtr->children(), list1); |
| QCOMPARE(allChildren.count(), list1.count()); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child4 ? 1 : 0; |
| if (expectedPaintEvents == 0) { |
| QCOMPARE(child->numPaintEvents, 0); |
| } else { |
| // show() issues multiple paint events on some window managers |
| QTRY_VERIFY(child->numPaintEvents >= expectedPaintEvents); |
| } |
| QCOMPARE(child->numZOrderChangeEvents, 0); |
| child->reset(); |
| } |
| |
| for (int i = 0; i < 5; ++i) |
| child2->raise(); |
| QTest::qWait(50); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child2 ? 1 : 0; |
| int expectedZOrderChangeEvents = child == child2 ? 1 : 0; |
| QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); |
| QCOMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); |
| child->reset(); |
| } |
| |
| QList<QObject *> list2; |
| list2 << child1 << child3 << child4 << child2; |
| QCOMPARE(parentPtr->children(), list2); |
| |
| // Creates a widget on top of all the children and checks that raising one of |
| // the children underneath doesn't trigger a repaint on the covering widget. |
| QWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget *parent = parentPtr.take(); |
| parent->setParent(&topLevel); |
| topLevel.show(); |
| |
| UpdateWidget *onTop = new UpdateWidget(&topLevel); |
| onTop->reset(); |
| onTop->resize(topLevel.size()); |
| onTop->setAutoFillBackground(true); |
| onTop->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| QTRY_VERIFY(onTop->numPaintEvents > 0); |
| onTop->reset(); |
| |
| // Reset all the children. |
| for (UpdateWidget *child : qAsConst(allChildren)) |
| child->reset(); |
| |
| for (int i = 0; i < 5; ++i) |
| child3->raise(); |
| QTest::qWait(50); |
| |
| QCOMPARE(onTop->numPaintEvents, 0); |
| QCOMPARE(onTop->numZOrderChangeEvents, 0); |
| |
| QObjectList list3{child1, child4, child2, child3}; |
| QCOMPARE(parent->children(), list3); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = 0; |
| int expectedZOrderChangeEvents = child == child3 ? 1 : 0; |
| QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); |
| QCOMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); |
| child->reset(); |
| } |
| } |
| |
| void tst_QWidget::lower() |
| { |
| QScopedPointer<QWidget> parent(new QWidget); |
| parent->setObjectName(QLatin1String("lower")); |
| parent->setWindowTitle(parent->objectName()); |
| parent->resize(200, 200); |
| QList<UpdateWidget *> allChildren; |
| |
| UpdateWidget *child1 = new UpdateWidget(parent.data()); |
| child1->setAutoFillBackground(true); |
| allChildren.append(child1); |
| |
| UpdateWidget *child2 = new UpdateWidget(parent.data()); |
| child2->setAutoFillBackground(true); |
| allChildren.append(child2); |
| |
| UpdateWidget *child3 = new UpdateWidget(parent.data()); |
| child3->setAutoFillBackground(true); |
| allChildren.append(child3); |
| |
| UpdateWidget *child4 = new UpdateWidget(parent.data()); |
| child4->setAutoFillBackground(true); |
| allChildren.append(child4); |
| |
| parent->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(parent.data())); |
| |
| QObjectList list1{child1, child2, child3, child4}; |
| QCOMPARE(parent->children(), list1); |
| QCOMPARE(allChildren.count(), list1.count()); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child4 ? 1 : 0; |
| if (expectedPaintEvents == 0) { |
| QCOMPARE(child->numPaintEvents, 0); |
| } else { |
| // show() issues multiple paint events on some window managers |
| QTRY_VERIFY(child->numPaintEvents >= expectedPaintEvents); |
| } |
| QCOMPARE(child->numZOrderChangeEvents, 0); |
| child->reset(); |
| } |
| |
| for (int i = 0; i < 5; ++i) |
| child4->lower(); |
| |
| QTest::qWait(100); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child3 ? 1 : 0; |
| int expectedZOrderChangeEvents = child == child4 ? 1 : 0; |
| QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); |
| QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); |
| child->reset(); |
| } |
| |
| QList<QObject *> list2; |
| list2 << child4 << child1 << child2 << child3; |
| QCOMPARE(parent->children(), list2); |
| } |
| |
| void tst_QWidget::stackUnder() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974: Cocoa has no Z-Order for views, we hack it, but it results in paint events."); |
| #endif |
| |
| QScopedPointer<QWidget> parent(new QWidget); |
| parent->setObjectName(QLatin1String("stackUnder")); |
| parent->setWindowTitle(parent->objectName()); |
| parent->resize(200, 200); |
| QList<UpdateWidget *> allChildren; |
| |
| UpdateWidget *child1 = new UpdateWidget(parent.data()); |
| child1->setAutoFillBackground(true); |
| allChildren.append(child1); |
| |
| UpdateWidget *child2 = new UpdateWidget(parent.data()); |
| child2->setAutoFillBackground(true); |
| allChildren.append(child2); |
| |
| UpdateWidget *child3 = new UpdateWidget(parent.data()); |
| child3->setAutoFillBackground(true); |
| allChildren.append(child3); |
| |
| UpdateWidget *child4 = new UpdateWidget(parent.data()); |
| child4->setAutoFillBackground(true); |
| allChildren.append(child4); |
| |
| parent->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(parent.data())); |
| QObjectList list1{child1, child2, child3, child4}; |
| QCOMPARE(parent->children(), list1); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child4 ? 1 : 0; |
| #if defined(Q_OS_WIN) || defined(Q_OS_OSX) |
| if (expectedPaintEvents == 1 && child->numPaintEvents == 2) |
| QEXPECT_FAIL(0, "Mac and Windows issues double repaints for Z-Order change", Continue); |
| #endif |
| QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); |
| QCOMPARE(child->numZOrderChangeEvents, 0); |
| child->reset(); |
| } |
| |
| for (int i = 0; i < 5; ++i) |
| child4->stackUnder(child2); |
| QTest::qWait(10); |
| |
| QObjectList list2{child1, child4, child2, child3}; |
| QCOMPARE(parent->children(), list2); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedPaintEvents = child == child3 ? 1 : 0; |
| int expectedZOrderChangeEvents = child == child4 ? 1 : 0; |
| QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); |
| QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); |
| child->reset(); |
| } |
| |
| for (int i = 0; i < 5; ++i) |
| child1->stackUnder(child3); |
| QTest::qWait(10); |
| |
| QObjectList list3{child4, child2, child1, child3}; |
| QCOMPARE(parent->children(), list3); |
| |
| for (UpdateWidget *child : qAsConst(allChildren)) { |
| int expectedZOrderChangeEvents = child == child1 ? 1 : 0; |
| if (child == child3) { |
| #ifndef Q_OS_OSX |
| QEXPECT_FAIL(0, "See QTBUG-493", Continue); |
| #endif |
| QCOMPARE(child->numPaintEvents, 0); |
| } else { |
| QCOMPARE(child->numPaintEvents, 0); |
| } |
| QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); |
| child->reset(); |
| } |
| } |
| |
| void drawPolygon(QPaintDevice *dev, int w, int h) |
| { |
| QPainter p(dev); |
| p.fillRect(0, 0, w, h, Qt::white); |
| |
| QPolygon a; |
| a << QPoint(0, 0) << QPoint(w/2, h/2) << QPoint(w, 0) |
| << QPoint(w/2, h) << QPoint(0, 0); |
| |
| p.setPen(QPen(Qt::black, 1)); |
| p.setBrush(Qt::DiagCrossPattern); |
| p.drawPolygon(a); |
| } |
| |
| class ContentsPropagationWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| explicit ContentsPropagationWidget(QWidget *parent = nullptr) : QWidget(parent) |
| { |
| setObjectName(QLatin1String("ContentsPropagationWidget")); |
| setWindowTitle(objectName()); |
| QWidget *child = this; |
| for (int i = 0; i < 32; ++i) { |
| child = new QWidget(child); |
| child->setGeometry(i, i, 400 - i * 2, 400 - i * 2); |
| } |
| } |
| |
| void setContentsPropagation(bool enable) |
| { |
| for (QObject *child : children()) |
| qobject_cast<QWidget *>(child)->setAutoFillBackground(!enable); |
| } |
| |
| protected: |
| void paintEvent(QPaintEvent *) override |
| { |
| int w = width(), h = height(); |
| drawPolygon(this, w, h); |
| } |
| |
| QSize sizeHint() const override { return {500, 500}; } |
| }; |
| |
| // Scale to remove devicePixelRatio should scaling be active. |
| static QPixmap grabFromWidget(QWidget *w, const QRect &rect) |
| { |
| QPixmap pixmap = w->grab(rect); |
| const qreal devicePixelRatio = pixmap.devicePixelRatioF(); |
| if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { |
| pixmap = pixmap.scaled((QSizeF(pixmap.size()) / devicePixelRatio).toSize(), |
| Qt::KeepAspectRatio, Qt::SmoothTransformation); |
| pixmap.setDevicePixelRatio(1); |
| } |
| return pixmap; |
| } |
| |
| void tst_QWidget::testContentsPropagation() |
| { |
| if (!qFuzzyCompare(qApp->devicePixelRatio(), qreal(1))) |
| QSKIP("This test does not work with scaling."); |
| ContentsPropagationWidget widget; |
| widget.setFixedSize(500, 500); |
| widget.setContentsPropagation(false); |
| QPixmap widgetSnapshot = widget.grab(QRect(QPoint(0, 0), QSize(-1, -1))); |
| |
| QPixmap correct(500, 500); |
| drawPolygon(&correct, 500, 500); |
| //correct.save("correct.png", "PNG"); |
| |
| //widgetSnapshot.save("snap1.png", "PNG"); |
| QVERIFY(widgetSnapshot.toImage() != correct.toImage()); |
| |
| widget.setContentsPropagation(true); |
| widgetSnapshot = widgetSnapshot = widget.grab(QRect(QPoint(0, 0), QSize(-1, -1))); |
| //widgetSnapshot.save("snap2.png", "PNG"); |
| |
| QCOMPARE(widgetSnapshot, correct); |
| } |
| |
| /* |
| Test that saving and restoring window geometry with |
| saveGeometry() and restoreGeometry() works. |
| */ |
| |
| void tst_QWidget::saveRestoreGeometry() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| const QPoint position = m_availableTopLeft + QPoint(100, 100); |
| const QSize size = m_testWidgetSize; |
| |
| QByteArray savedGeometry; |
| |
| { |
| QWidget widget; |
| widget.move(position); |
| widget.resize(size); |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QApplication::processEvents(); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support move/resize", Abort); |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget.pos(), position))); |
| QCOMPARE(widget.size(), size); |
| savedGeometry = widget.saveGeometry(); |
| } |
| |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| |
| const QByteArray empty; |
| const QByteArray one("a"); |
| const QByteArray two("ab"); |
| const QByteArray three("abc"); |
| const QByteArray four("abca"); |
| const QByteArray garbage("abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"); |
| |
| QVERIFY(!widget.restoreGeometry(empty)); |
| QVERIFY(!widget.restoreGeometry(one)); |
| QVERIFY(!widget.restoreGeometry(two)); |
| QVERIFY(!widget.restoreGeometry(three)); |
| QVERIFY(!widget.restoreGeometry(four)); |
| QVERIFY(!widget.restoreGeometry(garbage)); |
| |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QApplication::processEvents(); |
| |
| QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget.pos(), position))); |
| QCOMPARE(widget.size(), size); |
| widget.show(); |
| QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget.pos(), position))); |
| QCOMPARE(widget.size(), size); |
| } |
| |
| { |
| QWidget widget; |
| widget.move(position); |
| widget.resize(size); |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTRY_COMPARE(widget.geometry().size(), size); |
| |
| QRect geom; |
| |
| //Restore from Full screen |
| savedGeometry = widget.saveGeometry(); |
| geom = widget.geometry(); |
| widget.setWindowState(widget.windowState() | Qt::WindowFullScreen); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen)); |
| QTest::qWait(500); |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| QTest::qWait(120); |
| QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen)); |
| QTRY_COMPARE(widget.geometry(), geom); |
| |
| //Restore to full screen |
| widget.setWindowState(widget.windowState() | Qt::WindowFullScreen); |
| QTest::qWait(120); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen)); |
| QTest::qWait(500); |
| savedGeometry = widget.saveGeometry(); |
| geom = widget.geometry(); |
| widget.setWindowState(widget.windowState() ^ Qt::WindowFullScreen); |
| QTest::qWait(120); |
| QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen)); |
| QTest::qWait(400); |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| QTest::qWait(120); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowFullScreen)); |
| QTRY_COMPARE(widget.geometry(), geom); |
| QVERIFY((widget.windowState() & Qt::WindowFullScreen)); |
| widget.setWindowState(widget.windowState() ^ Qt::WindowFullScreen); |
| QTest::qWait(120); |
| QTRY_VERIFY(!(widget.windowState() & Qt::WindowFullScreen)); |
| QTest::qWait(120); |
| |
| //Restore from Maximised |
| widget.move(position); |
| widget.resize(size); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.size(), size); |
| QTest::qWait(500); |
| savedGeometry = widget.saveGeometry(); |
| geom = widget.geometry(); |
| widget.setWindowState(widget.windowState() | Qt::WindowMaximized); |
| QTest::qWait(120); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized)); |
| QTRY_VERIFY(widget.geometry() != geom); |
| QTest::qWait(500); |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| QTest::qWait(120); |
| QTRY_COMPARE(widget.geometry(), geom); |
| |
| QVERIFY(!(widget.windowState() & Qt::WindowMaximized)); |
| |
| //Restore to maximised |
| widget.setWindowState(widget.windowState() | Qt::WindowMaximized); |
| QTest::qWait(120); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized)); |
| QTest::qWait(500); |
| geom = widget.geometry(); |
| savedGeometry = widget.saveGeometry(); |
| widget.setWindowState(widget.windowState() ^ Qt::WindowMaximized); |
| QTest::qWait(120); |
| QTRY_VERIFY(!(widget.windowState() & Qt::WindowMaximized)); |
| QTest::qWait(500); |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| QTest::qWait(120); |
| QTRY_VERIFY((widget.windowState() & Qt::WindowMaximized)); |
| QTRY_COMPARE(widget.geometry(), geom); |
| } |
| } |
| |
| void tst_QWidget::restoreVersion1Geometry_data() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| QTest::addColumn<QString>("fileName"); |
| QTest::addColumn<Qt::WindowState>("expectedWindowState"); |
| QTest::addColumn<QPoint>("expectedPosition"); |
| QTest::addColumn<QSize>("expectedSize"); |
| QTest::addColumn<QRect>("expectedNormalGeometry"); |
| const QPoint position(100, 100); |
| const QSize size(200, 200); |
| const QRect normalGeometry(102, 124, 200, 200); |
| |
| QTest::newRow("geometry.dat") << ":geometry.dat" << Qt::WindowNoState << position << size << normalGeometry; |
| QTest::newRow("geometry-maximized.dat") << ":geometry-maximized.dat" << Qt::WindowMaximized << position << size << normalGeometry; |
| QTest::newRow("geometry-fullscreen.dat") << ":geometry-fullscreen.dat" << Qt::WindowFullScreen << position << size << normalGeometry; |
| } |
| |
| /* |
| Test that the current version of restoreGeometry() can restore geometry |
| saved width saveGeometry() version 1.0. |
| */ |
| void tst_QWidget::restoreVersion1Geometry() |
| { |
| QFETCH(QString, fileName); |
| QFETCH(Qt::WindowState, expectedWindowState); |
| QFETCH(QPoint, expectedPosition); |
| Q_UNUSED(expectedPosition); |
| QFETCH(QSize, expectedSize); |
| QFETCH(QRect, expectedNormalGeometry); |
| |
| if (m_platform == QLatin1String("windows") && QGuiApplication::primaryScreen()->geometry().width() > 2000) |
| QSKIP("Skipping due to minimum decorated window size on Windows"); |
| |
| // WindowActive is uninteresting for this test |
| const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized; |
| |
| QFile f(fileName); |
| QVERIFY(f.exists()); |
| f.open(QIODevice::ReadOnly); |
| const QByteArray savedGeometry = f.readAll(); |
| QCOMPARE(savedGeometry.count(), 46); |
| f.close(); |
| |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::") |
| + QLatin1String(QTest::currentDataTag())); |
| |
| QVERIFY(widget.restoreGeometry(savedGeometry)); |
| |
| QCOMPARE(widget.windowState() & WindowStateMask, expectedWindowState); |
| if (expectedWindowState == Qt::WindowNoState) { |
| QTRY_COMPARE(widget.geometry(), expectedNormalGeometry); |
| QCOMPARE(widget.size(), expectedSize); |
| } |
| |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(100); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support restoreGeometry", Abort); |
| |
| if (expectedWindowState == Qt::WindowNoState) { |
| QTRY_COMPARE(widget.size(), expectedSize); |
| QCOMPARE(widget.geometry(), expectedNormalGeometry); |
| } |
| |
| widget.showNormal(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(widget.geometry(), expectedNormalGeometry); |
| if (expectedWindowState == Qt::WindowNoState) |
| QCOMPARE(widget.size(), expectedSize); |
| |
| #if 0 |
| // Code for saving a new geometry*.dat files |
| { |
| QWidget widgetToSave; |
| widgetToSave.move(expectedPosition); |
| widgetToSave.resize(expectedSize); |
| widgetToSave.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(500); // stabilize |
| widgetToSave.setWindowState(Qt::WindowStates(expectedWindowState)); |
| QTest::qWait(500); // stabilize |
| |
| QByteArray geometryToSave = widgetToSave.saveGeometry(); |
| |
| // Code for saving a new geometry.dat file. |
| f.setFileName(fileName.mid(1)); |
| QVERIFY(f.open(QIODevice::WriteOnly)); // did you forget to 'p4 edit *.dat'? :) |
| f.write(geometryToSave); |
| f.close(); |
| } |
| #endif |
| } |
| |
| void tst_QWidget::widgetAt() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| if (m_platform == QStringLiteral("offscreen")) |
| QSKIP("Platform offscreen does not support lower()/raise() or WindowMasks"); |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support more than 1 top level widget"); |
| |
| Q_CHECK_PAINTEVENTS |
| |
| const QPoint referencePos = m_availableTopLeft + QPoint(100, 100); |
| QScopedPointer<QWidget> w1(new QWidget(nullptr, Qt::X11BypassWindowManagerHint)); |
| w1->setGeometry(QRect(referencePos, QSize(m_testWidgetSize.width(), 150))); |
| w1->setObjectName(QLatin1String("w1")); |
| w1->setWindowTitle(w1->objectName()); |
| QScopedPointer<QWidget> w2(new QWidget(nullptr, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint)); |
| w2->setGeometry(QRect(referencePos + QPoint(50, 50), QSize(m_testWidgetSize.width(), 100))); |
| w2->setObjectName(QLatin1String("w2")); |
| w2->setWindowTitle(w2->objectName()); |
| w1->showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(w1.data())); |
| const QPoint testPos = referencePos + QPoint(100, 100); |
| QWidget *wr; |
| QTRY_VERIFY((wr = QApplication::widgetAt((testPos)))); |
| QCOMPARE(wr->objectName(), QString("w1")); |
| |
| w2->showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(w2.data())); |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos))); |
| QCOMPARE(wr->objectName(), QString("w2")); |
| |
| w2->lower(); |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w1")); |
| w2->raise(); |
| |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w2")); |
| |
| QWidget *w3 = new QWidget(w2.data()); |
| w3->setGeometry(10,10,50,50); |
| w3->setObjectName("w3"); |
| w3->showNormal(); |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w3")); |
| |
| w3->setAttribute(Qt::WA_TransparentForMouseEvents); |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos)) && wr->objectName() == QString("w2")); |
| |
| if (!QGuiApplicationPrivate::platformIntegration() |
| ->hasCapability(QPlatformIntegration::WindowMasks)) { |
| QSKIP("Platform does not support WindowMasks"); |
| } |
| |
| QRegion rgn = QRect(QPoint(0,0), w2->size()); |
| QPoint point = w2->mapFromGlobal(testPos); |
| rgn -= QRect(point, QSize(1,1)); |
| w2->setMask(rgn); |
| |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos))); |
| QTRY_COMPARE(wr->objectName(), w1->objectName()); |
| QTRY_VERIFY((wr = QApplication::widgetAt(testPos + QPoint(1, 1)))); |
| QTRY_COMPARE(wr->objectName(), w2->objectName()); |
| |
| QBitmap bitmap(w2->size()); |
| QPainter p(&bitmap); |
| p.fillRect(bitmap.rect(), Qt::color1); |
| p.setPen(Qt::color0); |
| p.drawPoint(w2->mapFromGlobal(testPos)); |
| p.end(); |
| w2->setMask(bitmap); |
| QTRY_COMPARE(QApplication::widgetAt(testPos), w1.data()); |
| QTRY_VERIFY(QApplication::widgetAt(testPos + QPoint(1, 1)) == w2.data()); |
| } |
| |
| void tst_QWidget::task110173() |
| { |
| QWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| |
| QPushButton *pb1 = new QPushButton("click", &w); |
| pb1->setFocusPolicy(Qt::ClickFocus); |
| pb1->move(100, 100); |
| |
| QPushButton *pb2 = new QPushButton("push", &w); |
| pb2->setFocusPolicy(Qt::ClickFocus); |
| pb2->move(300, 300); |
| |
| QTest::keyClick( &w, Qt::Key_Tab ); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| } |
| |
| class Widget : public QWidget |
| { |
| public: |
| Widget() { setFocusPolicy(Qt::StrongFocus); } |
| void actionEvent(QActionEvent *) override { if (deleteThis) delete this; } |
| void changeEvent(QEvent *) override { if (deleteThis) delete this; } |
| void closeEvent(QCloseEvent *) override { if (deleteThis) delete this; } |
| void hideEvent(QHideEvent *) override { if (deleteThis) delete this; } |
| void focusOutEvent(QFocusEvent *) override { if (deleteThis) delete this; } |
| void keyPressEvent(QKeyEvent *) override { if (deleteThis) delete this; } |
| void keyReleaseEvent(QKeyEvent *) override { if (deleteThis) delete this; } |
| void mouseDoubleClickEvent(QMouseEvent *) override { if (deleteThis) delete this; } |
| void mousePressEvent(QMouseEvent *) override { if (deleteThis) delete this; } |
| void mouseReleaseEvent(QMouseEvent *) override { if (deleteThis) delete this; } |
| void mouseMoveEvent(QMouseEvent *) override { if (deleteThis) delete this; } |
| |
| bool deleteThis = false; |
| }; |
| |
| void tst_QWidget::testDeletionInEventHandlers() |
| { |
| // closeEvent |
| QPointer<Widget> w = new Widget; |
| w->deleteThis = true; |
| w->close(); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // focusOut (crashes) |
| //w = new Widget; |
| //w->show(); |
| //w->setFocus(); |
| //QCOMPARE(qApp->focusWidget(), w); |
| //w->deleteThis = true; |
| //w->clearFocus(); |
| //QVERIFY(w.isNull()); |
| |
| // key press |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| QTest::keyPress(w, Qt::Key_A); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // key release |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| QTest::keyRelease(w, Qt::Key_A); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // mouse press |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| QTest::mousePress(w, Qt::LeftButton); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // mouse release |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| QMouseEvent me(QEvent::MouseButtonRelease, QPoint(1, 1), Qt::LeftButton, Qt::LeftButton, Qt::KeyboardModifiers()); |
| qApp->notify(w, &me); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // mouse double click |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| QTest::mouseDClick(w, Qt::LeftButton); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // hide event (crashes) |
| //w = new Widget; |
| //w->show(); |
| //w->deleteThis = true; |
| //w->hide(); |
| //QVERIFY(w.isNull()); |
| |
| // action event |
| w = new Widget; |
| w->deleteThis = true; |
| w->addAction(new QAction(w)); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| // change event |
| w = new Widget; |
| w->show(); |
| w->deleteThis = true; |
| w->setMouseTracking(true); |
| QVERIFY(w.isNull()); |
| delete w; |
| |
| w = new Widget; |
| w->setMouseTracking(true); |
| w->show(); |
| w->deleteThis = true; |
| me = QMouseEvent(QEvent::MouseMove, QPoint(0, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier); |
| QApplication::sendEvent(w, &me); |
| QVERIFY(w.isNull()); |
| delete w; |
| } |
| |
| #ifdef Q_OS_OSX |
| class MaskedPainter : public QWidget |
| { |
| public: |
| QRect mask; |
| |
| MaskedPainter() |
| : mask(20, 20, 50, 50) |
| { |
| setMask(mask); |
| } |
| |
| void paintEvent(QPaintEvent *) |
| { |
| QPainter p(this); |
| p.fillRect(mask, QColor(Qt::red)); |
| } |
| }; |
| |
| /* |
| Verifies that the entire area inside the mask is painted red. |
| */ |
| bool verifyWidgetMask(QWidget *widget, QRect mask) |
| { |
| const QImage image = widget->grab(QRect(QPoint(0, 0), widget->size())).toImage(); |
| |
| const QImage masked = image.copy(mask); |
| QImage red(masked); |
| red.fill(QColor(Qt::red).rgb()); |
| |
| return (masked == red); |
| } |
| |
| void tst_QWidget::setMask() |
| { |
| { |
| MaskedPainter w; |
| w.resize(200, 200); |
| w.show(); |
| QTest::qWait(100); |
| QVERIFY(verifyWidgetMask(&w, w.mask)); |
| } |
| { |
| MaskedPainter w; |
| w.resize(200, 200); |
| w.setWindowFlags(w.windowFlags() | Qt::FramelessWindowHint); |
| w.show(); |
| QTest::qWait(100); |
| QRect mask = w.mask; |
| |
| QVERIFY(verifyWidgetMask(&w, mask)); |
| } |
| } |
| #endif |
| |
| class StaticWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| bool partial = false; |
| bool gotPaintEvent = false; |
| QRegion paintedRegion; |
| |
| explicit StaticWidget(QWidget *parent = nullptr) : QWidget(parent) |
| { |
| setAttribute(Qt::WA_StaticContents); |
| setAttribute(Qt::WA_OpaquePaintEvent); |
| setPalette(Qt::red); // Make sure we have an opaque palette. |
| setAutoFillBackground(true); |
| } |
| |
| void paintEvent(QPaintEvent *e) override |
| { |
| paintedRegion += e->region(); |
| gotPaintEvent = true; |
| // qDebug() << "paint" << e->region(); |
| // Look for a full update, set partial to false if found. |
| for (QRect r : e->region()) { |
| partial = (r != rect()); |
| if (!partial) |
| break; |
| } |
| } |
| }; |
| |
| /* |
| Test that widget resizes and moves can be done with minimal repaints when WA_StaticContents |
| and WA_OpaquePaintEvent is set. Test is mac-only for now. |
| */ |
| void tst_QWidget::optimizedResizeMove() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.resize(400, 400); |
| |
| StaticWidget staticWidget(&parent); |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(150, 150); |
| staticWidget.resize(150, 150); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| QTRY_VERIFY(staticWidget.gotPaintEvent); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(10, 10)); |
| QTest::qWait(20); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support move/resize", Abort); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(-10, 10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.resize(staticWidget.size() + QSize(10, 10)); |
| QTRY_VERIFY(staticWidget.gotPaintEvent); |
| QCOMPARE(staticWidget.partial, true); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.resize(staticWidget.size() + QSize(-10, -10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.resize(staticWidget.size() + QSize(10, -10)); |
| QTRY_VERIFY(staticWidget.gotPaintEvent); |
| QCOMPARE(staticWidget.partial, true); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(10, 10)); |
| staticWidget.resize(staticWidget.size() + QSize(-10, -10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(10, 10)); |
| staticWidget.resize(staticWidget.size() + QSize(10, 10)); |
| QTRY_VERIFY(staticWidget.gotPaintEvent); |
| QCOMPARE(staticWidget.partial, true); |
| |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); |
| staticWidget.resize(staticWidget.size() + QSize(-10, -10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| |
| staticWidget.setAttribute(Qt::WA_StaticContents, false); |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); |
| staticWidget.resize(staticWidget.size() + QSize(-10, -10)); |
| QTRY_VERIFY(staticWidget.gotPaintEvent); |
| QCOMPARE(staticWidget.partial, false); |
| staticWidget.setAttribute(Qt::WA_StaticContents, true); |
| |
| staticWidget.setAttribute(Qt::WA_StaticContents, false); |
| staticWidget.gotPaintEvent = false; |
| staticWidget.move(staticWidget.pos() + QPoint(10, 10)); |
| QTest::qWait(20); |
| QCOMPARE(staticWidget.gotPaintEvent, false); |
| staticWidget.setAttribute(Qt::WA_StaticContents, true); |
| } |
| |
| void tst_QWidget::optimizedResize_topLevel() |
| { |
| if (QHighDpiScaling::isActive()) |
| QSKIP("Skip due to rounding errors in the regions."); |
| StaticWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| topLevel.gotPaintEvent = false; |
| topLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| QTRY_VERIFY(topLevel.gotPaintEvent); |
| |
| topLevel.gotPaintEvent = false; |
| topLevel.partial = false; |
| topLevel.paintedRegion = QRegion(); |
| |
| #if !defined(Q_OS_WIN32) |
| topLevel.resize(topLevel.size() + QSize(10, 10)); |
| #else |
| // Static contents does not work when programmatically resizing |
| // top-levels with QWidget::resize. We do some funky stuff in |
| // setGeometry_sys. However, resizing it with the mouse or with |
| // a native function call works (it basically has to go through |
| // WM_RESIZE in QApplication). This is a corner case, though. |
| // See task 243708 |
| RECT rect; |
| GetWindowRect(winHandleOf(&topLevel), &rect); |
| MoveWindow(winHandleOf(&topLevel), rect.left, rect.top, |
| rect.right - rect.left + 10, rect.bottom - rect.top + 10, |
| true); |
| QTest::qWait(100); |
| #endif |
| |
| // Expected update region: New rect - old rect. |
| QRegion expectedUpdateRegion(topLevel.rect()); |
| expectedUpdateRegion -= QRect(QPoint(), topLevel.size() - QSize(10, 10)); |
| |
| QTRY_VERIFY(topLevel.gotPaintEvent); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("offscreen")) |
| QSKIP("QTBUG-26424"); |
| else if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support move/resize", Abort); |
| QCOMPARE(topLevel.partial, true); |
| QCOMPARE(topLevel.paintedRegion, expectedUpdateRegion); |
| } |
| |
| class SiblingDeleter : public QWidget |
| { |
| public: |
| inline SiblingDeleter(QWidget *sibling, QWidget *parent) |
| : QWidget(parent), sibling(sibling) {} |
| inline ~SiblingDeleter() { delete sibling; } |
| |
| private: |
| QPointer<QWidget> sibling; |
| }; |
| |
| |
| void tst_QWidget::childDeletesItsSibling() |
| { |
| auto commonParent = new QWidget(nullptr); |
| QPointer<QWidget> child(new QWidget(nullptr)); |
| QPointer<QWidget> siblingDeleter = new SiblingDeleter(child, commonParent); |
| child->setParent(commonParent); |
| delete commonParent; // don't crash |
| QVERIFY(!child); |
| QVERIFY(!siblingDeleter); |
| |
| } |
| |
| void tst_QWidget::setMinimumSize() |
| { |
| QWidget w; |
| QSize defaultSize = w.size(); |
| |
| w.setMinimumSize(defaultSize + QSize(100, 100)); |
| QCOMPARE(w.size(), defaultSize + QSize(100, 100)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setMinimumSize(defaultSize + QSize(50, 50)); |
| QCOMPARE(w.size(), defaultSize + QSize(100, 100)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setMinimumSize(defaultSize + QSize(200, 200)); |
| QCOMPARE(w.size(), defaultSize + QSize(200, 200)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| QSize nonDefaultSize = defaultSize + QSize(5,5); |
| w.setMinimumSize(nonDefaultSize); |
| w.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QVERIFY2(w.height() >= nonDefaultSize.height(), |
| msgComparisonFailed(w.height(), ">=", nonDefaultSize.height())); |
| QVERIFY2(w.width() >= nonDefaultSize.width(), |
| msgComparisonFailed(w.width(), ">=", nonDefaultSize.width())); |
| } |
| |
| void tst_QWidget::setMaximumSize() |
| { |
| QWidget w; |
| QSize defaultSize = w.size(); |
| |
| w.setMinimumSize(defaultSize + QSize(100, 100)); |
| QCOMPARE(w.size(), defaultSize + QSize(100, 100)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| w.setMinimumSize(defaultSize); |
| |
| w.setMaximumSize(defaultSize + QSize(200, 200)); |
| QCOMPARE(w.size(), defaultSize + QSize(100, 100)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setMaximumSize(defaultSize + QSize(50, 50)); |
| QCOMPARE(w.size(), defaultSize + QSize(50, 50)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| } |
| |
| void tst_QWidget::setFixedSize() |
| { |
| QWidget w; |
| QSize defaultSize = w.size(); |
| |
| w.setFixedSize(defaultSize + QSize(100, 100)); |
| QCOMPARE(w.size(), defaultSize + QSize(100, 100)); |
| QVERIFY(w.testAttribute(Qt::WA_Resized)); |
| |
| w.setFixedSize(defaultSize + QSize(200, 200)); |
| |
| QCOMPARE(w.minimumSize(), defaultSize + QSize(200,200)); |
| QCOMPARE(w.maximumSize(), defaultSize + QSize(200,200)); |
| QCOMPARE(w.size(), defaultSize + QSize(200, 200)); |
| QVERIFY(w.testAttribute(Qt::WA_Resized)); |
| |
| w.setFixedSize(defaultSize + QSize(50, 50)); |
| QCOMPARE(w.size(), defaultSize + QSize(50, 50)); |
| QVERIFY(w.testAttribute(Qt::WA_Resized)); |
| |
| w.setAttribute(Qt::WA_Resized, false); |
| w.setFixedSize(defaultSize + QSize(50, 50)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setFixedSize(defaultSize + QSize(150, 150)); |
| w.showNormal(); |
| QVERIFY(QTest::qWaitForWindowActive(&w)); |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| else if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support move/resize", Abort); |
| QCOMPARE(w.size(), defaultSize + QSize(150,150)); |
| } |
| |
| void tst_QWidget::ensureCreated() |
| { |
| { |
| QWidget widget; |
| WId widgetWinId = widget.winId(); |
| Q_UNUSED(widgetWinId); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| } |
| |
| { |
| QWidget window; |
| |
| QDialog dialog(&window); |
| dialog.setWindowModality(Qt::NonModal); |
| |
| WId dialogWinId = dialog.winId(); |
| Q_UNUSED(dialogWinId); |
| QVERIFY(dialog.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(window.testAttribute(Qt::WA_WState_Created)); |
| } |
| |
| { |
| QWidget window; |
| |
| QDialog dialog(&window); |
| dialog.setWindowModality(Qt::WindowModal); |
| |
| WId dialogWinId = dialog.winId(); |
| Q_UNUSED(dialogWinId); |
| QVERIFY(dialog.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(window.testAttribute(Qt::WA_WState_Created)); |
| } |
| |
| { |
| QWidget window; |
| |
| QDialog dialog(&window); |
| dialog.setWindowModality(Qt::ApplicationModal); |
| |
| WId dialogWinId = dialog.winId(); |
| Q_UNUSED(dialogWinId); |
| QVERIFY(dialog.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(window.testAttribute(Qt::WA_WState_Created)); |
| } |
| } |
| |
| class WinIdChangeWidget : public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| protected: |
| bool event(QEvent *e) override |
| { |
| if (e->type() == QEvent::WinIdChange) { |
| m_winIdList.append(internalWinId()); |
| return true; |
| } |
| return QWidget::event(e); |
| } |
| public: |
| QList<WId> m_winIdList; |
| int winIdChangeEventCount() const { return m_winIdList.count(); } |
| }; |
| |
| class CreateDestroyWidget : public WinIdChangeWidget |
| { |
| public: |
| void create() { QWidget::create(); } |
| void destroy() { QWidget::destroy(); } |
| }; |
| |
| void tst_QWidget::createAndDestroy() |
| { |
| CreateDestroyWidget widget; |
| |
| // Create and destroy via QWidget |
| widget.create(); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 1); |
| QVERIFY(widget.internalWinId()); |
| |
| widget.destroy(); |
| QVERIFY(!widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 2); |
| QVERIFY(!widget.internalWinId()); |
| |
| // Create via QWidget, destroy via QWindow |
| widget.create(); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 3); |
| QVERIFY(widget.internalWinId()); |
| |
| widget.windowHandle()->destroy(); |
| QVERIFY(!widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 4); |
| QVERIFY(!widget.internalWinId()); |
| |
| // Create via QWidget again |
| widget.create(); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 5); |
| QVERIFY(widget.internalWinId()); |
| |
| // Destroy via QWindow, create via QWindow |
| widget.windowHandle()->destroy(); |
| QVERIFY(widget.windowHandle()); |
| QVERIFY(!widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 6); |
| QVERIFY(!widget.internalWinId()); |
| |
| widget.windowHandle()->create(); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| QCOMPARE(widget.winIdChangeEventCount(), 7); |
| QVERIFY(widget.internalWinId()); |
| } |
| |
| void tst_QWidget::winIdChangeEvent() |
| { |
| { |
| // Transforming an alien widget into a native widget |
| WinIdChangeWidget widget; |
| const WId winIdBefore = widget.internalWinId(); |
| const WId winIdAfter = widget.winId(); |
| QVERIFY(winIdBefore != winIdAfter); |
| QCOMPARE(widget.winIdChangeEventCount(), 1); |
| } |
| |
| { |
| // Changing parent of a native widget |
| QWidget parent1, parent2; |
| WinIdChangeWidget child(&parent1); |
| const WId winIdBefore = child.winId(); |
| QCOMPARE(child.winIdChangeEventCount(), 1); |
| child.setParent(&parent2); |
| const WId winIdAfter = child.internalWinId(); |
| QCOMPARE(winIdBefore, winIdAfter); |
| QCOMPARE(child.winIdChangeEventCount(), 3); |
| // winId is set to zero during reparenting |
| QCOMPARE(WId(0), child.m_winIdList[1]); |
| } |
| |
| { |
| // Changing grandparent of a native widget |
| QWidget grandparent1, grandparent2; |
| QWidget parent(&grandparent1); |
| WinIdChangeWidget child(&parent); |
| const WId winIdBefore = child.winId(); |
| QCOMPARE(child.winIdChangeEventCount(), 1); |
| parent.setParent(&grandparent2); |
| const WId winIdAfter = child.internalWinId(); |
| QCOMPARE(winIdBefore, winIdAfter); |
| QCOMPARE(child.winIdChangeEventCount(), 1); |
| } |
| |
| { |
| // Changing parent of an alien widget |
| QWidget parent1, parent2; |
| WinIdChangeWidget child(&parent1); |
| const WId winIdBefore = child.internalWinId(); |
| child.setParent(&parent2); |
| const WId winIdAfter = child.internalWinId(); |
| QCOMPARE(winIdBefore, winIdAfter); |
| QCOMPARE(child.winIdChangeEventCount(), 0); |
| } |
| |
| { |
| // Making native child widget into a top-level window |
| QWidget parent; |
| WinIdChangeWidget child(&parent); |
| child.winId(); |
| const WId winIdBefore = child.internalWinId(); |
| QCOMPARE(child.winIdChangeEventCount(), 1); |
| const Qt::WindowFlags flags = child.windowFlags(); |
| child.setWindowFlags(flags | Qt::Window); |
| const WId winIdAfter = child.internalWinId(); |
| QCOMPARE(winIdBefore, winIdAfter); |
| QCOMPARE(child.winIdChangeEventCount(), 3); |
| // winId is set to zero during reparenting |
| QCOMPARE(WId(0), child.m_winIdList[1]); |
| } |
| } |
| |
| void tst_QWidget::persistentWinId() |
| { |
| QScopedPointer<QWidget> parent(new QWidget); |
| QWidget *w1 = new QWidget; |
| QWidget *w2 = new QWidget; |
| QWidget *w3 = new QWidget; |
| w1->setParent(parent.data()); |
| w2->setParent(w1); |
| w3->setParent(w2); |
| |
| WId winId1 = w1->winId(); |
| WId winId2 = w2->winId(); |
| WId winId3 = w3->winId(); |
| |
| // reparenting should preserve the winId of the widget being reparented and of its children |
| w1->setParent(nullptr); |
| QCOMPARE(w1->winId(), winId1); |
| QCOMPARE(w2->winId(), winId2); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w1->setParent(parent.data()); |
| QCOMPARE(w1->winId(), winId1); |
| QCOMPARE(w2->winId(), winId2); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w2->setParent(nullptr); |
| QCOMPARE(w2->winId(), winId2); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w2->setParent(parent.data()); |
| QCOMPARE(w2->winId(), winId2); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w2->setParent(w1); |
| QCOMPARE(w2->winId(), winId2); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w3->setParent(nullptr); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w3->setParent(w1); |
| QCOMPARE(w3->winId(), winId3); |
| |
| w3->setParent(w2); |
| QCOMPARE(w3->winId(), winId3); |
| } |
| |
| void tst_QWidget::transientParent() |
| { |
| QWidget topLevel; |
| topLevel.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize)); |
| topLevel.setWindowTitle(__FUNCTION__); |
| QWidget *child = new QWidget(&topLevel); |
| QMenu *menu = new QMenu(child); // QTBUG-41898: Use top level as transient parent for native widgets as well. |
| QToolButton *toolButton = new QToolButton(child); |
| toolButton->setMenu(menu); |
| toolButton->winId(); |
| topLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| QCOMPARE(menu->windowHandle()->transientParent(), topLevel.windowHandle()); |
| } |
| |
| void tst_QWidget::showNativeChild() |
| { |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support setGeometry"); |
| QWidget topLevel; |
| topLevel.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize)); |
| topLevel.setWindowTitle(__FUNCTION__); |
| QWidget child(&topLevel); |
| child.winId(); |
| topLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| } |
| |
| class ShowHideEventWidget : public QWidget |
| { |
| public: |
| int numberOfShowEvents = 0, numberOfHideEvents = 0; |
| int numberOfSpontaneousShowEvents = 0, numberOfSpontaneousHideEvents = 0; |
| |
| using QWidget::QWidget; |
| using QWidget::create; |
| |
| void showEvent(QShowEvent *e) override |
| { |
| ++numberOfShowEvents; |
| if (e->spontaneous()) |
| ++numberOfSpontaneousShowEvents; |
| } |
| |
| void hideEvent(QHideEvent *e) override |
| { |
| ++numberOfHideEvents; |
| if (e->spontaneous()) |
| ++numberOfSpontaneousHideEvents; |
| } |
| }; |
| |
| void tst_QWidget::showHideEvent_data() |
| { |
| QTest::addColumn<bool>("show"); |
| QTest::addColumn<bool>("hide"); |
| QTest::addColumn<bool>("create"); |
| QTest::addColumn<int>("expectedShowEvents"); |
| QTest::addColumn<int>("expectedHideEvents"); |
| |
| QTest::newRow("window: only show") |
| << true |
| << false |
| << false |
| << 1 |
| << 0; |
| QTest::newRow("window: show/hide") |
| << true |
| << true |
| << false |
| << 1 |
| << 1; |
| QTest::newRow("window: show/hide/create") |
| << true |
| << true |
| << true |
| << 1 |
| << 1; |
| QTest::newRow("window: hide/create") |
| << false |
| << true |
| << true |
| << 0 |
| << 0; |
| QTest::newRow("window: only hide") |
| << false |
| << true |
| << false |
| << 0 |
| << 0; |
| QTest::newRow("window: nothing") |
| << false |
| << false |
| << false |
| << 0 |
| << 0; |
| } |
| |
| void tst_QWidget::showHideEvent() |
| { |
| QFETCH(bool, show); |
| QFETCH(bool, hide); |
| QFETCH(bool, create); |
| QFETCH(int, expectedShowEvents); |
| QFETCH(int, expectedHideEvents); |
| |
| ShowHideEventWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| if (show) |
| widget.show(); |
| if (hide) |
| widget.hide(); |
| if (create && !widget.testAttribute(Qt::WA_WState_Created)) |
| widget.create(); |
| |
| QCOMPARE(widget.numberOfShowEvents, expectedShowEvents); |
| QCOMPARE(widget.numberOfHideEvents, expectedHideEvents); |
| } |
| |
| void tst_QWidget::showHideEventWhileMinimize() |
| { |
| const QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); |
| if (!pi->hasCapability(QPlatformIntegration::MultipleWindows) |
| || !pi->hasCapability(QPlatformIntegration::NonFullScreenWindows) |
| || !pi->hasCapability(QPlatformIntegration::WindowManagement)) { |
| QSKIP("This test requires window management capabilities"); |
| } |
| // QTBUG-41312, hide, show events should be received during minimized. |
| ShowHideEventWidget widget; |
| widget.setWindowTitle(__FUNCTION__); |
| widget.resize(m_testWidgetSize); |
| centerOnScreen(&widget); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| const int showEventsBeforeMinimize = widget.numberOfShowEvents; |
| const int hideEventsBeforeMinimize = widget.numberOfHideEvents; |
| widget.showMinimized(); |
| QTRY_COMPARE(widget.numberOfHideEvents, hideEventsBeforeMinimize + 1); |
| widget.showNormal(); |
| QTRY_COMPARE(widget.numberOfShowEvents, showEventsBeforeMinimize + 1); |
| } |
| |
| void tst_QWidget::showHideChildrenWhileMinimize_QTBUG50589() |
| { |
| const QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); |
| if (!pi->hasCapability(QPlatformIntegration::MultipleWindows) |
| || !pi->hasCapability(QPlatformIntegration::NonFullScreenWindows) |
| || !pi->hasCapability(QPlatformIntegration::WindowManagement)) { |
| QSKIP("This test requires window management capabilities"); |
| } |
| |
| QWidget parent; |
| ShowHideEventWidget child(&parent); |
| |
| parent.setWindowTitle(QTest::currentTestFunction()); |
| parent.resize(m_testWidgetSize); |
| centerOnScreen(&parent); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| |
| const int showEventsBeforeMinimize = child.numberOfSpontaneousShowEvents; |
| const int hideEventsBeforeMinimize = child.numberOfSpontaneousHideEvents; |
| parent.showMinimized(); |
| QTRY_COMPARE(child.numberOfSpontaneousHideEvents, hideEventsBeforeMinimize + 1); |
| parent.showNormal(); |
| QTRY_COMPARE(child.numberOfSpontaneousShowEvents, showEventsBeforeMinimize + 1); |
| } |
| |
| void tst_QWidget::update() |
| { |
| #ifdef Q_OS_MACOS |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| Q_CHECK_PAINTEVENTS |
| |
| UpdateWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.resize(100, 100); |
| centerOnScreen(&w); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| |
| QTRY_COMPARE(w.numPaintEvents, 1); |
| |
| QCOMPARE(w.visibleRegion(), QRegion(w.rect())); |
| QCOMPARE(w.paintedRegion, w.visibleRegion()); |
| w.reset(); |
| |
| UpdateWidget child(&w); |
| child.setGeometry(10, 10, 80, 80); |
| child.show(); |
| |
| QPoint childOffset = child.mapToParent(QPoint()); |
| |
| // widgets are transparent by default, so both should get repaints |
| { |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support setGeometry", Abort); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| QCOMPARE(child.numPaintEvents, 1); |
| QCOMPARE(child.visibleRegion(), QRegion(child.rect())); |
| QCOMPARE(child.paintedRegion, child.visibleRegion()); |
| QCOMPARE(w.numPaintEvents, 1); |
| QCOMPARE(w.visibleRegion(), QRegion(w.rect())); |
| QCOMPARE(w.paintedRegion, child.visibleRegion().translated(childOffset)); |
| |
| w.reset(); |
| child.reset(); |
| |
| w.update(); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| QCOMPARE(child.numPaintEvents, 1); |
| QCOMPARE(child.visibleRegion(), QRegion(child.rect())); |
| QCOMPARE(child.paintedRegion, child.visibleRegion()); |
| QCOMPARE(w.numPaintEvents, 1); |
| QCOMPARE(w.visibleRegion(), QRegion(w.rect())); |
| QCOMPARE(w.paintedRegion, w.visibleRegion()); |
| } |
| |
| QPalette opaquePalette = child.palette(); |
| opaquePalette.setColor(child.backgroundRole(), QColor(Qt::red)); |
| |
| // setting an opaque background on the child should prevent paint-events |
| // for the parent in the child area |
| { |
| child.setPalette(opaquePalette); |
| child.setAutoFillBackground(true); |
| QApplication::processEvents(); |
| |
| w.reset(); |
| child.reset(); |
| |
| w.update(); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| |
| QCOMPARE(w.numPaintEvents, 1); |
| QRegion expectedVisible = QRegion(w.rect()) |
| - child.visibleRegion().translated(childOffset); |
| QCOMPARE(w.visibleRegion(), expectedVisible); |
| QCOMPARE(w.paintedRegion, expectedVisible); |
| QCOMPARE(child.numPaintEvents, 0); |
| |
| w.reset(); |
| child.reset(); |
| |
| child.update(); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| |
| QCOMPARE(w.numPaintEvents, 0); |
| QCOMPARE(child.numPaintEvents, 1); |
| QCOMPARE(child.paintedRegion, child.visibleRegion()); |
| |
| w.reset(); |
| child.reset(); |
| } |
| |
| // overlapping sibling |
| UpdateWidget sibling(&w); |
| child.setGeometry(10, 10, 20, 20); |
| sibling.setGeometry(15, 15, 20, 20); |
| sibling.show(); |
| |
| QApplication::processEvents(); |
| w.reset(); |
| child.reset(); |
| sibling.reset(); |
| |
| const QPoint siblingOffset = sibling.mapToParent(QPoint()); |
| |
| sibling.update(); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| |
| // child is opaque, sibling transparent |
| { |
| QCOMPARE(sibling.numPaintEvents, 1); |
| QCOMPARE(sibling.paintedRegion, sibling.visibleRegion()); |
| |
| QCOMPARE(child.numPaintEvents, 1); |
| QCOMPARE(child.paintedRegion.translated(childOffset), |
| child.visibleRegion().translated(childOffset) |
| & sibling.visibleRegion().translated(siblingOffset)); |
| |
| QCOMPARE(w.numPaintEvents, 1); |
| QCOMPARE(w.paintedRegion, |
| w.visibleRegion() & sibling.visibleRegion().translated(siblingOffset)); |
| QCOMPARE(w.paintedRegion, |
| (w.visibleRegion() - child.visibleRegion().translated(childOffset)) |
| & sibling.visibleRegion().translated(siblingOffset)); |
| |
| } |
| w.reset(); |
| child.reset(); |
| sibling.reset(); |
| |
| sibling.setPalette(opaquePalette); |
| sibling.setAutoFillBackground(true); |
| |
| sibling.update(); |
| QApplication::processEvents(); |
| QApplication::processEvents(); |
| |
| // child opaque, sibling opaque |
| { |
| QCOMPARE(sibling.numPaintEvents, 1); |
| QCOMPARE(sibling.paintedRegion, sibling.visibleRegion()); |
| |
| #ifdef Q_OS_OSX |
| if (child.internalWinId()) // child is native |
| QEXPECT_FAIL(0, "Cocoa compositor paints child and sibling", Continue); |
| #endif |
| QCOMPARE(child.numPaintEvents, 0); |
| QCOMPARE(child.visibleRegion(), |
| QRegion(child.rect()) |
| - sibling.visibleRegion().translated(siblingOffset - childOffset)); |
| |
| QCOMPARE(w.numPaintEvents, 0); |
| QCOMPARE(w.visibleRegion(), |
| QRegion(w.rect()) |
| - child.visibleRegion().translated(childOffset) |
| - sibling.visibleRegion().translated(siblingOffset)); |
| } |
| } |
| |
| #ifndef Q_OS_OSX |
| static inline bool isOpaque(QWidget *widget) |
| { |
| if (!widget) |
| return false; |
| return qt_widget_private(widget)->isOpaque; |
| } |
| #endif |
| |
| void tst_QWidget::isOpaque() |
| { |
| #ifndef Q_OS_OSX |
| QWidget w; |
| QVERIFY(::isOpaque(&w)); |
| |
| QWidget child(&w); |
| QVERIFY(!::isOpaque(&child)); |
| |
| child.setAutoFillBackground(true); |
| QVERIFY(::isOpaque(&child)); |
| |
| QPalette palette; |
| |
| // background color |
| |
| palette = child.palette(); |
| palette.setColor(child.backgroundRole(), QColor(255, 0, 0, 127)); |
| child.setPalette(palette); |
| QVERIFY(!::isOpaque(&child)); |
| |
| palette.setColor(child.backgroundRole(), QColor(255, 0, 0, 255)); |
| child.setPalette(palette); |
| QVERIFY(::isOpaque(&child)); |
| |
| palette.setColor(QPalette::Window, QColor(0, 0, 255, 127)); |
| w.setPalette(palette); |
| |
| QVERIFY(!::isOpaque(&w)); |
| |
| child.setAutoFillBackground(false); |
| QVERIFY(!::isOpaque(&child)); |
| |
| // Qt::WA_OpaquePaintEvent |
| |
| child.setAttribute(Qt::WA_OpaquePaintEvent); |
| QVERIFY(::isOpaque(&child)); |
| |
| child.setAttribute(Qt::WA_OpaquePaintEvent, false); |
| QVERIFY(!::isOpaque(&child)); |
| |
| // Qt::WA_NoSystemBackground |
| |
| child.setAttribute(Qt::WA_NoSystemBackground); |
| QVERIFY(!::isOpaque(&child)); |
| |
| child.setAttribute(Qt::WA_NoSystemBackground, false); |
| QVERIFY(!::isOpaque(&child)); |
| |
| palette.setColor(QPalette::Window, QColor(0, 0, 255, 255)); |
| w.setPalette(palette); |
| QVERIFY(::isOpaque(&w)); |
| |
| w.setAttribute(Qt::WA_NoSystemBackground); |
| QVERIFY(!::isOpaque(&w)); |
| |
| w.setAttribute(Qt::WA_NoSystemBackground, false); |
| QVERIFY(::isOpaque(&w)); |
| |
| { |
| QPalette palette = QApplication::palette(); |
| QPalette old = palette; |
| palette.setColor(QPalette::Window, Qt::transparent); |
| QApplication::setPalette(palette); |
| |
| QWidget widget; |
| QVERIFY(!::isOpaque(&widget)); |
| |
| QApplication::setPalette(old); |
| QCOMPARE(::isOpaque(&widget), old.color(QPalette::Window).alpha() == 255); |
| } |
| #endif |
| } |
| |
| #ifndef Q_OS_OSX |
| /* |
| Test that scrolling of a widget invalidates the correct regions |
| */ |
| void tst_QWidget::scroll() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| QScreen *screen = QGuiApplication::primaryScreen(); |
| const int w = qMin(500, screen->availableGeometry().width() / 2); |
| const int h = qMin(500, screen->availableGeometry().height() / 2); |
| |
| UpdateWidget updateWidget; |
| updateWidget.resize(w, h); |
| updateWidget.reset(); |
| updateWidget.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)); |
| updateWidget.showNormal(); |
| QApplication::setActiveWindow(&updateWidget); |
| QVERIFY(QTest::qWaitForWindowActive(&updateWidget)); |
| QVERIFY(updateWidget.numPaintEvents > 0); |
| |
| { |
| updateWidget.reset(); |
| updateWidget.scroll(10, 10); |
| QCoreApplication::processEvents(); |
| QRegion dirty(QRect(0, 0, w, 10)); |
| dirty += QRegion(QRect(0, 10, 10, h - 10)); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT does not support move/resize", Abort); |
| QTRY_COMPARE(updateWidget.paintedRegion, dirty); |
| } |
| |
| { |
| updateWidget.reset(); |
| updateWidget.update(0, 0, 10, 10); |
| updateWidget.scroll(0, 10); |
| QCoreApplication::processEvents(); |
| QRegion dirty(QRect(0, 0, w, 10)); |
| dirty += QRegion(QRect(0, 10, 10, 10)); |
| QTRY_COMPARE(updateWidget.paintedRegion, dirty); |
| } |
| |
| if (updateWidget.width() < 200 || updateWidget.height() < 200) |
| QSKIP("Skip this test due to too small screen geometry."); |
| |
| { |
| updateWidget.reset(); |
| updateWidget.update(0, 0, 100, 100); |
| updateWidget.scroll(10, 10, QRect(50, 50, 100, 100)); |
| QCoreApplication::processEvents(); |
| QRegion dirty(QRect(0, 0, 100, 50)); |
| dirty += QRegion(QRect(0, 50, 150, 10)); |
| dirty += QRegion(QRect(0, 60, 110, 40)); |
| dirty += QRegion(QRect(50, 100, 60, 10)); |
| dirty += QRegion(QRect(50, 110, 10, 40)); |
| QTRY_COMPARE(updateWidget.paintedRegion, dirty); |
| } |
| |
| { |
| updateWidget.reset(); |
| updateWidget.update(0, 0, 100, 100); |
| updateWidget.scroll(10, 10, QRect(100, 100, 100, 100)); |
| QCoreApplication::processEvents(); |
| QRegion dirty(QRect(0, 0, 100, 100)); |
| dirty += QRegion(QRect(100, 100, 100, 10)); |
| dirty += QRegion(QRect(100, 110, 10, 90)); |
| QTRY_COMPARE(updateWidget.paintedRegion, dirty); |
| } |
| } |
| |
| // QTBUG-38999, scrolling a widget with native child widgets should move the children. |
| void tst_QWidget::scrollNativeChildren() |
| { |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(__FUNCTION__)); |
| parent.resize(400, 400); |
| centerOnScreen(&parent); |
| QLabel *nativeLabel = new QLabel(QStringLiteral("nativeLabel"), &parent); |
| const QPoint oldLabelPos(100, 100); |
| nativeLabel->move(oldLabelPos); |
| QVERIFY(nativeLabel->winId()); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| const QPoint delta(50, 50); |
| parent.scroll(delta.x(), delta.y()); |
| const QPoint newLabelPos = oldLabelPos + delta; |
| QWindow *labelWindow = nativeLabel->windowHandle(); |
| QVERIFY(labelWindow); |
| QTRY_COMPARE(labelWindow->geometry().topLeft(), newLabelPos); |
| QTRY_COMPARE(nativeLabel->geometry().topLeft(), newLabelPos); |
| } |
| |
| #endif // Mac OS |
| |
| class DestroyedSlotChecker : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| bool wasQWidget = false; |
| |
| public slots: |
| void destroyedSlot(QObject *object) |
| { |
| wasQWidget = (qobject_cast<QWidget *>(object) != nullptr || object->isWidgetType()); |
| } |
| }; |
| |
| /* |
| Test that qobject_cast<QWidget*> returns 0 in a slot |
| connected to QObject::destroyed. |
| */ |
| void tst_QWidget::qobject_castInDestroyedSlot() |
| { |
| DestroyedSlotChecker checker; |
| |
| QWidget *widget = new QWidget(); |
| |
| QObject::connect(widget, &QObject::destroyed, &checker, &DestroyedSlotChecker::destroyedSlot); |
| delete widget; |
| |
| QVERIFY(checker.wasQWidget); |
| } |
| |
| // Since X11 WindowManager operations are all async, and we have no way to know if the window |
| // manager has finished playing with the window geometry, this test can't be reliable on X11. |
| |
| using Rects = QVector<QRect>; |
| |
| void tst_QWidget::setWindowGeometry_data() |
| { |
| QTest::addColumn<Rects>("rects"); |
| QTest::addColumn<int>("windowFlags"); |
| |
| QVector<Rects> rects; |
| const int width = m_testWidgetSize.width(); |
| const int height = m_testWidgetSize.height(); |
| const QRect availableAdjusted = QGuiApplication::primaryScreen()->availableGeometry().adjusted(100, 100, -100, -100); |
| rects << Rects{QRect(m_availableTopLeft + QPoint(100, 100), m_testWidgetSize), |
| availableAdjusted, |
| QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)), |
| QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)), |
| QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0))} |
| << Rects{availableAdjusted, |
| QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)), |
| QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)), |
| QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)), |
| QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height))} |
| << Rects{QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)), |
| QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)), |
| QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)), |
| QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)), |
| availableAdjusted} |
| << Rects{QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0)), |
| QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)), |
| QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)), |
| availableAdjusted, |
| QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height))} |
| << Rects{QRect(m_availableTopLeft + QPoint(130, 50), QSize(0, 0)), |
| QRect(m_availableTopLeft + QPoint(100, 100), QSize(width, height)), |
| availableAdjusted, |
| QRect(m_availableTopLeft + QPoint(130, 100), QSize(0, height)), |
| QRect(m_availableTopLeft + QPoint(100, 50), QSize(width, 0))}; |
| |
| const Qt::WindowFlags windowFlags[] = {Qt::WindowFlags(), Qt::FramelessWindowHint}; |
| |
| const bool skipEmptyRects = (m_platform == QStringLiteral("windows")); |
| for (Rects l : qAsConst(rects)) { |
| if (skipEmptyRects) { |
| l.erase(std::remove_if(l.begin(), l.end(), [] (const QRect &r) { return r.isEmpty(); }), |
| l.end()); |
| } |
| const QRect &rect = l.constFirst(); |
| for (int windowFlag : windowFlags) { |
| QTest::newRow(QString("%1,%2 %3x%4, flags %5") |
| .arg(rect.x()) |
| .arg(rect.y()) |
| .arg(rect.width()) |
| .arg(rect.height()) |
| .arg(windowFlag, 0, 16).toLatin1()) |
| << l |
| << windowFlag; |
| } |
| } |
| } |
| |
| void tst_QWidget::setWindowGeometry() |
| { |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("X11: Skip this test due to Window manager positioning issues."); |
| else if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support setWindowGeometry"); |
| |
| QFETCH(Rects, rects); |
| QFETCH(int, windowFlags); |
| QRect rect = rects.takeFirst(); |
| |
| { |
| // test setGeometry() without actually showing the window |
| QWidget widget; |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.setGeometry(rect); |
| QTest::qWait(100); |
| QCOMPARE(widget.geometry(), rect); |
| |
| // setGeometry() without showing |
| for (const QRect &r : qAsConst(rects)) { |
| widget.setGeometry(r); |
| QTest::qWait(100); |
| QCOMPARE(widget.geometry(), r); |
| } |
| } |
| |
| { |
| // setGeometry() first, then show() |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.setGeometry(rect); |
| widget.showNormal(); |
| if (rect.isValid()) { |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| } else { |
| // in case of an invalid rect, wait for the geometry to become |
| // adjusted to the actual (valid) value. |
| QApplication::processEvents(); |
| } |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // setGeometry() while shown |
| for (const QRect &r : qAsConst(rects)) { |
| widget.setGeometry(r); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), r); |
| } |
| widget.setGeometry(rect); |
| QTest::qWait(20); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // now hide |
| widget.hide(); |
| QTest::qWait(20); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // setGeometry() after hide() |
| for (const QRect &r : qAsConst(rects)) { |
| widget.setGeometry(r); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), r); |
| } |
| widget.setGeometry(rect); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // show() again, geometry() should still be the same |
| widget.show(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // final hide(), again geometry() should be unchanged |
| widget.hide(); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| } |
| |
| { |
| // show() first, then setGeometry() |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.showNormal(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| widget.setGeometry(rect); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // setGeometry() while shown |
| for (const QRect &r : qAsConst(rects)) { |
| widget.setGeometry(r); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), r); |
| } |
| widget.setGeometry(rect); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // now hide |
| widget.hide(); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // setGeometry() after hide() |
| for (const QRect &r : qAsConst(rects)) { |
| widget.setGeometry(r); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), r); |
| } |
| widget.setGeometry(rect); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // show() again, geometry() should still be the same |
| widget.show(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| |
| // final hide(), again geometry() should be unchanged |
| widget.hide(); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.geometry(), rect); |
| } |
| } |
| |
| #if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) |
| void tst_QWidget::setGeometry_win() |
| { |
| QWidget widget; |
| |
| setFrameless(&widget); |
| widget.setGeometry(QRect(m_availableTopLeft + QPoint(0, 600), QSize(100, 100))); |
| widget.show(); |
| widget.setWindowState(widget.windowState() | Qt::WindowMaximized); |
| QRect geom = widget.normalGeometry(); |
| widget.close(); |
| widget.setGeometry(geom); |
| widget.setWindowState(widget.windowState() | Qt::WindowMaximized); |
| widget.show(); |
| RECT rt; |
| ::GetWindowRect(winHandleOf(&widget), &rt); |
| QVERIFY2(rt.left <= m_availableTopLeft.x(), |
| msgComparisonFailed(int(rt.left), "<=", m_availableTopLeft.x())); |
| QVERIFY2(rt.top <= m_availableTopLeft.y(), |
| msgComparisonFailed(int(rt.top), "<=", m_availableTopLeft.y())); |
| } |
| #endif // defined (Q_OS_WIN) && !defined(Q_OS_WINRT) |
| |
| // Since X11 WindowManager operation are all async, and we have no way to know if the window |
| // manager has finished playing with the window geometry, this test can't be reliable on X11. |
| |
| void tst_QWidget::windowMoveResize_data() |
| { |
| setWindowGeometry_data(); |
| } |
| |
| void tst_QWidget::windowMoveResize() |
| { |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("X11: Skip this test due to Window manager positioning issues."); |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support move/resize"); |
| |
| QFETCH(Rects, rects); |
| QFETCH(int, windowFlags); |
| |
| QRect rect = rects.takeFirst(); |
| |
| { |
| // test setGeometry() without actually showing the window |
| QWidget widget; |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // move() without showing |
| for (const QRect &r : qAsConst(rects)) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), r.topLeft()); |
| QTRY_COMPARE(widget.size(), r.size()); |
| } |
| } |
| |
| { |
| // move() first, then show() |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| widget.showNormal(); |
| |
| QTest::qWait(10); |
| QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), rect.topLeft(), m_fuzz), |
| qPrintable(HighDpi::msgPointMismatch(widget.pos(), rect.topLeft()))); |
| // Windows: Minimum size of decorated windows. |
| const bool expectResizeFail = (!windowFlags && (rect.width() < 160 || rect.height() < 40)) |
| && m_platform == QStringLiteral("windows"); |
| if (!expectResizeFail) |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // move() while shown |
| for (const QRect &r : qAsConst(rects)) { |
| // XCB: First resize after show of zero-sized gets wrong win_gravity. |
| const bool expectMoveFail = !windowFlags |
| && ((widget.width() == 0 || widget.height() == 0) && r.width() != 0 && r.height() != 0) |
| && m_platform == QStringLiteral("xcb") |
| && (rect == QRect(QPoint(130, 100), QSize(0, 200)) |
| || rect == QRect(QPoint(100, 50), QSize(200, 0)) |
| || rect == QRect(QPoint(130, 50), QSize(0, 0))); |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| QApplication::processEvents(); |
| if (!expectMoveFail) { |
| QTRY_COMPARE(widget.pos(), r.topLeft()); |
| QTRY_COMPARE(widget.size(), r.size()); |
| } |
| } |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // now hide |
| widget.hide(); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // move() after hide() |
| for (const QRect &r : qAsConst(rects)) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| QApplication::processEvents(); |
| #if defined(Q_OS_OSX) |
| if (r.width() == 0 && r.height() > 0) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| } |
| #endif |
| QTRY_COMPARE(widget.pos(), r.topLeft()); |
| QTRY_COMPARE(widget.size(), r.size()); |
| } |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // show() again, pos() should be the same |
| widget.show(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // final hide(), again pos() should be unchanged |
| widget.hide(); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| } |
| |
| { |
| // show() first, then move() |
| QWidget widget; |
| if (windowFlags != 0) |
| widget.setWindowFlags(Qt::WindowFlags(windowFlags)); |
| |
| widget.showNormal(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QApplication::processEvents(); |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // move() while shown |
| for (const QRect &r : qAsConst(rects)) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), r.topLeft()); |
| QTRY_COMPARE(widget.size(), r.size()); |
| } |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // now hide |
| widget.hide(); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // move() after hide() |
| for (const QRect &r : qAsConst(rects)) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| QApplication::processEvents(); |
| #if defined(Q_OS_OSX) |
| if (r.width() == 0 && r.height() > 0) { |
| widget.move(r.topLeft()); |
| widget.resize(r.size()); |
| } |
| #endif |
| QTRY_COMPARE(widget.pos(), r.topLeft()); |
| QTRY_COMPARE(widget.size(), r.size()); |
| } |
| widget.move(rect.topLeft()); |
| widget.resize(rect.size()); |
| QApplication::processEvents(); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // show() again, pos() should be the same |
| widget.show(); |
| if (rect.isValid()) |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| |
| // final hide(), again pos() should be unchanged |
| widget.hide(); |
| QTest::qWait(10); |
| QTRY_COMPARE(widget.pos(), rect.topLeft()); |
| QTRY_COMPARE(widget.size(), rect.size()); |
| } |
| } |
| |
| class ColorWidget : public QWidget |
| { |
| public: |
| explicit ColorWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags(), |
| const QColor &c = QColor(Qt::red)) |
| : QWidget(parent, f), color(c) |
| { |
| QPalette opaquePalette = palette(); |
| opaquePalette.setColor(backgroundRole(), color); |
| setPalette(opaquePalette); |
| setAutoFillBackground(true); |
| } |
| |
| void paintEvent(QPaintEvent *e) override |
| { |
| r += e->region(); |
| } |
| |
| void reset() |
| { |
| r = QRegion(); |
| } |
| |
| void enterEvent(QEvent *) override { ++enters; } |
| void leaveEvent(QEvent *) override { ++leaves; } |
| |
| void resetCounts() |
| { |
| enters = 0; |
| leaves = 0; |
| } |
| |
| QColor color; |
| QRegion r; |
| int enters = 0; |
| int leaves = 0; |
| }; |
| |
| static inline QByteArray msgRgbMismatch(unsigned actual, unsigned expected) |
| { |
| return QByteArrayLiteral("Color mismatch, 0x") + QByteArray::number(actual, 16) + |
| QByteArrayLiteral(" != 0x") + QByteArray::number(expected, 16); |
| } |
| |
| static QPixmap grabWindow(QWindow *window, int x, int y, int width, int height) |
| { |
| QScreen *screen = window->screen(); |
| Q_ASSERT(screen); |
| return screen->grabWindow(window->winId(), x, y, width, height); |
| } |
| |
| #define VERIFY_COLOR(child, region, color) verifyColor(child, region, color, __LINE__) |
| |
| bool verifyColor(QWidget &child, const QRegion ®ion, const QColor &color, int callerLine) |
| { |
| QWindow *window = child.window()->windowHandle(); |
| Q_ASSERT(window); |
| const QPoint offset = child.mapTo(child.window(), QPoint(0,0)); |
| bool grabBackingStore = false; |
| for (QRect r : region) { |
| QRect rect = r.translated(offset); |
| for (int t = 0; t < 6; t++) { |
| const QPixmap pixmap = grabBackingStore |
| ? child.grab(rect) |
| : grabWindow(window, rect.left(), rect.top(), rect.width(), rect.height()); |
| const QSize actualSize = pixmap.size() / pixmap.devicePixelRatioF(); |
| if (!QTest::qCompare(actualSize, rect.size(), "pixmap.size()", "rect.size()", __FILE__, callerLine)) |
| return false; |
| QPixmap expectedPixmap(pixmap); /* ensure equal formats */ |
| expectedPixmap.detach(); |
| expectedPixmap.fill(color); |
| QImage image = pixmap.toImage(); |
| uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0; |
| uint firstPixel = image.pixel(0,0) | alphaCorrection; |
| if (t < 5) { |
| /* Normal run. |
| If it succeeds: return success |
| If it fails: do not return, but wait a bit and reiterate (retry) |
| */ |
| if (firstPixel == QColor(color).rgb() && image == expectedPixmap.toImage()) |
| return true; |
| if (t == 4) { |
| grabBackingStore = true; |
| rect = r; |
| } else { |
| QTest::qWait(200); |
| } |
| } else { |
| // Last run, report failure if it still fails |
| if (!QTest::qVerify(firstPixel == QColor(color).rgb(), |
| "firstPixel == QColor(color).rgb()", |
| qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), |
| __FILE__, callerLine)) |
| return false; |
| if (!QTest::qVerify(image == expectedPixmap.toImage(), |
| "image == expectedPixmap.toImage()", |
| "grabbed pixmap differs from expected pixmap", |
| __FILE__, callerLine)) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void tst_QWidget::moveChild_data() |
| { |
| QTest::addColumn<QPoint>("offset"); |
| |
| QTest::newRow("right") << QPoint(20, 0); |
| QTest::newRow("down") << QPoint(0, 20); |
| QTest::newRow("left") << QPoint(-20, 0); |
| QTest::newRow("up") << QPoint(0, -20); |
| } |
| |
| void tst_QWidget::moveChild() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| QFETCH(QPoint, offset); |
| |
| ColorWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint); |
| // prevent custom styles |
| const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows"))); |
| parent.setStyle(style.data()); |
| ColorWidget child(&parent, Qt::Widget, Qt::blue); |
| |
| parent.setGeometry(QRect(parent.screen()->availableGeometry().topLeft() + QPoint(50, 50), |
| QSize(200, 200))); |
| child.setGeometry(25, 25, 50, 50); |
| #ifndef QT_NO_CURSOR // Try to make sure the cursor is not in a taskbar area to prevent tooltips or window highlighting |
| QCursor::setPos(parent.geometry().topRight() + QPoint(50 , 50)); |
| #endif |
| parent.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| |
| QTRY_COMPARE(parent.r, QRegion(parent.rect()) - child.geometry()); |
| QTRY_COMPARE(child.r, QRegion(child.rect())); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support setGeometry (and we cannot use QEXPECT_FAIL because of VERIFY_COLOR)"); |
| VERIFY_COLOR(child, child.rect(), |
| child.color); |
| VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), parent.color); |
| parent.reset(); |
| child.reset(); |
| |
| // move |
| |
| const QRect oldGeometry = child.geometry(); |
| |
| QPoint pos = child.pos() + offset; |
| child.move(pos); |
| QTRY_COMPARE(pos, child.pos()); |
| |
| QTRY_COMPARE(parent.r, QRegion(oldGeometry) - child.geometry()); |
| #if !defined(Q_OS_OSX) |
| // should be scrolled in backingstore |
| QCOMPARE(child.r, QRegion()); |
| #endif |
| VERIFY_COLOR(child, child.rect(), child.color); |
| VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), parent.color); |
| } |
| |
| void tst_QWidget::showAndMoveChild() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| QWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint); |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| // prevent custom styles |
| const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows"))); |
| parent.setStyle(style.data()); |
| |
| QRect desktopDimensions = parent.screen()->availableGeometry(); |
| desktopDimensions = desktopDimensions.adjusted(64, 64, -64, -64); |
| |
| #ifndef QT_NO_CURSOR // Try to make sure the cursor is not in a taskbar area to prevent tooltips or window highlighting |
| QCursor::setPos(desktopDimensions.topRight() + QPoint(40, 40)); |
| #endif |
| parent.setGeometry(desktopDimensions); |
| parent.setPalette(Qt::red); |
| parent.show(); |
| QApplication::setActiveWindow(&parent); |
| QVERIFY(QTest::qWaitForWindowActive(&parent)); |
| |
| QWidget child(&parent); |
| child.resize(desktopDimensions.width()/2, desktopDimensions.height()/2); |
| child.setPalette(Qt::blue); |
| child.setAutoFillBackground(true); |
| |
| // Ensure that the child is repainted correctly when moved right after show. |
| // NB! Do NOT processEvents() (or qWait()) in between show() and move(). |
| child.show(); |
| child.move(desktopDimensions.width()/2, desktopDimensions.height()/2); |
| QCoreApplication::processEvents(); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT does not support setGeometry (and we cannot use QEXPECT_FAIL because of VERIFY_COLOR)"); |
| VERIFY_COLOR(child, child.rect(), Qt::blue); |
| VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), Qt::red); |
| } |
| |
| |
| void tst_QWidget::subtractOpaqueSiblings() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974: Cocoa only has rect granularity."); |
| #endif |
| |
| QWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setGeometry(50, 50, 300, 300); |
| |
| ColorWidget *large = new ColorWidget(&w, Qt::Widget, Qt::red); |
| large->setGeometry(50, 50, 200, 200); |
| |
| ColorWidget *medium = new ColorWidget(large, Qt::Widget, Qt::gray); |
| medium->setGeometry(50, 50, 100, 100); |
| |
| ColorWidget *tall = new ColorWidget(&w, Qt::Widget, Qt::blue); |
| tall->setGeometry(100, 30, 50, 100); |
| |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| |
| large->reset(); |
| medium->reset(); |
| tall->reset(); |
| |
| medium->update(); |
| |
| // QWidgetPrivate::subtractOpaqueSiblings() should prevent parts of medium |
| // to be repainted and tall from be repainted at all. |
| |
| QTRY_COMPARE(large->r, QRegion()); |
| QTRY_COMPARE(tall->r, QRegion()); |
| QTRY_COMPARE(medium->r.translated(medium->mapTo(&w, QPoint())), |
| QRegion(medium->geometry().translated(large->pos())) |
| - tall->geometry()); |
| } |
| |
| void tst_QWidget::deleteStyle() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.setStyle(QStyleFactory::create(QLatin1String("Windows"))); |
| widget.show(); |
| delete widget.style(); |
| QCoreApplication::processEvents(); |
| } |
| |
| class TopLevelFocusCheck: public QWidget |
| { |
| Q_OBJECT |
| public: |
| QLineEdit* edit; |
| explicit TopLevelFocusCheck(QWidget *parent = nullptr) |
| : QWidget(parent), edit(new QLineEdit(this)) |
| { |
| edit->hide(); |
| edit->installEventFilter(this); |
| } |
| |
| public slots: |
| void mouseDoubleClickEvent ( QMouseEvent * /*event*/ ) override |
| { |
| edit->show(); |
| edit->setFocus(Qt::OtherFocusReason); |
| QCoreApplication::processEvents(); |
| } |
| bool eventFilter(QObject *obj, QEvent *event) override |
| { |
| if (obj == edit && event->type()== QEvent::FocusOut) { |
| edit->hide(); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| void tst_QWidget::multipleToplevelFocusCheck() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) |
| QSKIP("Window activation is not supported"); |
| else if (m_platform == QStringLiteral("winrt")) |
| QSKIP("Winrt: Sometimes crashes in QTextLayout. - QTBUG-68297"); |
| TopLevelFocusCheck w1; |
| TopLevelFocusCheck w2; |
| |
| const QString title = QLatin1String(QTest::currentTestFunction()); |
| w1.setWindowTitle(title + QLatin1String("_W1")); |
| w1.move(m_availableTopLeft + QPoint(20, 20)); |
| w1.resize(200, 200); |
| w1.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w1)); |
| w2.setWindowTitle(title + QLatin1String("_W2")); |
| w2.move(w1.frameGeometry().topRight() + QPoint(20, 0)); |
| w2.resize(200,200); |
| w2.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w2)); |
| |
| QApplication::setActiveWindow(&w1); |
| w1.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowActive(&w1)); |
| QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); |
| QTest::mouseDClick(&w1, Qt::LeftButton); |
| QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit)); |
| |
| w2.activateWindow(); |
| QApplication::setActiveWindow(&w2); |
| QVERIFY(QTest::qWaitForWindowActive(&w2)); |
| QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); |
| QTest::mouseClick(&w2, Qt::LeftButton); |
| QTRY_COMPARE(QApplication::focusWidget(), nullptr); |
| |
| QTest::mouseDClick(&w2, Qt::LeftButton); |
| QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w2.edit)); |
| |
| w1.activateWindow(); |
| QApplication::setActiveWindow(&w1); |
| QVERIFY(QTest::qWaitForWindowActive(&w1)); |
| QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); |
| QTest::mouseDClick(&w1, Qt::LeftButton); |
| QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit)); |
| |
| w2.activateWindow(); |
| QApplication::setActiveWindow(&w2); |
| QVERIFY(QTest::qWaitForWindowActive(&w2)); |
| QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); |
| QTest::mouseClick(&w2, Qt::LeftButton); |
| QTRY_COMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| class FocusWidget: public QWidget |
| { |
| protected: |
| bool event(QEvent *ev) override |
| { |
| if (ev->type() == QEvent::FocusAboutToChange) |
| widgetDuringFocusAboutToChange = QApplication::focusWidget(); |
| return QWidget::event(ev); |
| } |
| void focusInEvent(QFocusEvent *) override |
| { |
| focusObjectDuringFocusIn = QGuiApplication::focusObject(); |
| detectedBadEventOrdering = focusObjectDuringFocusIn != mostRecentFocusObjectChange; |
| } |
| void focusOutEvent(QFocusEvent *) override |
| { |
| focusObjectDuringFocusOut = QGuiApplication::focusObject(); |
| widgetDuringFocusOut = QApplication::focusWidget(); |
| detectedBadEventOrdering = focusObjectDuringFocusOut != mostRecentFocusObjectChange; |
| } |
| |
| void focusObjectChanged(QObject *focusObject) |
| { |
| mostRecentFocusObjectChange = focusObject; |
| } |
| |
| public: |
| explicit FocusWidget(QWidget *parent) : QWidget(parent) |
| { |
| connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &FocusWidget::focusObjectChanged); |
| } |
| |
| QWidget *widgetDuringFocusAboutToChange = nullptr; |
| QWidget *widgetDuringFocusOut = nullptr; |
| |
| QObject *focusObjectDuringFocusIn = nullptr; |
| QObject *focusObjectDuringFocusOut = nullptr; |
| |
| QObject *mostRecentFocusObjectChange = nullptr; |
| bool detectedBadEventOrdering = false; |
| }; |
| |
| void tst_QWidget::setFocus() |
| { |
| QScopedPointer<QWidget> testWidget(new QWidget); |
| testWidget->resize(m_testWidgetSize); |
| testWidget->setWindowTitle(__FUNCTION__); |
| centerOnScreen(testWidget.data()); |
| testWidget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(testWidget.data())); |
| |
| const QPoint windowPos = testWidget->geometry().topRight() + QPoint(50, 0); |
| { |
| // move focus to another window |
| testWidget->activateWindow(); |
| QApplication::setActiveWindow(testWidget.data()); |
| if (testWidget->focusWidget()) |
| testWidget->focusWidget()->clearFocus(); |
| else |
| testWidget->clearFocus(); |
| |
| // window and children never shown, nobody gets focus |
| QWidget window; |
| window.setWindowTitle(QStringLiteral("#1 ") + __FUNCTION__); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| QWidget child1(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| |
| QWidget child2(&window); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| |
| child1.setFocus(); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child2.setFocus(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child2); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| { |
| // window and children show, but window not active, nobody gets focus |
| QWidget window; |
| window.setWindowTitle(QStringLiteral("#2 ") + __FUNCTION__); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| QWidget child1(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| |
| QWidget child2(&window); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| |
| window.show(); |
| |
| // note: window may be active, but we don't want it to be |
| testWidget->activateWindow(); |
| QApplication::setActiveWindow(testWidget.data()); |
| if (testWidget->focusWidget()) |
| testWidget->focusWidget()->clearFocus(); |
| else |
| testWidget->clearFocus(); |
| |
| child1.setFocus(); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT fails here - QTBUG-68297", Abort); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child2.setFocus(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child2); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| { |
| // window and children show, but window *is* active, children get focus |
| QWidget window; |
| window.setWindowTitle(QStringLiteral("#3 ") + __FUNCTION__); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| FocusWidget child1(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| |
| QWidget child2(&window); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| |
| window.show(); |
| window.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(QGuiApplication::focusWindow()); |
| |
| child1.setFocus(); |
| QTRY_VERIFY(child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), &child1); |
| |
| child2.setFocus(); |
| QVERIFY(child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child2); |
| QCOMPARE(QApplication::focusWidget(), &child2); |
| |
| // focus changed in between the events |
| QCOMPARE(child1.widgetDuringFocusAboutToChange, &child1); |
| QCOMPARE(child1.widgetDuringFocusOut, &child2); |
| } |
| |
| { |
| // window shown and active, children created, don't get focus, but get focus when shown |
| QWidget window; |
| window.setWindowTitle(QStringLiteral("#4 ") + __FUNCTION__); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| window.show(); |
| window.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(QGuiApplication::focusWindow()); |
| |
| QWidget child1(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| |
| QWidget child2(&window); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| |
| child1.setFocus(); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child1.show(); |
| QApplication::processEvents(); |
| QTRY_VERIFY(child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), &child1); |
| |
| child2.setFocus(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), &child1); |
| |
| child2.show(); |
| QVERIFY(child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child2); |
| QCOMPARE(QApplication::focusWidget(), &child2); |
| } |
| |
| { |
| // window shown and active, children created, don't get focus, |
| // even after setFocus(), hide(), then show() |
| QWidget window; |
| window.setWindowTitle(QStringLiteral("#5 ") + __FUNCTION__); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| window.show(); |
| window.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(QGuiApplication::focusWindow()); |
| |
| QWidget child1(&window); |
| child1.setFocusPolicy(Qt::StrongFocus); |
| |
| QWidget child2(&window); |
| child2.setFocusPolicy(Qt::StrongFocus); |
| |
| child1.setFocus(); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child1.hide(); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child1.show(); |
| QVERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child2.setFocus(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child2.hide(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| |
| child2.show(); |
| QVERIFY(!child2.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| } |
| |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(m_testWidgetSize); |
| window.move(windowPos); |
| |
| FocusWidget child1(&window); |
| QWidget child2(&window); |
| |
| window.show(); |
| window.activateWindow(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(QApplication::focusWindow()); |
| |
| QCOMPARE(QApplication::focusObject(), &window); |
| |
| child1.setFocus(); |
| QTRY_VERIFY(child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), &child1); |
| QCOMPARE(QApplication::focusWidget(), &child1); |
| QCOMPARE(QApplication::focusObject(), &child1); |
| QCOMPARE(child1.focusObjectDuringFocusIn, &child1); |
| QVERIFY2(!child1.detectedBadEventOrdering, |
| "focusObjectChanged should be delivered before widget focus events on setFocus"); |
| |
| child1.clearFocus(); |
| QTRY_VERIFY(!child1.hasFocus()); |
| QCOMPARE(window.focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusWidget(), nullptr); |
| QCOMPARE(QApplication::focusObject(), &window); |
| QVERIFY(child1.focusObjectDuringFocusOut != &child1); |
| QVERIFY2(!child1.detectedBadEventOrdering, |
| "focusObjectChanged should be delivered before widget focus events on clearFocus"); |
| } |
| } |
| |
| template<class T> class EventSpy : public QObject |
| { |
| public: |
| EventSpy(T *widget, QEvent::Type event) |
| : m_widget(widget), eventToSpy(event) |
| { |
| if (m_widget) |
| m_widget->installEventFilter(this); |
| } |
| |
| T *widget() const { return m_widget; } |
| int count() const { return m_count; } |
| void clear() { m_count = 0; } |
| |
| protected: |
| bool eventFilter(QObject *object, QEvent *event) override |
| { |
| if (event->type() == eventToSpy) |
| ++m_count; |
| return QObject::eventFilter(object, event); |
| } |
| |
| private: |
| T *m_widget; |
| const QEvent::Type eventToSpy; |
| int m_count = 0; |
| }; |
| |
| #ifndef QT_NO_CURSOR |
| void tst_QWidget::setCursor() |
| { |
| { |
| QWidget window; |
| window.resize(200, 200); |
| QWidget child(&window); |
| |
| QVERIFY(!window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| |
| window.setCursor(window.cursor()); |
| QVERIFY(window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window.cursor().shape()); |
| } |
| |
| // do it again, but with window show()n |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget child(&window); |
| window.show(); |
| |
| QVERIFY(!window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| |
| window.setCursor(window.cursor()); |
| QVERIFY(window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window.cursor().shape()); |
| } |
| |
| |
| { |
| QWidget window; |
| window.resize(200, 200); |
| QWidget child(&window); |
| |
| window.setCursor(Qt::WaitCursor); |
| QVERIFY(window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window.cursor().shape()); |
| } |
| |
| // same thing again, just with window show()n |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget child(&window); |
| |
| window.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| window.setCursor(Qt::WaitCursor); |
| QVERIFY(window.testAttribute(Qt::WA_SetCursor)); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window.cursor().shape()); |
| } |
| |
| // reparenting child should not cause the WA_SetCursor to become set |
| { |
| QWidget window; |
| window.resize(200, 200); |
| QWidget window2; |
| window2.resize(200, 200); |
| QWidget child(&window); |
| |
| window.setCursor(Qt::WaitCursor); |
| |
| child.setParent(nullptr); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), QCursor().shape()); |
| |
| child.setParent(&window2); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window2.cursor().shape()); |
| |
| window2.setCursor(Qt::WaitCursor); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window2.cursor().shape()); |
| } |
| |
| // again, with windows show()n |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(200, 200); |
| QWidget window2; |
| window2.resize(200, 200); |
| QWidget child(&window); |
| |
| window.setCursor(Qt::WaitCursor); |
| window.show(); |
| |
| child.setParent(nullptr); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), QCursor().shape()); |
| |
| child.setParent(&window2); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window2.cursor().shape()); |
| |
| window2.show(); |
| window2.setCursor(Qt::WaitCursor); |
| QVERIFY(!child.testAttribute(Qt::WA_SetCursor)); |
| QCOMPARE(child.cursor().shape(), window2.cursor().shape()); |
| } |
| |
| // test if CursorChange is sent |
| { |
| QWidget widget; |
| EventSpy<QWidget> spy(&widget, QEvent::CursorChange); |
| QCOMPARE(spy.count(), 0); |
| widget.setCursor(QCursor(Qt::WaitCursor)); |
| QCOMPARE(spy.count(), 1); |
| widget.unsetCursor(); |
| QCOMPARE(spy.count(), 2); |
| } |
| } |
| #endif |
| |
| void tst_QWidget::setToolTip() |
| { |
| QWidget widget; |
| widget.resize(200, 200); |
| // Showing the widget is not required for the tooltip event count test |
| // to work. It should just prevent the application from becoming inactive |
| // which would cause it to close all popups, interfering with the test |
| // in the loop below. |
| widget.setObjectName(QLatin1String("tst_qwidget setToolTip")); |
| widget.setWindowTitle(widget.objectName()); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| EventSpy<QWidget> spy(&widget, QEvent::ToolTipChange); |
| QCOMPARE(spy.count(), 0); |
| |
| QCOMPARE(widget.toolTip(), QString()); |
| widget.setToolTip(QString("Hello")); |
| QCOMPARE(widget.toolTip(), QString("Hello")); |
| QCOMPARE(spy.count(), 1); |
| widget.setToolTip(QString()); |
| QCOMPARE(widget.toolTip(), QString()); |
| QCOMPARE(spy.count(), 2); |
| |
| for (int pass = 0; pass < 2; ++pass) { |
| QCursor::setPos(m_safeCursorPos); |
| QScopedPointer<QWidget> popup(new QWidget(nullptr, Qt::Popup)); |
| popup->setObjectName(QLatin1String("tst_qwidget setToolTip #") + QString::number(pass)); |
| popup->setWindowTitle(popup->objectName()); |
| popup->setGeometry(50, 50, 150, 50); |
| QFrame *frame = new QFrame(popup.data()); |
| frame->setGeometry(0, 0, 50, 50); |
| frame->setFrameStyle(QFrame::Box | QFrame::Plain); |
| EventSpy<QWidget> spy1(frame, QEvent::ToolTip); |
| EventSpy<QWidget> spy2(popup.data(), QEvent::ToolTip); |
| frame->setMouseTracking(pass != 0); |
| frame->setToolTip(QLatin1String("TOOLTIP FRAME")); |
| popup->setToolTip(QLatin1String("TOOLTIP POPUP")); |
| popup->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(popup.data())); |
| QWindow *popupWindow = popup->windowHandle(); |
| QTest::qWait(10); |
| QTest::mouseMove(popupWindow, QPoint(25, 25)); |
| QTest::qWait(900); // delay is 700 |
| |
| QCOMPARE(spy1.count(), 1); |
| QCOMPARE(spy2.count(), 0); |
| if (pass == 0) |
| QTest::qWait(2200); // delay is 2000 |
| QTest::mouseMove(popupWindow); |
| } |
| |
| QTRY_COMPARE(QApplication::topLevelWidgets().size(), 1); |
| } |
| |
| void tst_QWidget::testWindowIconChangeEventPropagation() |
| { |
| typedef QSharedPointer<EventSpy<QWidget> > EventSpyPtr; |
| typedef QSharedPointer<EventSpy<QWindow> > WindowEventSpyPtr; |
| // Create widget hierarchy. |
| QWidget topLevelWidget; |
| topLevelWidget.setWindowTitle(QStringLiteral("TopLevel ") + __FUNCTION__); |
| topLevelWidget.resize(m_testWidgetSize); |
| topLevelWidget.move(m_availableTopLeft + QPoint(100, 100)); |
| QWidget topLevelChild(&topLevelWidget); |
| |
| QDialog dialog(&topLevelWidget); |
| dialog.resize(m_testWidgetSize); |
| dialog.move(topLevelWidget.geometry().topRight() + QPoint(100, 0)); |
| dialog.setWindowTitle(QStringLiteral("Dialog ") + __FUNCTION__); |
| QWidget dialogChild(&dialog); |
| |
| QWidgetList widgets; |
| widgets << &topLevelWidget << &topLevelChild |
| << &dialog << &dialogChild; |
| QCOMPARE(widgets.count(), 4); |
| |
| topLevelWidget.show(); |
| dialog.show(); |
| |
| QWindowList windows; |
| windows << topLevelWidget.windowHandle() << dialog.windowHandle(); |
| QWindow otherWindow; |
| windows << &otherWindow; |
| const int lastWidgetWindow = 1; // 0 and 1 are qwidgetwindow, 2 is a pure qwindow |
| |
| // Create spy lists. |
| QList <EventSpyPtr> applicationEventSpies; |
| QList <EventSpyPtr> widgetEventSpies; |
| for (QWidget *widget : qAsConst(widgets)) { |
| applicationEventSpies.append(EventSpyPtr::create(widget, QEvent::ApplicationWindowIconChange)); |
| widgetEventSpies.append(EventSpyPtr::create(widget, QEvent::WindowIconChange)); |
| } |
| QList <WindowEventSpyPtr> appWindowEventSpies; |
| QList <WindowEventSpyPtr> windowEventSpies; |
| for (QWindow *window : qAsConst(windows)) { |
| appWindowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::ApplicationWindowIconChange)); |
| windowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::WindowIconChange)); |
| } |
| |
| // QApplication::setWindowIcon |
| const QIcon windowIcon = qApp->style()->standardIcon(QStyle::SP_TitleBarMenuButton); |
| qApp->setWindowIcon(windowIcon); |
| |
| for (int i = 0; i < widgets.count(); ++i) { |
| // Check QEvent::ApplicationWindowIconChange |
| EventSpyPtr spy = applicationEventSpies.at(i); |
| QWidget *widget = spy->widget(); |
| if (widget->isWindow()) { |
| QCOMPARE(spy->count(), 1); |
| QCOMPARE(widget->windowIcon(), windowIcon); |
| } else { |
| QCOMPARE(spy->count(), 0); |
| } |
| spy->clear(); |
| |
| // Check QEvent::WindowIconChange |
| spy = widgetEventSpies.at(i); |
| QCOMPARE(spy->count(), 1); |
| spy->clear(); |
| } |
| for (int i = 0; i < windows.count(); ++i) { |
| // Check QEvent::ApplicationWindowIconChange (sent to QWindow) |
| // QWidgetWindows don't get this event, since the widget takes care of changing the icon |
| WindowEventSpyPtr spy = appWindowEventSpies.at(i); |
| QWindow *window = spy->widget(); |
| QCOMPARE(spy->count(), i > lastWidgetWindow ? 1 : 0); |
| QCOMPARE(window->icon(), windowIcon); |
| spy->clear(); |
| |
| // Check QEvent::WindowIconChange (sent to QWindow) |
| spy = windowEventSpies.at(i); |
| QCOMPARE(spy->count(), 1); |
| spy->clear(); |
| } |
| |
| // Set icon on a top-level widget. |
| topLevelWidget.setWindowIcon(QIcon()); |
| |
| for (int i = 0; i < widgets.count(); ++i) { |
| // Check QEvent::ApplicationWindowIconChange |
| EventSpyPtr spy = applicationEventSpies.at(i); |
| QCOMPARE(spy->count(), 0); |
| spy->clear(); |
| |
| // Check QEvent::WindowIconChange |
| spy = widgetEventSpies.at(i); |
| QWidget *widget = spy->widget(); |
| if (widget == &topLevelWidget) { |
| QCOMPARE(widget->windowIcon(), QIcon()); |
| QCOMPARE(spy->count(), 1); |
| } else if (topLevelWidget.isAncestorOf(widget)) { |
| QCOMPARE(spy->count(), 1); |
| } else { |
| QCOMPARE(spy->count(), 0); |
| } |
| spy->clear(); |
| } |
| |
| // Cleanup. |
| qApp->setWindowIcon(QIcon()); |
| } |
| |
| void tst_QWidget::minAndMaxSizeWithX11BypassWindowManagerHint() |
| { |
| if (m_platform != QStringLiteral("xcb")) |
| QSKIP("This test is for X11 only."); |
| // Same size as in QWidgetPrivate::create. |
| const QSize desktopSize = QApplication::desktop()->size(); |
| const QSize originalSize(desktopSize.width() / 2, desktopSize.height() * 4 / 10); |
| |
| { // Maximum size. |
| QWidget widget(nullptr, Qt::X11BypassWindowManagerHint); |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| |
| const QSize newMaximumSize = widget.size().boundedTo(originalSize) - QSize(10, 10); |
| widget.setMaximumSize(newMaximumSize); |
| QCOMPARE(widget.size(), newMaximumSize); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QCOMPARE(widget.size(), newMaximumSize); |
| } |
| |
| { // Minimum size. |
| QWidget widget(nullptr, Qt::X11BypassWindowManagerHint); |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| |
| const QSize newMinimumSize = widget.size().expandedTo(originalSize) + QSize(10, 10); |
| widget.setMinimumSize(newMinimumSize); |
| QCOMPARE(widget.size(), newMinimumSize); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QCOMPARE(widget.size(), newMinimumSize); |
| } |
| } |
| |
| class ShowHideShowWidget : public QWidget, public QAbstractNativeEventFilter |
| { |
| Q_OBJECT |
| |
| int state = 0; |
| public: |
| bool gotExpectedMapNotify = false; |
| bool gotExpectedGlobalEvent = false; |
| |
| ShowHideShowWidget() |
| { |
| startTimer(1000); |
| } |
| |
| void timerEvent(QTimerEvent *) override |
| { |
| switch (state++) { |
| case 0: |
| show(); |
| break; |
| case 1: |
| emit done(); |
| break; |
| } |
| } |
| |
| bool isMapNotify(const QByteArray &eventType, void *message) |
| { |
| enum { XCB_MAP_NOTIFY = 19 }; |
| if (state == 1 && eventType == QByteArrayLiteral("xcb_generic_event_t")) { |
| // XCB events have a uint8 response_type member at the beginning. |
| const auto responseType = *reinterpret_cast<const unsigned char *>(message); |
| return ((responseType & ~0x80) == XCB_MAP_NOTIFY); |
| } |
| return false; |
| } |
| |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| bool nativeEvent(const QByteArray &eventType, void *message, qintptr *) override |
| #else |
| bool nativeEvent(const QByteArray &eventType, void *message, long *) override |
| #endif |
| { |
| if (isMapNotify(eventType, message)) |
| gotExpectedMapNotify = true; |
| return false; |
| } |
| |
| // QAbstractNativeEventFilter interface |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override |
| #else |
| bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override |
| #endif |
| { |
| if (isMapNotify(eventType, message)) |
| gotExpectedGlobalEvent = true; |
| return false; |
| } |
| |
| signals: |
| void done(); |
| }; |
| |
| void tst_QWidget::showHideShowX11() |
| { |
| if (m_platform != QStringLiteral("xcb")) |
| QSKIP("This test is for X11 only."); |
| |
| ShowHideShowWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| qApp->installNativeEventFilter(&w); |
| |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| w.hide(); |
| |
| QEventLoop eventLoop; |
| connect(&w, &ShowHideShowWidget::done, &eventLoop, &QEventLoop::quit); |
| eventLoop.exec(); |
| |
| QVERIFY(w.gotExpectedGlobalEvent); |
| QVERIFY(w.gotExpectedMapNotify); |
| } |
| |
| void tst_QWidget::clean_qt_x11_enforce_cursor() |
| { |
| if (m_platform != QStringLiteral("xcb")) |
| QSKIP("This test is for X11 only."); |
| |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget *w = new QWidget(&window); |
| QWidget *child = new QWidget(w); |
| child->setAttribute(Qt::WA_SetCursor, true); |
| |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowActive(&window)); |
| QTest::qWait(100); |
| QCursor::setPos(window.geometry().center()); |
| QTest::qWait(100); |
| |
| child->setFocus(); |
| QApplication::processEvents(); |
| QTest::qWait(100); |
| |
| delete w; |
| } |
| |
| QGraphicsScene scene; |
| QLineEdit *edit = new QLineEdit; |
| scene.addWidget(edit); |
| |
| // If the test didn't crash, then it passed. |
| } |
| |
| class EventRecorder : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| typedef QPair<QWidget *, QEvent::Type> WidgetEventTypePair; |
| typedef QList<WidgetEventTypePair> EventList; |
| |
| using QObject::QObject; |
| |
| EventList eventList() const |
| { |
| return events; |
| } |
| |
| void clear() |
| { |
| events.clear(); |
| } |
| |
| bool eventFilter(QObject *object, QEvent *event) override |
| { |
| QWidget *widget = qobject_cast<QWidget *>(object); |
| if (widget && !event->spontaneous()) |
| events.append(qMakePair(widget, event->type())); |
| return false; |
| } |
| |
| static QByteArray msgEventListMismatch(const EventList &expected, const EventList &actual); |
| static QByteArray msgExpectFailQtBug26424(const EventList &expected, const EventList &actual) |
| { return QByteArrayLiteral("QTBUG-26424: ") + msgEventListMismatch(expected, actual); } |
| |
| private: |
| static inline void formatEventList(const EventList &l, QDebug &d); |
| |
| EventList events; |
| }; |
| |
| void EventRecorder::formatEventList(const EventList &l, QDebug &d) |
| { |
| QWidget *lastWidget = nullptr; |
| for (const WidgetEventTypePair &p : l) { |
| if (p.first != lastWidget) { |
| d << p.first << ':'; |
| lastWidget = p.first; |
| } |
| d << p.second << ' '; |
| } |
| } |
| |
| QByteArray EventRecorder::msgEventListMismatch(const EventList &expected, const EventList &actual) |
| { |
| QString result; |
| QDebug d = QDebug(&result).nospace(); |
| d << "Event list mismatch, expected " << expected.size() << " ("; |
| EventRecorder::formatEventList(expected, d); |
| d << "), actual " << actual.size() << " ("; |
| EventRecorder::formatEventList(actual, d); |
| d << ')'; |
| return result.toLocal8Bit(); |
| } |
| |
| void tst_QWidget::childEvents() |
| { |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT: This fails. QTBUG-68297."); |
| EventRecorder::EventList expected; |
| |
| // Move away the cursor; otherwise it might result in an enter event if it's |
| // inside the widget when the widget is shown. |
| QCursor::setPos(m_safeCursorPos); |
| QTest::qWait(100); |
| |
| { |
| // no children created, not shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| QCoreApplication::sendPostedEvents(); |
| |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)); |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| |
| { |
| // no children, shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| widget.showNormal(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::PlatformSurface) |
| << qMakePair(&widget, QEvent::WinIdChange) |
| << qMakePair(&widget, QEvent::WindowIconChange) |
| << qMakePair(&widget, QEvent::Move) |
| << qMakePair(&widget, QEvent::Resize) |
| << qMakePair(&widget, QEvent::Show) |
| << qMakePair(&widget, QEvent::CursorChange) |
| << qMakePair(&widget, QEvent::ShowToParent); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| spy.clear(); |
| |
| QCoreApplication::sendPostedEvents(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) |
| << qMakePair(&widget, QEvent::UpdateLater) |
| << qMakePair(&widget, QEvent::UpdateRequest); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| |
| { |
| // 2 children, not shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| QWidget child1(&widget); |
| QWidget child2; |
| child2.setParent(&widget); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 2))); |
| |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildAdded); |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| spy.clear(); |
| |
| QCoreApplication::sendPostedEvents(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 2)); |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| |
| { |
| // 2 children, widget shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| QWidget child1(&widget); |
| QWidget child2; |
| child2.setParent(&widget); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 2))); |
| |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildAdded); |
| QCOMPARE(spy.eventList(), expected); |
| spy.clear(); |
| |
| widget.showNormal(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::PlatformSurface) |
| << qMakePair(&widget, QEvent::WinIdChange) |
| << qMakePair(&widget, QEvent::WindowIconChange) |
| << qMakePair(&widget, QEvent::Move) |
| << qMakePair(&widget, QEvent::Resize) |
| << qMakePair(&widget, QEvent::Show) |
| << qMakePair(&widget, QEvent::CursorChange) |
| << qMakePair(&widget, QEvent::ShowToParent); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| spy.clear(); |
| |
| QCoreApplication::sendPostedEvents(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 2)) |
| << qMakePair(&widget, QEvent::UpdateLater) |
| << qMakePair(&widget, QEvent::UpdateRequest); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| |
| { |
| // 2 children, but one is reparented away, not shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| QWidget child1(&widget); |
| QWidget child2; |
| child2.setParent(&widget); |
| child2.setParent(nullptr); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 2))); |
| |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildRemoved); |
| QCOMPARE(spy.eventList(), expected); |
| spy.clear(); |
| |
| QCoreApplication::sendPostedEvents(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 2)); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| |
| { |
| // 2 children, but one is reparented away, then widget is shown |
| QWidget widget; |
| widget.resize(200, 200); |
| EventRecorder spy; |
| widget.installEventFilter(&spy); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); |
| |
| QWidget child1(&widget); |
| QWidget child2; |
| child2.setParent(&widget); |
| child2.setParent(nullptr); |
| |
| QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 2))); |
| |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildAdded) |
| << qMakePair(&widget, QEvent::ChildRemoved); |
| QCOMPARE(spy.eventList(), expected); |
| spy.clear(); |
| |
| widget.showNormal(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::Polish) |
| << qMakePair(&widget, QEvent::ChildPolished) |
| << qMakePair(&widget, QEvent::PlatformSurface) |
| << qMakePair(&widget, QEvent::WinIdChange) |
| << qMakePair(&widget, QEvent::WindowIconChange) |
| << qMakePair(&widget, QEvent::Move) |
| << qMakePair(&widget, QEvent::Resize) |
| << qMakePair(&widget, QEvent::Show) |
| << qMakePair(&widget, QEvent::CursorChange) |
| << qMakePair(&widget, QEvent::ShowToParent); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| spy.clear(); |
| |
| QCoreApplication::sendPostedEvents(); |
| expected = |
| EventRecorder::EventList() |
| << qMakePair(&widget, QEvent::PolishRequest) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) |
| << qMakePair(&widget, QEvent::Type(QEvent::User + 2)) |
| << qMakePair(&widget, QEvent::UpdateLater) |
| << qMakePair(&widget, QEvent::UpdateRequest); |
| |
| QVERIFY2(spy.eventList() == expected, |
| EventRecorder::msgEventListMismatch(expected, spy.eventList()).constData()); |
| } |
| } |
| |
| class RenderWidget : public QWidget |
| { |
| public: |
| RenderWidget(QWidget *source) |
| : source(source), ellipse(false) {} |
| |
| void setEllipseEnabled(bool enable = true) |
| { |
| ellipse = enable; |
| update(); |
| } |
| |
| protected: |
| void paintEvent(QPaintEvent *) |
| { |
| if (ellipse) { |
| QPainter painter(this); |
| painter.fillRect(rect(), Qt::red); |
| painter.end(); |
| QRegion regionToRender = QRegion(0, 0, source->width(), source->height() / 2, |
| QRegion::Ellipse); |
| source->render(this, QPoint(0, 30), regionToRender); |
| } else { |
| source->render(this); |
| } |
| } |
| |
| private: |
| QWidget *source; |
| bool ellipse; |
| }; |
| |
| void tst_QWidget::render() |
| { |
| return; |
| QCalendarWidget source; |
| source.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| // disable anti-aliasing to eliminate potential differences when subpixel antialiasing |
| // is enabled on the screen |
| QFont f; |
| f.setStyleStrategy(QFont::NoAntialias); |
| source.setFont(f); |
| source.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&source)); |
| |
| // Render the entire source into target. |
| RenderWidget target(&source); |
| target.resize(source.size()); |
| target.show(); |
| |
| QCoreApplication::processEvents(); |
| QCoreApplication::sendPostedEvents(); |
| QTest::qWait(250); |
| |
| const QImage sourceImage = source.grab(QRect(QPoint(0, 0), QSize(-1, -1))).toImage(); |
| QCoreApplication::processEvents(); |
| QImage targetImage = target.grab(QRect(QPoint(0, 0), QSize(-1, -1))).toImage(); |
| QCoreApplication::processEvents(); |
| QCOMPARE(sourceImage, targetImage); |
| |
| // Fill target.rect() will Qt::red and render |
| // QRegion(0, 0, source->width(), source->height() / 2, QRegion::Ellipse) |
| // of source into target with offset (0, 30). |
| target.setEllipseEnabled(); |
| QCoreApplication::processEvents(); |
| QCoreApplication::sendPostedEvents(); |
| |
| targetImage = target.grab(QRect(QPoint(0, 0), QSize(-1, -1))).toImage(); |
| QVERIFY(sourceImage != targetImage); |
| |
| QCOMPARE(targetImage.pixel(target.width() / 2, 29), QColor(Qt::red).rgb()); |
| QCOMPARE(targetImage.pixel(target.width() / 2, 30), sourceImage.pixel(source.width() / 2, 0)); |
| |
| // Test that a child widget properly fills its background |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(100, 100); |
| // prevent custom styles |
| window.setStyle(QStyleFactory::create(QLatin1String("Windows"))); |
| window.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QWidget child(&window); |
| child.resize(window.size()); |
| child.show(); |
| |
| QCoreApplication::processEvents(); |
| const QPixmap childPixmap = child.grab(QRect(QPoint(0, 0), QSize(-1, -1))); |
| const QPixmap windowPixmap = window.grab(QRect(QPoint(0, 0), QSize(-1, -1))); |
| QCOMPARE(childPixmap, windowPixmap); |
| } |
| |
| { // Check that the target offset is correct. |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(200, 200); |
| widget.setAutoFillBackground(true); |
| widget.setPalette(Qt::red); |
| // prevent custom styles |
| widget.setStyle(QStyleFactory::create(QLatin1String("Windows"))); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QImage image(widget.size(), QImage::Format_RGB32); |
| image.fill(QColor(Qt::blue).rgb()); |
| |
| // Target offset (0, 0) |
| widget.render(&image, QPoint(), QRect(20, 20, 100, 100)); |
| QCOMPARE(image.pixel(0, 0), QColor(Qt::red).rgb()); |
| QCOMPARE(image.pixel(99, 99), QColor(Qt::red).rgb()); |
| QCOMPARE(image.pixel(100, 100), QColor(Qt::blue).rgb()); |
| |
| // Target offset (20, 20). |
| image.fill(QColor(Qt::blue).rgb()); |
| widget.render(&image, QPoint(20, 20), QRect(20, 20, 100, 100)); |
| QCOMPARE(image.pixel(0, 0), QColor(Qt::blue).rgb()); |
| QCOMPARE(image.pixel(19, 19), QColor(Qt::blue).rgb()); |
| QCOMPARE(image.pixel(20, 20), QColor(Qt::red).rgb()); |
| QCOMPARE(image.pixel(119, 119), QColor(Qt::red).rgb()); |
| QCOMPARE(image.pixel(120, 120), QColor(Qt::blue).rgb()); |
| } |
| } |
| |
| // On Windows the active palette is used instead of the inactive palette even |
| // though the widget is invisible. This is probably related to task 178507/168682, |
| // but for the renderInvisible test it doesn't matter, we're mostly interested |
| // in testing the geometry so just workaround the palette issue for now. |
| static void workaroundPaletteIssue(QWidget *widget) |
| { |
| #ifndef Q_OS_WIN |
| return; |
| #endif |
| if (!widget) |
| return; |
| |
| QWidget *navigationBar = widget->findChild<QWidget *>(QLatin1String("qt_calendar_navigationbar")); |
| QVERIFY(navigationBar); |
| |
| QPalette palette = navigationBar->palette(); |
| const QColor background = palette.color(QPalette::Inactive, navigationBar->backgroundRole()); |
| const QColor highlightedText = palette.color(QPalette::Inactive, QPalette::HighlightedText); |
| palette.setColor(QPalette::Active, navigationBar->backgroundRole(), background); |
| palette.setColor(QPalette::Active, QPalette::HighlightedText, highlightedText); |
| navigationBar->setPalette(palette); |
| } |
| |
| //#define RENDER_DEBUG |
| void tst_QWidget::renderInvisible() |
| { |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| if (m_platform == QStringLiteral("winrt")) |
| QSKIP("WinRT: This fails. QTBUG-68297."); |
| |
| QScopedPointer<QCalendarWidget> calendar(new QCalendarWidget); |
| calendar->move(m_availableTopLeft + QPoint(100, 100)); |
| calendar->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| // disable anti-aliasing to eliminate potential differences when subpixel antialiasing |
| // is enabled on the screen |
| QFont f; |
| f.setStyleStrategy(QFont::NoAntialias); |
| calendar->setFont(f); |
| calendar->showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(calendar.data())); |
| |
| // Create a dummy focus widget to get rid of focus rect in reference image. |
| QLineEdit dummyFocusWidget; |
| dummyFocusWidget.setMinimumWidth(m_testWidgetSize.width()); |
| dummyFocusWidget.move(calendar->geometry().bottomLeft() + QPoint(0, 100)); |
| dummyFocusWidget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&dummyFocusWidget)); |
| QCoreApplication::processEvents(); |
| QTest::qWait(120); |
| |
| // Create normal reference image. |
| const QSize calendarSize = calendar->size(); |
| QImage referenceImage(calendarSize, QImage::Format_ARGB32); |
| calendar->render(&referenceImage); |
| #ifdef RENDER_DEBUG |
| referenceImage.save("referenceImage.png"); |
| #endif |
| QVERIFY(!referenceImage.isNull()); |
| |
| // Create resized reference image. |
| const QSize calendarSizeResized = calendar->size() + QSize(50, 50); |
| calendar->resize(calendarSizeResized); |
| QCoreApplication::processEvents(); |
| QTest::qWait(30); |
| QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32); |
| calendar->render(&referenceImageResized); |
| #ifdef RENDER_DEBUG |
| referenceImageResized.save("referenceImageResized.png"); |
| #endif |
| QVERIFY(!referenceImageResized.isNull()); |
| |
| // Explicitly hide the calendar. |
| calendar->hide(); |
| QCoreApplication::processEvents(); |
| QTest::qWait(30); |
| workaroundPaletteIssue(calendar.data()); |
| |
| { // Make sure we get the same image when the calendar is explicitly hidden. |
| QImage testImage(calendarSizeResized, QImage::Format_ARGB32); |
| calendar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("explicitlyHiddenCalendarResized.png"); |
| #endif |
| QCOMPARE(testImage, referenceImageResized); |
| } |
| |
| // Now that we have reference images we can delete the source and re-create |
| // the calendar and check that we get the same images from a calendar which has never |
| // been visible, laid out or created (Qt::WA_WState_Created). |
| calendar.reset(new QCalendarWidget); |
| calendar->setFont(f); |
| workaroundPaletteIssue(calendar.data()); |
| |
| { // Never been visible, created or laid out. |
| QImage testImage(calendarSize, QImage::Format_ARGB32); |
| calendar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("neverBeenVisibleCreatedOrLaidOut.png"); |
| #endif |
| QCOMPARE(testImage, referenceImage); |
| } |
| |
| calendar->hide(); |
| QCoreApplication::processEvents(); |
| QTest::qWait(30); |
| |
| { // Calendar explicitly hidden. |
| QImage testImage(calendarSize, QImage::Format_ARGB32); |
| calendar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("explicitlyHiddenCalendar.png"); |
| #endif |
| QCOMPARE(testImage, referenceImage); |
| } |
| |
| // Get navigation bar and explicitly hide it. |
| QWidget *navigationBar = calendar.data()->findChild<QWidget *>(QLatin1String("qt_calendar_navigationbar")); |
| QVERIFY(navigationBar); |
| navigationBar->hide(); |
| |
| { // Check that the navigation bar isn't drawn when rendering the entire calendar. |
| QImage testImage(calendarSize, QImage::Format_ARGB32); |
| calendar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("calendarWithoutNavigationBar.png"); |
| #endif |
| QVERIFY(testImage != referenceImage); |
| } |
| |
| { // Make sure the navigation bar renders correctly even though it's hidden. |
| QImage testImage(navigationBar->size(), QImage::Format_ARGB32); |
| navigationBar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("explicitlyHiddenNavigationBar.png"); |
| #endif |
| QCOMPARE(testImage, referenceImage.copy(navigationBar->rect())); |
| } |
| |
| // Get next month button. |
| QWidget *nextMonthButton = navigationBar->findChild<QWidget *>(QLatin1String("qt_calendar_nextmonth")); |
| QVERIFY(nextMonthButton); |
| |
| { // Render next month button. |
| // Fill test image with correct background color. |
| QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32); |
| navigationBar->render(&testImage, QPoint(), QRegion(), QWidget::RenderFlags()); |
| #ifdef RENDER_DEBUG |
| testImage.save("nextMonthButtonBackground.png"); |
| #endif |
| |
| // Set the button's background color to Qt::transparent; otherwise it will fill the |
| // background with QPalette::Window. |
| const QPalette originalPalette = nextMonthButton->palette(); |
| QPalette palette = originalPalette; |
| palette.setColor(QPalette::Window, Qt::transparent); |
| nextMonthButton->setPalette(palette); |
| |
| // Render the button on top of the background. |
| nextMonthButton->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("nextMonthButton.png"); |
| #endif |
| const QRect buttonRect(nextMonthButton->mapTo(calendar.data(), QPoint()), nextMonthButton->size()); |
| QCOMPARE(testImage, referenceImage.copy(buttonRect)); |
| |
| // Restore palette. |
| nextMonthButton->setPalette(originalPalette); |
| } |
| |
| // Navigation bar isn't explicitly hidden anymore. |
| navigationBar->show(); |
| QCoreApplication::processEvents(); |
| QTest::qWait(30); |
| QVERIFY(!calendar->isVisible()); |
| |
| // Now, completely mess up the layout. This will trigger an update on the layout |
| // when the calendar is visible or shown, but it's not. QWidget::render must therefore |
| // make sure the layout is activated before rendering. |
| QVERIFY(!calendar->isVisible()); |
| calendar->resize(calendarSizeResized); |
| QCoreApplication::processEvents(); |
| |
| { // Make sure we get an image equal to the resized reference image. |
| QImage testImage(calendarSizeResized, QImage::Format_ARGB32); |
| calendar->render(&testImage); |
| #ifdef RENDER_DEBUG |
| testImage.save("calendarResized.png"); |
| #endif |
| QCOMPARE(testImage, referenceImageResized); |
| } |
| |
| { // Make sure we lay out the widget correctly the first time it's rendered. |
| QCalendarWidget calendar; |
| const QSize calendarSize = calendar.sizeHint(); |
| |
| QImage image(2 * calendarSize, QImage::Format_ARGB32); |
| image.fill(QColor(Qt::red).rgb()); |
| calendar.render(&image); |
| |
| for (int i = calendarSize.height(); i < 2 * calendarSize.height(); ++i) |
| for (int j = calendarSize.width(); j < 2 * calendarSize.width(); ++j) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| |
| { // Ensure that we don't call adjustSize() on invisible top-levels if render() is called |
| // right after widgets have been added/removed to/from its layout. |
| QWidget topLevel; |
| topLevel.setLayout(new QVBoxLayout); |
| |
| QWidget *widget = new QLineEdit; |
| topLevel.layout()->addWidget(widget); |
| |
| const QSize initialSize = topLevel.size(); |
| QPixmap pixmap(topLevel.sizeHint()); |
| topLevel.render(&pixmap); // triggers adjustSize() |
| const QSize finalSize = topLevel.size(); |
| QVERIFY2(finalSize != initialSize, |
| msgComparisonFailed(finalSize, "!=", initialSize)); |
| |
| topLevel.layout()->removeWidget(widget); |
| QCOMPARE(topLevel.size(), finalSize); |
| topLevel.render(&pixmap); |
| QCOMPARE(topLevel.size(), finalSize); |
| |
| topLevel.layout()->addWidget(widget); |
| QCOMPARE(topLevel.size(), finalSize); |
| topLevel.render(&pixmap); |
| QCOMPARE(topLevel.size(), finalSize); |
| } |
| } |
| |
| void tst_QWidget::renderWithPainter() |
| { |
| QWidget widget(nullptr, Qt::Tool); |
| // prevent custom styles |
| |
| const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows"))); |
| widget.setStyle(style.data()); |
| widget.show(); |
| widget.resize(70, 50); |
| widget.setAutoFillBackground(true); |
| widget.setPalette(Qt::black); |
| |
| // Render the entire widget onto the image. |
| QImage image(QSize(70, 50), QImage::Format_ARGB32); |
| image.fill(QColor(Qt::red).rgb()); |
| QPainter painter(&image); |
| widget.render(&painter); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb()); |
| } |
| |
| // Translate painter (10, 10). |
| painter.save(); |
| image.fill(QColor(Qt::red).rgb()); |
| painter.translate(10, 10); |
| widget.render(&painter); |
| painter.restore(); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (i < 10 || j < 10) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb()); |
| } |
| } |
| |
| // Pass target offset (10, 10) (the same as QPainter::translate). |
| image.fill(QColor(Qt::red).rgb()); |
| widget.render(&painter, QPoint(10, 10)); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (i < 10 || j < 10) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb()); |
| } |
| } |
| |
| // Translate (10, 10) and pass target offset (10, 10). |
| painter.save(); |
| image.fill(QColor(Qt::red).rgb()); |
| painter.translate(10, 10); |
| widget.render(&painter, QPoint(10, 10)); |
| painter.restore(); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (i < 20 || j < 20) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb()); |
| } |
| } |
| |
| // Rotate painter 90 degrees. |
| painter.save(); |
| image.fill(QColor(Qt::red).rgb()); |
| painter.rotate(90); |
| widget.render(&painter); |
| painter.restore(); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| |
| // Translate and rotate. |
| image.fill(QColor(Qt::red).rgb()); |
| widget.resize(40, 10); |
| painter.translate(10, 10); |
| painter.rotate(90); |
| widget.render(&painter); |
| |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (i >= 10 && j >= 0 && j < 10) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::black).rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| } |
| |
| // Make sure QWidget::render does not modify the render hints set on the painter. |
| painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
| | QPainter::TextAntialiasing); |
| QPainter::RenderHints oldRenderHints = painter.renderHints(); |
| widget.render(&painter); |
| QCOMPARE(painter.renderHints(), oldRenderHints); |
| } |
| |
| void tst_QWidget::render_task188133() |
| { |
| QMainWindow mainWindow; |
| |
| // Make sure QWidget::render does not trigger QWidget::repaint/update |
| // and asserts for Qt::WA_WState_Created. |
| const QPixmap pixmap = mainWindow.grab(QRect(QPoint(0, 0), QSize(-1, -1))); |
| Q_UNUSED(pixmap) |
| } |
| |
| void tst_QWidget::render_task211796() |
| { |
| class MyWidget : public QWidget |
| { |
| void resizeEvent(QResizeEvent *) override |
| { |
| QPixmap pixmap(size()); |
| render(&pixmap); |
| } |
| }; |
| |
| { // Please don't die in a resize recursion. |
| MyWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(m_testWidgetSize); |
| centerOnScreen(&widget); |
| widget.show(); |
| } |
| |
| { // Same check with a deeper hierarchy. |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(m_testWidgetSize); |
| centerOnScreen(&widget); |
| widget.show(); |
| QWidget child(&widget); |
| MyWidget grandChild; |
| grandChild.setParent(&child); |
| grandChild.resize(100, 100); |
| child.show(); |
| } |
| } |
| |
| void tst_QWidget::render_task217815() |
| { |
| // Make sure we don't change the size of the widget when calling |
| // render() and the widget has an explicit size set. |
| // This was a problem on Windows because we called createWinId(), |
| // which in turn enforced the size to be bigger than the smallest |
| // possible native window size (which is (115,something) on WinXP). |
| QWidget widget; |
| const QSize explicitSize(80, 20); |
| widget.resize(explicitSize); |
| QCOMPARE(widget.size(), explicitSize); |
| |
| QPixmap pixmap(explicitSize); |
| widget.render(&pixmap); |
| |
| QCOMPARE(widget.size(), explicitSize); |
| } |
| |
| // Window Opacity is not supported on Windows CE. |
| void tst_QWidget::render_windowOpacity() |
| { |
| if (m_platform == QStringLiteral("offscreen")) |
| QSKIP("Platform offscreen does not support setting opacity"); |
| |
| const qreal opacity = 0.5; |
| |
| { // Check that the painter opacity effects the widget drawing. |
| QWidget topLevel; |
| QWidget child(&topLevel); |
| child.resize(50, 50); |
| child.setPalette(Qt::red); |
| child.setAutoFillBackground(true); |
| |
| QPixmap expected(child.size()); |
| |
| if (m_platform == QStringLiteral("xcb") && expected.depth() < 24) |
| QSKIP("This test won't give correct results with dithered pixmaps"); |
| |
| expected.fill(Qt::green); |
| QPainter painter(&expected); |
| painter.setOpacity(opacity); |
| painter.fillRect(QRect(QPoint(0, 0), child.size()), Qt::red); |
| painter.end(); |
| |
| QPixmap result(child.size()); |
| result.fill(Qt::green); |
| painter.begin(&result); |
| painter.setOpacity(opacity); |
| child.render(&painter); |
| painter.end(); |
| QCOMPARE(result, expected); |
| } |
| |
| { // Combine the opacity set on the painter with the widget opacity. |
| class MyWidget : public QWidget |
| { |
| public: |
| explicit MyWidget(qreal opacityIn) : opacity(opacityIn) {} |
| void paintEvent(QPaintEvent *) override |
| { |
| QPainter painter(this); |
| painter.setOpacity(opacity); |
| QCOMPARE(painter.opacity(), opacity); |
| painter.fillRect(rect(), Qt::red); |
| } |
| const qreal opacity; |
| }; |
| |
| MyWidget widget(opacity); |
| widget.resize(50, 50); |
| widget.setPalette(Qt::blue); |
| widget.setAutoFillBackground(true); |
| |
| QPixmap expected(widget.size()); |
| expected.fill(Qt::green); |
| QPainter painter(&expected); |
| painter.setOpacity(opacity); |
| QPixmap pixmap(widget.size()); |
| pixmap.fill(Qt::blue); |
| QPainter pixmapPainter(&pixmap); |
| pixmapPainter.setOpacity(opacity); |
| pixmapPainter.fillRect(QRect(QPoint(), widget.size()), Qt::red); |
| painter.drawPixmap(QPoint(), pixmap); |
| painter.end(); |
| |
| QPixmap result(widget.size()); |
| result.fill(Qt::green); |
| painter.begin(&result); |
| painter.setOpacity(opacity); |
| widget.render(&painter); |
| painter.end(); |
| QCOMPARE(result, expected); |
| } |
| } |
| |
| void tst_QWidget::render_systemClip() |
| { |
| QWidget widget; |
| widget.setPalette(Qt::blue); |
| widget.resize(100, 100); |
| |
| QImage image(widget.size(), QImage::Format_RGB32); |
| image.fill(QColor(Qt::red).rgb()); |
| |
| QPaintEngine *paintEngine = image.paintEngine(); |
| QVERIFY(paintEngine); |
| paintEngine->setSystemClip(QRegion(0, 0, 50, 50)); |
| |
| QPainter painter(&image); |
| // Make sure we're using the same paint engine and has the right clip set. |
| QCOMPARE(painter.paintEngine(), paintEngine); |
| QCOMPARE(paintEngine->systemClip(), QRegion(0, 0, 50, 50)); |
| |
| // Translate painter outside system clip. |
| painter.translate(50, 0); |
| widget.render(&painter); |
| |
| #ifdef RENDER_DEBUG |
| image.save("outside_systemclip.png"); |
| #endif |
| |
| // All pixels should be red. |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| |
| // Restore painter and refill image with red. |
| image.fill(QColor(Qt::red).rgb()); |
| painter.translate(-50, 0); |
| |
| // Set transform on the painter. |
| QTransform transform; |
| transform.shear(0, 1); |
| painter.setTransform(transform); |
| widget.render(&painter); |
| |
| #ifdef RENDER_DEBUG |
| image.save("blue_triangle.png"); |
| #endif |
| |
| // We should now have a blue triangle starting at scan line 1, and the rest should be red. |
| // rrrrrrrrrr |
| // brrrrrrrrr |
| // bbrrrrrrrr |
| // bbbrrrrrrr |
| // bbbbrrrrrr |
| // rrrrrrrrrr |
| // ... |
| |
| #ifndef Q_OS_OSX |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (i < 50 && j < i) |
| QCOMPARE(image.pixel(j, i), QColor(Qt::blue).rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| } |
| #else |
| // We don't paint directly on the image on the Mac, so we cannot do the pixel comparison |
| // as above due to QPainter::SmoothPixmapTransform. We therefore need to generate an |
| // expected image by first painting on a pixmap, and then draw the pixmap onto |
| // the image using QPainter::SmoothPixmapTransform. Then we can compare pixels :) |
| // The check is basically the same, except that it takes the smoothening into account. |
| QPixmap pixmap(50, 50); |
| const QRegion sysClip(0, 0, 50, 50); |
| widget.render(&pixmap, QPoint(), sysClip); |
| |
| QImage expectedImage(widget.size(), QImage::Format_RGB32); |
| expectedImage.fill(QColor(Qt::red).rgb()); |
| expectedImage.paintEngine()->setSystemClip(sysClip); |
| |
| QPainter expectedImagePainter(&expectedImage); |
| expectedImagePainter.setTransform(QTransform().shear(0, 1)); |
| // NB! This is the important part (SmoothPixmapTransform). |
| expectedImagePainter.setRenderHints(QPainter::SmoothPixmapTransform); |
| expectedImagePainter.drawPixmap(QPoint(0, 0), pixmap); |
| expectedImagePainter.end(); |
| |
| QCOMPARE(image, expectedImage); |
| #endif |
| } |
| |
| void tst_QWidget::render_systemClip2_data() |
| { |
| QTest::addColumn<bool>("autoFillBackground"); |
| QTest::addColumn<bool>("usePaintEvent"); |
| QTest::addColumn<QColor>("expectedColor"); |
| |
| QTest::newRow("Only auto-fill background") << true << false << QColor(Qt::blue); |
| QTest::newRow("Only draw in paintEvent") << false << true << QColor(Qt::green); |
| QTest::newRow("Auto-fill background and draw in paintEvent") << true << true << QColor(Qt::green); |
| } |
| |
| void tst_QWidget::render_systemClip2() |
| { |
| QFETCH(bool, autoFillBackground); |
| QFETCH(bool, usePaintEvent); |
| QFETCH(QColor, expectedColor); |
| |
| QVERIFY2(expectedColor != QColor(Qt::red), "Qt::red is the reference color for the image, pick another color"); |
| |
| class MyWidget : public QWidget |
| { |
| public: |
| explicit MyWidget(bool usePaintEventIn) : usePaintEvent(usePaintEventIn) {} |
| const bool usePaintEvent; |
| void paintEvent(QPaintEvent *) override |
| { |
| if (usePaintEvent) |
| QPainter(this).fillRect(rect(), Qt::green); |
| } |
| }; |
| |
| MyWidget widget(usePaintEvent); |
| widget.setPalette(Qt::blue); |
| // NB! widget.setAutoFillBackground(autoFillBackground) won't do the |
| // trick here since the widget is a top-level. The background is filled |
| // regardless, unless Qt::WA_OpaquePaintEvent or Qt::WA_NoSystemBackground |
| // is set. We therefore use the opaque attribute to turn off auto-fill. |
| if (!autoFillBackground) |
| widget.setAttribute(Qt::WA_OpaquePaintEvent); |
| widget.resize(100, 100); |
| |
| QImage image(widget.size(), QImage::Format_RGB32); |
| image.fill(QColor(Qt::red).rgb()); |
| |
| QPaintEngine *paintEngine = image.paintEngine(); |
| QVERIFY(paintEngine); |
| |
| QRegion systemClip(QRegion(50, 0, 50, 10)); |
| systemClip += QRegion(90, 10, 10, 40); |
| paintEngine->setSystemClip(systemClip); |
| |
| // Render entire widget directly onto device. |
| widget.render(&image); |
| |
| #ifdef RENDER_DEBUG |
| image.save("systemclip_with_device.png"); |
| #endif |
| // All pixels within the system clip should now be |
| // the expectedColor, and the rest should be red. |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (systemClip.contains(QPoint(j, i))) |
| QCOMPARE(image.pixel(j, i), expectedColor.rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| } |
| |
| // Refill image with red. |
| image.fill(QColor(Qt::red).rgb()); |
| paintEngine->setSystemClip(systemClip); |
| |
| // Do the same with an untransformed painter. |
| QPainter painter(&image); |
| //Make sure we're using the same paint engine and has the right clip set. |
| QCOMPARE(painter.paintEngine(), paintEngine); |
| QCOMPARE(paintEngine->systemClip(), systemClip); |
| |
| widget.render(&painter); |
| |
| #ifdef RENDER_DEBUG |
| image.save("systemclip_with_untransformed_painter.png"); |
| #endif |
| // All pixels within the system clip should now be |
| // the expectedColor, and the rest should be red. |
| for (int i = 0; i < image.height(); ++i) { |
| for (int j = 0; j < image.width(); ++j) { |
| if (systemClip.contains(QPoint(j, i))) |
| QCOMPARE(image.pixel(j, i), expectedColor.rgb()); |
| else |
| QCOMPARE(image.pixel(j, i), QColor(Qt::red).rgb()); |
| } |
| } |
| } |
| |
| void tst_QWidget::render_systemClip3_data() |
| { |
| QTest::addColumn<QSize>("size"); |
| QTest::addColumn<bool>("useSystemClip"); |
| |
| // Reference: http://en.wikipedia.org/wiki/Flag_of_Norway |
| QTest::newRow("Norwegian Civil Flag") << QSize(220, 160) << false; |
| QTest::newRow("Norwegian War Flag") << QSize(270, 160) << true; |
| } |
| |
| // This test ensures that the current engine clip (systemClip + painter clip) |
| // is preserved after QPainter::setClipRegion(..., Qt::ReplaceClip); |
| void tst_QWidget::render_systemClip3() |
| { |
| QFETCH(QSize, size); |
| QFETCH(bool, useSystemClip); |
| |
| // Calculate the inner/outer cross of the flag. |
| QRegion outerCross(0, 0, size.width(), size.height()); |
| outerCross -= QRect(0, 0, 60, 60); |
| outerCross -= QRect(100, 0, size.width() - 100, 60); |
| outerCross -= QRect(0, 100, 60, 60); |
| outerCross -= QRect(100, 100, size.width() - 100, 60); |
| |
| QRegion innerCross(0, 0, size.width(), size.height()); |
| innerCross -= QRect(0, 0, 70, 70); |
| innerCross -= QRect(90, 0, size.width() - 90, 70); |
| innerCross -= QRect(0, 90, 70, 70); |
| innerCross -= QRect(90, 90, size.width() - 90, 70); |
| |
| const QRegion redArea(QRegion(0, 0, size.width(), size.height()) - outerCross); |
| const QRegion whiteArea(outerCross - innerCross); |
| QRegion systemClip; |
| |
| // Okay, here's the image that should look like a Norwegian civil/war flag in the end. |
| QImage flag(size, QImage::Format_ARGB32); |
| flag.fill(QColor(Qt::transparent).rgba()); |
| |
| if (useSystemClip) { |
| QPainterPath warClip(QPoint(size.width(), 0)); |
| warClip.lineTo(size.width() - 110, 60); |
| warClip.lineTo(size.width(), 80); |
| warClip.lineTo(size.width() - 110, 100); |
| warClip.lineTo(size.width(), 160); |
| warClip.closeSubpath(); |
| systemClip = QRegion(0, 0, size.width(), size.height()) - QRegion(warClip.toFillPolygon().toPolygon()); |
| flag.paintEngine()->setSystemClip(systemClip); |
| } |
| |
| QPainter painter(&flag); |
| painter.fillRect(QRect(QPoint(), size), Qt::red); // Fill image background with red. |
| painter.setClipRegion(outerCross); // Limit widget painting to inside the outer cross. |
| |
| // Here's the widget that's supposed to draw the inner/outer cross of the flag. |
| // The outer cross (white) should be drawn when the background is auto-filled, and |
| // the inner cross (blue) should be drawn in the paintEvent. |
| class MyWidget : public QWidget |
| { |
| public: |
| void paintEvent(QPaintEvent *) override |
| { |
| QPainter painter(this); |
| // Be evil and try to paint outside the outer cross. This should not be |
| // possible since the shared painter is clipped to the outer cross. |
| painter.setClipRect(0, 0, 60, 60, Qt::ReplaceClip); |
| painter.fillRect(rect(), Qt::green); |
| painter.setClipRegion(clip, Qt::ReplaceClip); |
| painter.fillRect(rect(), Qt::blue); |
| } |
| QRegion clip; |
| }; |
| |
| MyWidget widget; |
| widget.clip = innerCross; |
| widget.setFixedSize(size); |
| widget.setPalette(Qt::white); |
| widget.setAutoFillBackground(true); |
| widget.render(&painter); |
| |
| #ifdef RENDER_DEBUG |
| flag.save("flag.png"); |
| #endif |
| |
| // Let's make sure we got a Norwegian flag. |
| for (int i = 0; i < flag.height(); ++i) { |
| for (int j = 0; j < flag.width(); ++j) { |
| const QPoint pixel(j, i); |
| const QRgb pixelValue = flag.pixel(pixel); |
| if (useSystemClip && !systemClip.contains(pixel)) |
| QCOMPARE(pixelValue, QColor(Qt::transparent).rgba()); |
| else if (redArea.contains(pixel)) |
| QCOMPARE(pixelValue, QColor(Qt::red).rgba()); |
| else if (whiteArea.contains(pixel)) |
| QCOMPARE(pixelValue, QColor(Qt::white).rgba()); |
| else |
| QCOMPARE(pixelValue, QColor(Qt::blue).rgba()); |
| } |
| } |
| } |
| |
| void tst_QWidget::render_task252837() |
| { |
| QWidget widget; |
| widget.resize(200, 200); |
| |
| QPixmap pixmap(widget.size()); |
| QPainter painter(&pixmap); |
| // Please do not crash. |
| widget.render(&painter); |
| } |
| |
| void tst_QWidget::render_worldTransform() |
| { |
| class MyWidget : public QWidget |
| { |
| public: |
| void paintEvent(QPaintEvent *) override |
| { |
| QPainter painter(this); |
| // Make sure world transform is identity. |
| QCOMPARE(painter.worldTransform(), QTransform()); |
| |
| // Make sure device transform is correct. |
| const QPoint widgetOffset = geometry().topLeft(); |
| QTransform expectedDeviceTransform = QTransform::fromTranslate(105, 5); |
| expectedDeviceTransform.rotate(90); |
| expectedDeviceTransform.translate(widgetOffset.x(), widgetOffset.y()); |
| QCOMPARE(painter.deviceTransform(), expectedDeviceTransform); |
| |
| // Set new world transform. |
| QTransform newWorldTransform = QTransform::fromTranslate(10, 10); |
| newWorldTransform.rotate(90); |
| painter.setWorldTransform(newWorldTransform); |
| QCOMPARE(painter.worldTransform(), newWorldTransform); |
| |
| // Again, check device transform. |
| expectedDeviceTransform.translate(10, 10); |
| expectedDeviceTransform.rotate(90); |
| QCOMPARE(painter.deviceTransform(), expectedDeviceTransform); |
| |
| painter.fillRect(QRect(0, 0, 20, 10), Qt::green); |
| } |
| }; |
| |
| MyWidget widget; |
| widget.setFixedSize(100, 100); |
| widget.setPalette(Qt::red); |
| widget.setAutoFillBackground(true); |
| |
| MyWidget child; |
| child.setParent(&widget); |
| child.move(50, 50); |
| child.setFixedSize(50, 50); |
| child.setPalette(Qt::blue); |
| child.setAutoFillBackground(true); |
| |
| QImage image(QSize(110, 110), QImage::Format_RGB32); |
| image.fill(QColor(Qt::black).rgb()); |
| |
| QPainter painter(&image); |
| painter.translate(105, 5); |
| painter.rotate(90); |
| |
| // Render widgets onto image. |
| widget.render(&painter); |
| #ifdef RENDER_DEBUG |
| image.save("render_worldTransform_image.png"); |
| #endif |
| |
| // Ensure the transforms are unchanged after render. |
| QCOMPARE(painter.worldTransform(), painter.worldTransform()); |
| QCOMPARE(painter.deviceTransform(), painter.deviceTransform()); |
| painter.end(); |
| |
| // Paint expected image. |
| QImage expected(QSize(110, 110), QImage::Format_RGB32); |
| expected.fill(QColor(Qt::black).rgb()); |
| |
| QPainter expectedPainter(&expected); |
| expectedPainter.translate(105, 5); |
| expectedPainter.rotate(90); |
| expectedPainter.save(); |
| expectedPainter.fillRect(widget.rect(),Qt::red); |
| expectedPainter.translate(10, 10); |
| expectedPainter.rotate(90); |
| expectedPainter.fillRect(QRect(0, 0, 20, 10), Qt::green); |
| expectedPainter.restore(); |
| expectedPainter.translate(50, 50); |
| expectedPainter.fillRect(child.rect(),Qt::blue); |
| expectedPainter.translate(10, 10); |
| expectedPainter.rotate(90); |
| expectedPainter.fillRect(QRect(0, 0, 20, 10), Qt::green); |
| expectedPainter.end(); |
| |
| #ifdef RENDER_DEBUG |
| expected.save("render_worldTransform_expected.png"); |
| #endif |
| |
| QCOMPARE(image, expected); |
| } |
| |
| void tst_QWidget::setContentsMargins() |
| { |
| QLabel label("why does it always rain on me?"); |
| QSize oldSize = label.sizeHint(); |
| label.setFrameStyle(QFrame::Sunken | QFrame::Box); |
| QSize newSize = label.sizeHint(); |
| QVERIFY2(oldSize != newSize, msgComparisonFailed(oldSize, "!=", newSize)); |
| |
| QLabel label2("why does it always rain on me?"); |
| label2.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| label2.show(); |
| label2.setFrameStyle(QFrame::Sunken | QFrame::Box); |
| QCOMPARE(newSize, label2.sizeHint()); |
| |
| QLabel label3("why does it always rain on me?"); |
| label3.setFrameStyle(QFrame::Sunken | QFrame::Box); |
| QCOMPARE(newSize, label3.sizeHint()); |
| } |
| |
| void tst_QWidget::moveWindowInShowEvent_data() |
| { |
| QTest::addColumn<QPoint>("initial"); |
| QTest::addColumn<QPoint>("position"); |
| |
| QPoint p = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); |
| |
| QTest::newRow("1") << p << (p + QPoint(10, 10)); |
| QTest::newRow("2") << (p + QPoint(10,10)) << p; |
| } |
| |
| void tst_QWidget::moveWindowInShowEvent() |
| { |
| if (m_platform == QStringLiteral("xcb")) |
| QSKIP("QTBUG-26424"); |
| |
| QFETCH(QPoint, initial); |
| QFETCH(QPoint, position); |
| |
| class MoveWindowInShowEventWidget : public QWidget |
| { |
| public: |
| QPoint position; |
| void showEvent(QShowEvent *) override |
| { |
| move(position); |
| } |
| }; |
| |
| MoveWindowInShowEventWidget widget; |
| QScreen *screen = QGuiApplication::primaryScreen(); |
| widget.resize(QSize(screen->availableGeometry().size() / 3).expandedTo(QSize(1, 1))); |
| // move to this position in showEvent() |
| widget.position = position; |
| |
| // put the widget in it's starting position |
| widget.move(initial); |
| QCOMPARE(widget.pos(), initial); |
| |
| // show it |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(100); |
| // it should have moved |
| QCOMPARE(widget.pos(), position); |
| } |
| |
| void tst_QWidget::repaintWhenChildDeleted() |
| { |
| #ifdef Q_OS_WIN |
| QTest::qWait(1000); |
| #endif |
| ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red); |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QPoint startPoint = w.screen()->availableGeometry().topLeft(); |
| startPoint.rx() += 50; |
| startPoint.ry() += 50; |
| w.setGeometry(QRect(startPoint, QSize(100, 100))); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QTRY_COMPARE(w.r, QRegion(w.rect())); |
| w.r = QRegion(); |
| |
| { |
| ColorWidget child(&w, Qt::Widget, Qt::blue); |
| child.setGeometry(10, 10, 10, 10); |
| child.show(); |
| QTRY_COMPARE(child.r, QRegion(child.rect())); |
| w.r = QRegion(); |
| } |
| |
| QTRY_COMPARE(w.r, QRegion(10, 10, 10, 10)); |
| } |
| |
| // task 175114 |
| void tst_QWidget::hideOpaqueChildWhileHidden() |
| { |
| ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red); |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QPoint startPoint = w.screen()->availableGeometry().topLeft(); |
| startPoint.rx() += 50; |
| startPoint.ry() += 50; |
| w.setGeometry(QRect(startPoint, QSize(100, 100))); |
| |
| ColorWidget child(&w, Qt::Widget, Qt::blue); |
| child.setGeometry(10, 10, 80, 80); |
| |
| ColorWidget child2(&child, Qt::Widget, Qt::white); |
| child2.setGeometry(10, 10, 60, 60); |
| |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QTRY_COMPARE(child2.r, QRegion(child2.rect())); |
| child.r = QRegion(); |
| child2.r = QRegion(); |
| w.r = QRegion(); |
| |
| child.hide(); |
| child2.hide(); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QTRY_COMPARE(w.r, QRegion(child.geometry())); |
| |
| child.show(); |
| QTRY_COMPARE(child.r, QRegion(child.rect())); |
| QCOMPARE(child2.r, QRegion()); |
| } |
| |
| // This test doesn't make sense without support for showMinimized(). |
| void tst_QWidget::updateWhileMinimized() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| if (m_platform == QStringLiteral("offscreen")) |
| QSKIP("Platform offscreen does not support showMinimized()"); |
| |
| #if defined(Q_OS_QNX) |
| QSKIP("Platform does not support showMinimized()"); |
| #endif |
| UpdateWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| // Filter out activation change and focus events to avoid update() calls in QWidget. |
| widget.updateOnActivationChangeAndFocusIn = false; |
| widget.reset(); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTRY_VERIFY(widget.numPaintEvents > 0); |
| QTest::qWait(150); |
| |
| // Minimize window. |
| widget.showMinimized(); |
| QTest::qWait(110); |
| |
| widget.reset(); |
| |
| // The widget is not visible on the screen (but isVisible() still returns true). |
| // Make sure update requests are discarded until the widget is shown again. |
| widget.update(0, 0, 50, 50); |
| QTest::qWait(10); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QCOMPARE(widget.numPaintEvents, 0); |
| |
| // Restore window. |
| widget.showNormal(); |
| QTRY_COMPARE(widget.numPaintEvents, 1); |
| QCOMPARE(widget.paintedRegion, QRegion(0, 0, 50, 50)); |
| } |
| |
| class PaintOnScreenWidget: public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| #if defined(Q_OS_WIN) |
| // This is the only way to enable PaintOnScreen on Windows. |
| QPaintEngine *paintEngine() const override { return nullptr; } |
| #endif |
| }; |
| |
| void tst_QWidget::alienWidgets() |
| { |
| if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows")) |
| QSKIP("This test is only for X11/Windows."); |
| |
| qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.resize(m_testWidgetSize); |
| QWidget child(&parent); |
| QWidget grandChild(&child); |
| QWidget greatGrandChild(&grandChild); |
| parent.show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| |
| // Verify that the WA_WState_Created attribute is set |
| // and the top-level is the only native window. |
| QVERIFY(parent.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(parent.internalWinId()); |
| |
| QVERIFY(child.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(!child.internalWinId()); |
| |
| QVERIFY(grandChild.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(!grandChild.internalWinId()); |
| |
| QVERIFY(greatGrandChild.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(!greatGrandChild.internalWinId()); |
| |
| // Enforce native windows all the way up in the parent hierarchy |
| // if not WA_DontCreateNativeAncestors is set. |
| grandChild.setAttribute(Qt::WA_DontCreateNativeAncestors); |
| greatGrandChild.setAttribute(Qt::WA_NativeWindow); |
| QVERIFY(greatGrandChild.internalWinId()); |
| QVERIFY(grandChild.internalWinId()); |
| QVERIFY(!child.internalWinId()); |
| |
| { |
| // Ensure that hide() on an ancestor of a widget with |
| // Qt::WA_DontCreateNativeAncestors still gets unmapped |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(m_testWidgetSize); |
| QWidget widget(&window); |
| QWidget child(&widget); |
| child.setAttribute(Qt::WA_NativeWindow); |
| child.setAttribute(Qt::WA_DontCreateNativeAncestors); |
| window.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(child.testAttribute(Qt::WA_Mapped)); |
| widget.hide(); |
| QTRY_VERIFY(!child.testAttribute(Qt::WA_Mapped)); |
| } |
| |
| // Enforce a native window when calling QWidget::winId. |
| QVERIFY(child.winId()); |
| QVERIFY(child.internalWinId()); |
| |
| // Check that paint on screen widgets (incl. children) are native. |
| PaintOnScreenWidget paintOnScreen(&parent); |
| QWidget paintOnScreenChild(&paintOnScreen); |
| paintOnScreen.show(); |
| QVERIFY(paintOnScreen.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(!paintOnScreen.testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(!paintOnScreen.internalWinId()); |
| QVERIFY(!paintOnScreenChild.testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(!paintOnScreenChild.internalWinId()); |
| |
| paintOnScreen.setAttribute(Qt::WA_PaintOnScreen); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreen.testAttribute(Qt::WA_NativeWindow)); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreen.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreenChild.testAttribute(Qt::WA_NativeWindow)); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreenChild.internalWinId()); |
| |
| // Check that widgets with the Qt::MSWindowsOwnDC attribute set |
| // are native. |
| QWidget msWindowsOwnDC(&parent, Qt::MSWindowsOwnDC); |
| msWindowsOwnDC.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| msWindowsOwnDC.show(); |
| QVERIFY(msWindowsOwnDC.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(msWindowsOwnDC.testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(msWindowsOwnDC.internalWinId()); |
| |
| { // Enforce a native window when calling QWidget::handle() (on X11) or QWidget::getDC() (on Windows). |
| QWidget widget(&parent); |
| widget.show(); |
| QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); |
| QVERIFY(!widget.internalWinId()); |
| |
| widget.winId(); |
| QVERIFY(widget.internalWinId()); |
| } |
| |
| if (m_platform == QStringLiteral("xcb")) { |
| // Make sure we don't create native windows when setting Qt::WA_X11NetWmWindowType attributes |
| // on alien widgets (see task 194231). |
| QWidget dummy; |
| dummy.resize(m_testWidgetSize); |
| QVERIFY(dummy.winId()); |
| QWidget widget(&dummy); |
| widget.setAttribute(Qt::WA_X11NetWmWindowTypeToolBar); |
| QVERIFY(!widget.internalWinId()); |
| } |
| |
| { // Make sure we create native ancestors when setting Qt::WA_PaintOnScreen before show(). |
| QWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| topLevel.resize(m_testWidgetSize); |
| QWidget child(&topLevel); |
| QWidget grandChild(&child); |
| PaintOnScreenWidget greatGrandChild(&grandChild); |
| |
| greatGrandChild.setAttribute(Qt::WA_PaintOnScreen); |
| QVERIFY(!child.internalWinId()); |
| QVERIFY(!grandChild.internalWinId()); |
| QVERIFY(!greatGrandChild.internalWinId()); |
| |
| topLevel.show(); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(child.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(grandChild.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(greatGrandChild.internalWinId()); |
| } |
| |
| { // Ensure that widgets reparented into Qt::WA_PaintOnScreen widgets become native. |
| QWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| topLevel.resize(m_testWidgetSize); |
| QWidget *widget = new PaintOnScreenWidget(&topLevel); |
| widget->setAttribute(Qt::WA_PaintOnScreen); |
| QWidget *child = new QWidget; |
| QWidget *dummy = new QWidget(child); |
| QWidget *grandChild = new QWidget(child); |
| QWidget *dummy2 = new QWidget(grandChild); |
| |
| child->setParent(widget); |
| |
| QVERIFY(!topLevel.internalWinId()); |
| QVERIFY(!child->internalWinId()); |
| QVERIFY(!dummy->internalWinId()); |
| QVERIFY(!grandChild->internalWinId()); |
| QVERIFY(!dummy2->internalWinId()); |
| |
| topLevel.show(); |
| QVERIFY(topLevel.internalWinId()); |
| QVERIFY(widget->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(child->internalWinId()); |
| QVERIFY(child->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(!child->testAttribute(Qt::WA_PaintOnScreen)); |
| QVERIFY(!dummy->internalWinId()); |
| QVERIFY(!dummy->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(!grandChild->internalWinId()); |
| QVERIFY(!grandChild->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(!dummy2->internalWinId()); |
| QVERIFY(!dummy2->testAttribute(Qt::WA_NativeWindow)); |
| } |
| |
| { // Ensure that ancestors of a Qt::WA_PaintOnScreen widget stay native |
| // if they are re-created (typically in QWidgetPrivate::setParent_sys) (task 210822). |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.resize(m_testWidgetSize); |
| QWidget child(&window); |
| |
| QWidget grandChild; |
| grandChild.setWindowTitle("This causes the widget to be created"); |
| |
| PaintOnScreenWidget paintOnScreenWidget; |
| paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen); |
| paintOnScreenWidget.setParent(&grandChild); |
| |
| grandChild.setParent(&child); |
| |
| window.show(); |
| |
| QVERIFY(window.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(child.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(child.testAttribute(Qt::WA_NativeWindow)); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(grandChild.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(grandChild.testAttribute(Qt::WA_NativeWindow)); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreenWidget.internalWinId()); |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QEXPECT_FAIL("", "QTBUG-26424", Continue); |
| QVERIFY(paintOnScreenWidget.testAttribute(Qt::WA_NativeWindow)); |
| } |
| |
| { // Ensure that all siblings are native unless Qt::AA_DontCreateNativeWidgetSiblings is set. |
| qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, false); |
| QWidget mainWindow; |
| mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget *toolBar = new QWidget(&mainWindow); |
| QWidget *dockWidget = new QWidget(&mainWindow); |
| QWidget *centralWidget = new QWidget(&mainWindow); |
| centralWidget->setMinimumSize(m_testWidgetSize); |
| |
| QWidget *button = new QWidget(centralWidget); |
| QWidget *mdiArea = new QWidget(centralWidget); |
| |
| QWidget *horizontalScroll = new QWidget(mdiArea); |
| QWidget *verticalScroll = new QWidget(mdiArea); |
| QWidget *viewport = new QWidget(mdiArea); |
| |
| viewport->setAttribute(Qt::WA_NativeWindow); |
| mainWindow.show(); |
| |
| // Ensure that the viewport and its siblings are native: |
| QVERIFY(verticalScroll->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(verticalScroll->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(horizontalScroll->testAttribute(Qt::WA_NativeWindow)); |
| |
| // Ensure that the mdi area and its siblings are native: |
| QVERIFY(mdiArea->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(button->testAttribute(Qt::WA_NativeWindow)); |
| |
| // Ensure that the central widget and its siblings are native: |
| QVERIFY(centralWidget->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(dockWidget->testAttribute(Qt::WA_NativeWindow)); |
| QVERIFY(toolBar->testAttribute(Qt::WA_NativeWindow)); |
| } |
| } |
| |
| class ASWidget : public QWidget |
| { |
| public: |
| ASWidget(QSize sizeHint, QSizePolicy sizePolicy, bool layout, bool hfwLayout, QWidget *parent = nullptr) |
| : QWidget(parent), mySizeHint(sizeHint) |
| { |
| setObjectName(QStringLiteral("ASWidget")); |
| setWindowTitle(objectName()); |
| setSizePolicy(sizePolicy); |
| if (layout) { |
| QSizePolicy sp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); |
| sp.setHeightForWidth(hfwLayout); |
| |
| QVBoxLayout *vbox = new QVBoxLayout; |
| vbox->setContentsMargins(0, 0, 0, 0); |
| vbox->addWidget(new ASWidget(sizeHint + QSize(30, 20), sp, false, false)); |
| setLayout(vbox); |
| } |
| } |
| |
| QSize sizeHint() const override |
| { |
| if (layout()) |
| return layout()->totalSizeHint(); |
| return mySizeHint; |
| } |
| int heightForWidth(int width) const override |
| { |
| return sizePolicy().hasHeightForWidth() ? width * 2 : -1; |
| } |
| |
| QSize mySizeHint; |
| }; |
| |
| void tst_QWidget::adjustSize_data() |
| { |
| const int MagicW = 200; |
| const int MagicH = 100; |
| |
| QTest::addColumn<QSize>("sizeHint"); |
| QTest::addColumn<int>("hPolicy"); |
| QTest::addColumn<int>("vPolicy"); |
| QTest::addColumn<bool>("hfwSP"); |
| QTest::addColumn<bool>("layout"); |
| QTest::addColumn<bool>("hfwLayout"); |
| QTest::addColumn<bool>("haveParent"); |
| QTest::addColumn<QSize>("expectedSize"); |
| |
| QTest::newRow("1") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << false << false << false << QSize(5, qMax(6, MagicH)); |
| QTest::newRow("2") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << true << false << false << false << QSize(5, qMax(10, MagicH)); |
| QTest::newRow("3") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << false << false << QSize(35, 26); |
| QTest::newRow("4") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << true << false << QSize(35, 70); |
| QTest::newRow("5") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << false << false << false << QSize(100000, 100000); |
| QTest::newRow("6") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << true << false << false << false << QSize(100000, 100000); |
| QTest::newRow("7") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << false << false << QSize(100000, 100000); |
| QTest::newRow("8") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << true << false << QSize(100000, 100000); |
| QTest::newRow("9") << QSize(5, 6) << int(QSizePolicy::Expanding) << int(QSizePolicy::Minimum) |
| << true << false << false << false << QSize(qMax(5, MagicW), 10); |
| |
| QTest::newRow("1c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << false << false << true << QSize(5, 6); |
| QTest::newRow("2c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << true << false << false << true << QSize(5, 6 /* or 10 would be OK too, since hfw contradicts sizeHint() */); |
| QTest::newRow("3c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << false << true << QSize(35, 26); |
| QTest::newRow("4c") << QSize(5, 6) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << true << true << QSize(35, 70); |
| QTest::newRow("5c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << false << false << true << QSize(40001, 30001); |
| QTest::newRow("6c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << true << false << false << true << QSize(40001, 30001 /* or 80002 would be OK too, since hfw contradicts sizeHint() */); |
| QTest::newRow("7c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << false << true << QSize(40001 + 30, 30001 + 20); |
| QTest::newRow("8c") << QSize(40001, 30001) << int(QSizePolicy::Minimum) << int(QSizePolicy::Expanding) |
| << false << true << true << true << QSize(40001 + 30, 80002 + 60); |
| QTest::newRow("9c") << QSize(5, 6) << int(QSizePolicy::Expanding) << int(QSizePolicy::Minimum) |
| << true << false << false << true << QSize(5, 6); |
| } |
| |
| void tst_QWidget::adjustSize() |
| { |
| QFETCH(QSize, sizeHint); |
| QFETCH(int, hPolicy); |
| QFETCH(int, vPolicy); |
| QFETCH(bool, hfwSP); |
| QFETCH(bool, layout); |
| QFETCH(bool, hfwLayout); |
| QFETCH(bool, haveParent); |
| QFETCH(QSize, expectedSize); |
| |
| QScopedPointer<QWidget> parent(new QWidget); |
| |
| QSizePolicy sp = QSizePolicy(QSizePolicy::Policy(hPolicy), QSizePolicy::Policy(vPolicy)); |
| sp.setHeightForWidth(hfwSP); |
| |
| QWidget *child = new ASWidget(sizeHint, sp, layout, hfwLayout, haveParent ? parent.data() : nullptr); |
| child->resize(123, 456); |
| child->adjustSize(); |
| if (expectedSize == QSize(100000, 100000)) { |
| QVERIFY2(child->size().width() < sizeHint.width(), |
| msgComparisonFailed(child->size().width(), "<", sizeHint.width())); |
| QVERIFY2(child->size().height() < sizeHint.height(), |
| msgComparisonFailed(child->size().height(), "<", sizeHint.height())); |
| } else { |
| QCOMPARE(child->size(), expectedSize); |
| } |
| if (!haveParent) |
| delete child; |
| } |
| |
| class TestLayout : public QVBoxLayout |
| { |
| Q_OBJECT |
| public: |
| using QVBoxLayout::QVBoxLayout; |
| |
| void invalidate() override |
| { |
| invalidated = true; |
| } |
| |
| bool invalidated = false; |
| }; |
| |
| void tst_QWidget::updateGeometry_data() |
| { |
| QTest::addColumn<QSize>("minSize"); |
| QTest::addColumn<bool>("shouldInvalidate"); |
| QTest::addColumn<QSize>("maxSize"); |
| QTest::addColumn<bool>("shouldInvalidate2"); |
| QTest::addColumn<QSizePolicy::Policy>("verticalSizePolicy"); |
| QTest::addColumn<bool>("shouldInvalidate3"); |
| QTest::addColumn<bool>("setVisible"); |
| QTest::addColumn<bool>("shouldInvalidate4"); |
| |
| QTest::newRow("setMinimumSize") |
| << QSize(100, 100) << true |
| << QSize() << false |
| << QSizePolicy::Preferred << false |
| << true << false; |
| QTest::newRow("setMaximumSize") |
| << QSize() << false |
| << QSize(100, 100) << true |
| << QSizePolicy::Preferred << false |
| << true << false; |
| QTest::newRow("setMinimumSize, then maximumSize to a different size") |
| << QSize(100, 100) << true |
| << QSize(300, 300) << true |
| << QSizePolicy::Preferred << false |
| << true << false; |
| QTest::newRow("setMinimumSize, then maximumSize to the same size") |
| << QSize(100, 100) << true |
| << QSize(100, 100) << true |
| << QSizePolicy::Preferred << false |
| << true << false; |
| QTest::newRow("setMinimumSize, then maximumSize to the same size and then hide it") |
| << QSize(100, 100) << true |
| << QSize(100, 100) << true |
| << QSizePolicy::Preferred << false |
| << false << true; |
| QTest::newRow("Change sizePolicy") |
| << QSize() << false |
| << QSize() << false |
| << QSizePolicy::Minimum << true |
| << true << false; |
| |
| } |
| |
| void tst_QWidget::updateGeometry() |
| { |
| QFETCH(QSize, minSize); |
| QFETCH(bool, shouldInvalidate); |
| QFETCH(QSize, maxSize); |
| QFETCH(bool, shouldInvalidate2); |
| QFETCH(QSizePolicy::Policy, verticalSizePolicy); |
| QFETCH(bool, shouldInvalidate3); |
| QFETCH(bool, setVisible); |
| QFETCH(bool, shouldInvalidate4); |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::") |
| + QLatin1String(QTest::currentDataTag())); |
| parent.resize(200, 200); |
| TestLayout *lout = new TestLayout(); |
| parent.setLayout(lout); |
| QWidget *child = new QWidget(&parent); |
| lout->addWidget(child); |
| parent.show(); |
| QApplication::processEvents(); |
| |
| lout->invalidated = false; |
| if (minSize.isValid()) |
| child->setMinimumSize(minSize); |
| QCOMPARE(lout->invalidated, shouldInvalidate); |
| |
| lout->invalidated = false; |
| if (maxSize.isValid()) |
| child->setMaximumSize(maxSize); |
| QCOMPARE(lout->invalidated, shouldInvalidate2); |
| |
| lout->invalidated = false; |
| child->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, verticalSizePolicy)); |
| if (shouldInvalidate3) |
| QCOMPARE(lout->invalidated, true); |
| |
| lout->invalidated = false; |
| if (!setVisible) |
| child->setVisible(false); |
| QCOMPARE(lout->invalidated, shouldInvalidate4); |
| } |
| |
| void tst_QWidget::sendUpdateRequestImmediately() |
| { |
| UpdateWidget updateWidget; |
| updateWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| updateWidget.show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&updateWidget)); |
| |
| QCoreApplication::processEvents(); |
| updateWidget.reset(); |
| |
| QCOMPARE(updateWidget.numUpdateRequestEvents, 0); |
| updateWidget.repaint(); |
| QCOMPARE(updateWidget.numUpdateRequestEvents, 1); |
| } |
| |
| void tst_QWidget::doubleRepaint() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974"); |
| #endif |
| |
| #if defined(Q_OS_OSX) |
| if (!macHasAccessToWindowsServer()) |
| QSKIP("Not having window server access causes the wrong number of repaints to be issues"); |
| #endif |
| UpdateWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| centerOnScreen(&widget); |
| widget.setFocusPolicy(Qt::StrongFocus); |
| // Filter out activation change and focus events to avoid update() calls in QWidget. |
| widget.updateOnActivationChangeAndFocusIn = false; |
| |
| // Show: 1 repaint |
| int expectedRepaints = 1; |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTRY_COMPARE(widget.numPaintEvents, expectedRepaints); |
| widget.numPaintEvents = 0; |
| |
| // Minmize: Should not trigger a repaint. |
| widget.showMinimized(); |
| QTest::qWait(10); |
| #if defined(Q_OS_QNX) |
| QEXPECT_FAIL("", "Platform does not support showMinimized()", Continue); |
| #endif |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QCOMPARE(widget.numPaintEvents, 0); |
| widget.numPaintEvents = 0; |
| |
| // Restore: Should not trigger a repaint. |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QTest::qWait(10); |
| QCOMPARE(widget.numPaintEvents, 0); |
| } |
| |
| void tst_QWidget::resizeInPaintEvent() |
| { |
| QWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| UpdateWidget widget(&window); |
| window.resize(200, 200); |
| window.show(); |
| QApplication::setActiveWindow(&window); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QTRY_VERIFY(widget.numPaintEvents > 0); |
| |
| widget.reset(); |
| QCOMPARE(widget.numPaintEvents, 0); |
| |
| widget.resizeInPaintEvent = true; |
| // This will call resize in the paintEvent, which in turn will call |
| // invalidateBackingStore() and a new update request should be posted. |
| widget.repaint(); |
| QCOMPARE(widget.numPaintEvents, 1); |
| widget.numPaintEvents = 0; |
| |
| // Make sure the resize triggers another update. |
| QTRY_COMPARE(widget.numPaintEvents, 1); |
| } |
| |
| void tst_QWidget::opaqueChildren() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(200, 200); |
| |
| QWidget child(&widget); |
| child.setGeometry(-700, -700, 200, 200); |
| |
| QWidget grandChild(&child); |
| grandChild.resize(200, 200); |
| |
| QWidget greatGrandChild(&grandChild); |
| greatGrandChild.setGeometry(50, 50, 200, 200); |
| greatGrandChild.setPalette(Qt::red); |
| greatGrandChild.setAutoFillBackground(true); // Opaque child widget. |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| |
| // Child, grandChild and greatGrandChild are outside the ancestor clip. |
| QRegion expectedOpaqueRegion(50, 50, 150, 150); |
| QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion); |
| |
| // Now they are all inside the ancestor clip. |
| child.setGeometry(50, 50, 150, 150); |
| QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion); |
| |
| // Set mask on greatGrandChild. |
| const QRegion mask(10, 10, 50, 50); |
| greatGrandChild.setMask(mask); |
| expectedOpaqueRegion &= mask.translated(50, 50); |
| QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), expectedOpaqueRegion); |
| |
| // Make greatGrandChild "transparent". |
| greatGrandChild.setAutoFillBackground(false); |
| QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), QRegion()); |
| } |
| |
| |
| class MaskSetWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| using QWidget::QWidget; |
| |
| void paintEvent(QPaintEvent *event) override |
| { |
| QPainter p(this); |
| |
| paintedRegion += event->region(); |
| for (const QRect &r : event->region()) |
| p.fillRect(r, Qt::red); |
| } |
| |
| void resizeEvent(QResizeEvent *) override |
| { |
| setMask(QRegion(QRect(0, 0, width(), 10).normalized())); |
| } |
| |
| QRegion paintedRegion; |
| |
| public slots: |
| void resizeDown() { setGeometry(QRect(0, 50, 50, 50)); } |
| void resizeUp() { setGeometry(QRect(0, 50, 150, 50)); } |
| }; |
| |
| void tst_QWidget::setMaskInResizeEvent() |
| { |
| UpdateWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.reset(); |
| w.resize(200, 200); |
| centerOnScreen(&w); |
| w.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); |
| w.raise(); |
| |
| MaskSetWidget testWidget(&w); |
| testWidget.setGeometry(0, 0, 100, 100); |
| testWidget.setMask(QRegion(QRect(0,0,100,10))); |
| testWidget.show(); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QTRY_VERIFY(w.numPaintEvents > 0); |
| |
| w.reset(); |
| testWidget.paintedRegion = QRegion(); |
| QTimer::singleShot(0, &testWidget, SLOT(resizeDown())); |
| QTest::qWait(100); |
| |
| QRegion expectedParentUpdate(0, 0, 100, 10); // Old testWidget area. |
| expectedParentUpdate += testWidget.geometry(); // New testWidget area. |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QTRY_COMPARE(w.paintedRegion, expectedParentUpdate); |
| QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask()); |
| |
| testWidget.paintedRegion = QRegion(); |
| // Now resize the widget again, but in the oposite direction |
| QTimer::singleShot(0, &testWidget, SLOT(resizeUp())); |
| QTest::qWait(100); |
| |
| QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask()); |
| } |
| |
| class MoveInResizeWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| explicit MoveInResizeWidget(QWidget *p = nullptr) |
| : QWidget(p) |
| { |
| setWindowFlags(Qt::FramelessWindowHint); |
| } |
| |
| void resizeEvent(QResizeEvent *) override |
| { |
| move(QPoint(100,100)); |
| |
| static bool firstTime = true; |
| if (firstTime) |
| QTimer::singleShot(250, this, &MoveInResizeWidget::resizeMe); |
| |
| firstTime = false; |
| } |
| |
| public slots: |
| void resizeMe() { |
| resize(100, 100); |
| } |
| }; |
| |
| void tst_QWidget::moveInResizeEvent() |
| { |
| MoveInResizeWidget testWidget; |
| testWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| testWidget.setGeometry(50, 50, 200, 200); |
| testWidget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); |
| |
| QRect expectedGeometry(100,100, 100, 100); |
| QTRY_COMPARE(testWidget.geometry(), expectedGeometry); |
| } |
| |
| #ifdef QT_BUILD_INTERNAL |
| void tst_QWidget::immediateRepaintAfterInvalidateBackingStore() |
| { |
| if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows")) |
| QSKIP("We don't support immediate repaint right after show on other platforms."); |
| |
| QScopedPointer<UpdateWidget> widget(new UpdateWidget); |
| widget->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| centerOnScreen(widget.data()); |
| widget->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(widget.data())); |
| |
| widget->numPaintEvents = 0; |
| |
| // Marks the area covered by the widget as dirty in the backing store and |
| // posts an UpdateRequest event. |
| qt_widget_private(widget.data())->invalidateBackingStore(widget->rect()); |
| QCOMPARE(widget->numPaintEvents, 0); |
| |
| // The entire widget is already dirty, but this time we want to update immediately |
| // by calling repaint(), and thus we have to repaint the widget and not wait for |
| // the UpdateRequest to be sent when we get back to the event loop. |
| widget->repaint(); |
| QCOMPARE(widget->numPaintEvents, 1); |
| } |
| #endif |
| |
| void tst_QWidget::effectiveWinId() |
| { |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.resize(200, 200); |
| QWidget child(&parent); |
| |
| // Shouldn't crash. |
| QVERIFY(!parent.effectiveWinId()); |
| QVERIFY(!child.effectiveWinId()); |
| |
| parent.show(); |
| |
| QVERIFY(parent.effectiveWinId()); |
| QVERIFY(child.effectiveWinId()); |
| } |
| |
| void tst_QWidget::effectiveWinId2() |
| { |
| QWidget parent; |
| |
| class MyWidget : public QWidget |
| { |
| bool event(QEvent *e) override |
| { |
| if (e->type() == QEvent::WinIdChange) { |
| // Shouldn't crash. |
| effectiveWinId(); |
| } |
| |
| return QWidget::event(e); |
| } |
| }; |
| |
| MyWidget child; |
| child.setParent(&parent); |
| parent.show(); |
| |
| child.setParent(nullptr); |
| child.setParent(&parent); |
| } |
| |
| class CustomWidget : public QWidget |
| { |
| public: |
| mutable int metricCallCount = 0; |
| |
| using QWidget::QWidget; |
| |
| int metric(PaintDeviceMetric metric) const override |
| { |
| ++metricCallCount; |
| return QWidget::metric(metric); |
| } |
| }; |
| |
| void tst_QWidget::customDpi() |
| { |
| QScopedPointer<QWidget> topLevel(new QWidget); |
| CustomWidget *custom = new CustomWidget(topLevel.data()); |
| QWidget *child = new QWidget(custom); |
| |
| custom->metricCallCount = 0; |
| topLevel->logicalDpiX(); |
| QCOMPARE(custom->metricCallCount, 0); |
| custom->logicalDpiX(); |
| QCOMPARE(custom->metricCallCount, 1); |
| child->logicalDpiX(); |
| QCOMPARE(custom->metricCallCount, 1); |
| } |
| |
| void tst_QWidget::customDpiProperty() |
| { |
| QScopedPointer<QWidget> topLevel(new QWidget); |
| QWidget *middle = new CustomWidget(topLevel.data()); |
| QWidget *child = new QWidget(middle); |
| |
| const int initialDpiX = topLevel->logicalDpiX(); |
| const int initialDpiY = topLevel->logicalDpiY(); |
| |
| middle->setProperty("_q_customDpiX", 300); |
| middle->setProperty("_q_customDpiY", 400); |
| |
| QCOMPARE(topLevel->logicalDpiX(), initialDpiX); |
| QCOMPARE(topLevel->logicalDpiY(), initialDpiY); |
| |
| QCOMPARE(middle->logicalDpiX(), 300); |
| QCOMPARE(middle->logicalDpiY(), 400); |
| |
| QCOMPARE(child->logicalDpiX(), 300); |
| QCOMPARE(child->logicalDpiY(), 400); |
| |
| middle->setProperty("_q_customDpiX", QVariant()); |
| middle->setProperty("_q_customDpiY", QVariant()); |
| |
| QCOMPARE(topLevel->logicalDpiX(), initialDpiX); |
| QCOMPARE(topLevel->logicalDpiY(), initialDpiY); |
| |
| QCOMPARE(middle->logicalDpiX(), initialDpiX); |
| QCOMPARE(middle->logicalDpiY(), initialDpiY); |
| |
| QCOMPARE(child->logicalDpiX(), initialDpiX); |
| QCOMPARE(child->logicalDpiY(), initialDpiY); |
| } |
| |
| void tst_QWidget::quitOnCloseAttribute() |
| { |
| QWidget w; |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true); |
| w.setAttribute(Qt::WA_QuitOnClose, false); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::Tool); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::Popup); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::ToolTip); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::SplashScreen); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::SubWindow); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| |
| w.setAttribute(Qt::WA_QuitOnClose); |
| w.setWindowFlags(Qt::Dialog); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true); |
| w.show(); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), true); |
| w.setWindowFlags(Qt::Tool); |
| QCOMPARE(w.testAttribute(Qt::WA_QuitOnClose), false); |
| } |
| |
| void tst_QWidget::moveRect() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(200, 200); |
| widget.setUpdatesEnabled(false); |
| QWidget child(&widget); |
| child.setUpdatesEnabled(false); |
| child.setAttribute(Qt::WA_OpaquePaintEvent); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| child.move(10, 10); // Don't crash. |
| } |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| class GDIWidget : public QDialog |
| { |
| Q_OBJECT |
| public: |
| GDIWidget() { |
| setAttribute(Qt::WA_PaintOnScreen); |
| timer.setSingleShot(true); |
| timer.setInterval(0); |
| } |
| QPaintEngine *paintEngine() const override { return nullptr; } |
| |
| void paintEvent(QPaintEvent *) override |
| { |
| QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface(); |
| const auto hdc = reinterpret_cast<HDC>(ni->nativeResourceForWindow(QByteArrayLiteral("getDC"), windowHandle())); |
| if (hdc) { |
| const HBRUSH brush = CreateSolidBrush(RGB(255, 0, 0)); |
| SelectObject(hdc, brush); |
| Rectangle(hdc, 0, 0, 10, 10); |
| DeleteObject(brush); |
| ni->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), windowHandle()); |
| } else { |
| qWarning("%s: Unable to obtain native DC.", Q_FUNC_INFO); |
| } |
| if (!timer.isActive()) { |
| connect(&timer, &QTimer::timeout, this, |
| hdc ? &GDIWidget::slotTimer : &QDialog::reject); |
| timer.start(); |
| } |
| } |
| |
| QSize sizeHint() const override { return {400, 300}; }; |
| |
| private slots: |
| void slotTimer() { |
| QScreen *screen = windowHandle()->screen(); |
| const QImage im = screen->grabWindow(internalWinId(), 0, 0, -1, -1).toImage(); |
| color = im.pixel(1, 1); |
| accept(); |
| } |
| |
| public: |
| QColor color; |
| QTimer timer; |
| }; |
| |
| void tst_QWidget::gdiPainting() |
| { |
| GDIWidget w; |
| w.exec(); |
| |
| QCOMPARE(w.color, QColor(255, 0, 0)); |
| |
| } |
| |
| void tst_QWidget::paintOnScreenPossible() |
| { |
| QWidget w1; |
| w1.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w1.setAttribute(Qt::WA_PaintOnScreen); |
| QVERIFY(!w1.testAttribute(Qt::WA_PaintOnScreen)); |
| |
| GDIWidget w2; |
| w2.setAttribute(Qt::WA_PaintOnScreen); |
| QVERIFY(w2.testAttribute(Qt::WA_PaintOnScreen)); |
| } |
| #endif // Q_OS_WIN && !Q_OS_WINRT |
| |
| void tst_QWidget::reparentStaticWidget() |
| { |
| QWidget window1; |
| window1.setWindowTitle(QStringLiteral("window1 ") + __FUNCTION__); |
| window1.resize(m_testWidgetSize); |
| window1.move(m_availableTopLeft + QPoint(100, 100)); |
| |
| QWidget *child = new QWidget(&window1); |
| child->setPalette(Qt::red); |
| child->setAutoFillBackground(true); |
| child->setAttribute(Qt::WA_StaticContents); |
| child->resize(window1.width() - 40, window1.height() - 40); |
| child->setWindowTitle(QStringLiteral("child ") + __FUNCTION__); |
| |
| QWidget *grandChild = new QWidget(child); |
| grandChild->setPalette(Qt::blue); |
| grandChild->setAutoFillBackground(true); |
| grandChild->resize(50, 50); |
| grandChild->setAttribute(Qt::WA_StaticContents); |
| window1.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window1)); |
| |
| QWidget window2; |
| window2.setWindowTitle(QStringLiteral("window2 ") + __FUNCTION__); |
| window2.resize(m_testWidgetSize); |
| window2.move(window1.geometry().topRight() + QPoint(100, 0)); |
| window2.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window2)); |
| |
| // Reparent into another top-level. |
| child->setParent(&window2); |
| child->show(); |
| |
| // Please don't crash. |
| window1.resize(window1.size() + QSize(2, 2)); |
| QTest::qWait(20); |
| |
| // Make sure we move all static children even though |
| // the reparented widget itself is non-static. |
| child->setAttribute(Qt::WA_StaticContents, false); |
| child->setParent(&window1); |
| child->show(); |
| |
| // Please don't crash. |
| window2.resize(window2.size() + QSize(2, 2)); |
| QTest::qWait(20); |
| |
| child->setParent(nullptr); |
| child->show(); |
| QTest::qWait(20); |
| |
| // Please don't crash. |
| child->resize(child->size() + QSize(2, 2)); |
| window2.resize(window2.size() + QSize(2, 2)); |
| QTest::qWait(20); |
| |
| QWidget *siblingOfGrandChild = new QWidget(child); |
| siblingOfGrandChild->show(); |
| QTest::qWait(20); |
| |
| // Nothing should happen when reparenting within the same top-level. |
| grandChild->setParent(siblingOfGrandChild); |
| grandChild->show(); |
| QTest::qWait(20); |
| |
| QWidget paintOnScreen; |
| paintOnScreen.setWindowTitle(QStringLiteral("paintOnScreen ") + __FUNCTION__); |
| paintOnScreen.resize(m_testWidgetSize); |
| paintOnScreen.move(window1.geometry().bottomLeft() + QPoint(0, 50)); |
| |
| paintOnScreen.setAttribute(Qt::WA_PaintOnScreen); |
| paintOnScreen.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&paintOnScreen)); |
| QTest::qWait(20); |
| |
| child->setParent(&paintOnScreen); |
| child->show(); |
| QTest::qWait(20); |
| |
| // Please don't crash. |
| paintOnScreen.resize(paintOnScreen.size() + QSize(2, 2)); |
| QTest::qWait(20); |
| |
| } |
| |
| void tst_QWidget::QTBUG6883_reparentStaticWidget2() |
| { |
| QMainWindow mw; |
| mw.setWindowTitle(QStringLiteral("MainWindow ") + __FUNCTION__); |
| mw.move(m_availableTopLeft + QPoint(100, 100)); |
| |
| QDockWidget *one = new QDockWidget(QStringLiteral("Dock ") + __FUNCTION__, &mw); |
| mw.addDockWidget(Qt::LeftDockWidgetArea, one , Qt::Vertical); |
| |
| QWidget *child = new QWidget(); |
| child->setPalette(Qt::red); |
| child->setAutoFillBackground(true); |
| child->setAttribute(Qt::WA_StaticContents); |
| child->resize(m_testWidgetSize); |
| one->setWidget(child); |
| |
| QToolBar *mainTools = mw.addToolBar("Main Tools"); |
| QLineEdit *le = new QLineEdit; |
| le->setMinimumWidth(m_testWidgetSize.width()); |
| mainTools->addWidget(le); |
| |
| mw.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&mw)); |
| |
| one->setFloating(true); |
| QTest::qWait(20); |
| //do not crash |
| } |
| |
| class ColorRedWidget : public QWidget |
| { |
| public: |
| explicit ColorRedWidget(QWidget *parent = nullptr) |
| : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip) |
| { |
| } |
| |
| void paintEvent(QPaintEvent *) override |
| { |
| QPainter p(this); |
| p.fillRect(rect(),Qt::red); |
| } |
| }; |
| |
| void tst_QWidget::translucentWidget() |
| { |
| QPixmap pm(16,16); |
| pm.fill(Qt::red); |
| ColorRedWidget label; |
| label.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| label.setFixedSize(16,16); |
| label.setAttribute(Qt::WA_TranslucentBackground); |
| const QPoint labelPos = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); |
| label.move(labelPos); |
| label.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&label)); |
| |
| QPixmap widgetSnapshot; |
| |
| #ifdef Q_OS_WIN |
| QWidget *desktopWidget = QApplication::desktop()->screen(0); |
| widgetSnapshot = grabWindow(desktopWidget->windowHandle(), labelPos.x(), labelPos.y(), label.width(), label.height()); |
| #else |
| widgetSnapshot = label.grab(QRect(QPoint(0, 0), label.size())); |
| #endif |
| const QImage actual = widgetSnapshot.toImage().convertToFormat(QImage::Format_RGB32); |
| QImage expected = pm.toImage().scaled(label.devicePixelRatioF() * pm.size()); |
| expected.setDevicePixelRatio(label.devicePixelRatioF()); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QCOMPARE(actual.size(),expected.size()); |
| QCOMPARE(actual,expected); |
| |
| const QWindow *window = label.windowHandle(); |
| const QSurfaceFormat translucentFormat = window->requestedFormat(); |
| label.setAttribute(Qt::WA_TranslucentBackground, false); |
| const QSurfaceFormat opaqueFormat = window->requestedFormat(); |
| QVERIFY(translucentFormat != opaqueFormat); |
| } |
| |
| class MaskResizeTestWidget : public QWidget |
| { |
| Q_OBJECT |
| public: |
| explicit MaskResizeTestWidget(QWidget* p = nullptr) : QWidget(p) |
| { |
| setMask(QRegion(QRect(0, 0, 100, 100).normalized())); |
| } |
| |
| void paintEvent(QPaintEvent* event) override |
| { |
| QPainter p(this); |
| |
| paintedRegion += event->region(); |
| for (const QRect &r : event->region()) |
| p.fillRect(r, Qt::red); |
| } |
| |
| QRegion paintedRegion; |
| |
| public slots: |
| void enlargeMask() { |
| QRegion newMask(QRect(0, 0, 150, 150).normalized()); |
| setMask(newMask); |
| } |
| |
| void shrinkMask() { |
| QRegion newMask(QRect(0, 0, 50, 50).normalized()); |
| setMask(newMask); |
| } |
| |
| }; |
| |
| void tst_QWidget::setClearAndResizeMask() |
| { |
| UpdateWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| topLevel.resize(160, 160); |
| centerOnScreen(&topLevel); |
| topLevel.show(); |
| QApplication::setActiveWindow(&topLevel); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| QTRY_VERIFY(topLevel.numPaintEvents > 0); |
| topLevel.reset(); |
| |
| // Mask top-level widget |
| const QRegion topLevelMask(0, 0, 100, 100, QRegion::Ellipse); |
| topLevel.setMask(topLevelMask); |
| QCOMPARE(topLevel.mask(), topLevelMask); |
| // Ensure that the top-level doesn't get any update. |
| // We don't control what's happening on platforms other than X11, Windows |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) |
| QCOMPARE(topLevel.numPaintEvents, 0); |
| |
| topLevel.reset(); |
| |
| // Clear top-level mask |
| topLevel.clearMask(); |
| QCOMPARE(topLevel.mask(), QRegion()); |
| QTest::qWait(10); |
| QRegion outsideOldMask(topLevel.rect()); |
| outsideOldMask -= topLevelMask; |
| // Ensure that the top-level gets an update for the area outside the old mask. |
| // We don't control what's happening on platforms other than X11, Windows |
| if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("windows")) { |
| QTRY_VERIFY(topLevel.numPaintEvents > 0); |
| QTRY_COMPARE(topLevel.paintedRegion, outsideOldMask); |
| } |
| |
| UpdateWidget child(&topLevel); |
| child.setAutoFillBackground(true); // NB! Opaque child. |
| child.setPalette(Qt::red); |
| child.resize(100, 100); |
| child.show(); |
| QTest::qWait(10); |
| |
| child.reset(); |
| topLevel.reset(); |
| |
| // Mask child widget with a mask that is smaller than the rect |
| const QRegion childMask(0, 0, 50, 50); |
| child.setMask(childMask); |
| QTRY_COMPARE(child.mask(), childMask); |
| // and ensure that the child widget doesn't get any update. |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (child.internalWinId()) |
| QCOMPARE(child.numPaintEvents, 1); |
| else |
| #endif |
| QCOMPARE(child.numPaintEvents, 0); |
| // and the parent widget gets an update for the newly exposed area. |
| QTRY_COMPARE(topLevel.numPaintEvents, 1); |
| QRegion expectedParentExpose(child.rect()); |
| expectedParentExpose -= childMask; |
| QCOMPARE(topLevel.paintedRegion, expectedParentExpose); |
| |
| child.reset(); |
| topLevel.reset(); |
| |
| // Clear child widget mask |
| child.clearMask(); |
| QTRY_COMPARE(child.mask(), QRegion()); |
| // and ensure that that the child widget gets an update for the area outside the old mask. |
| QTRY_COMPARE(child.numPaintEvents, 1); |
| outsideOldMask = child.rect(); |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (!child.internalWinId()) |
| #endif |
| outsideOldMask -= childMask; |
| QCOMPARE(child.paintedRegion, outsideOldMask); |
| // and the parent widget doesn't get any update. |
| QCOMPARE(topLevel.numPaintEvents, 0); |
| |
| child.reset(); |
| topLevel.reset(); |
| |
| // Mask child widget with a mask that is bigger than the rect |
| child.setMask(QRegion(0, 0, 1000, 1000)); |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (child.internalWinId()) |
| QTRY_COMPARE(child.numPaintEvents, 1); |
| else |
| #endif |
| // and ensure that we don't get any updates at all. |
| QTRY_COMPARE(child.numPaintEvents, 0); |
| QCOMPARE(topLevel.numPaintEvents, 0); |
| |
| // ...and the same applies when clearing the mask. |
| child.clearMask(); |
| QTest::qWait(100); |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (child.internalWinId()) |
| QTRY_VERIFY(child.numPaintEvents > 0); |
| else |
| #endif |
| QCOMPARE(child.numPaintEvents, 0); |
| QCOMPARE(topLevel.numPaintEvents, 0); |
| |
| QWidget resizeParent; |
| MaskResizeTestWidget resizeChild(&resizeParent); |
| |
| resizeParent.resize(300,300); |
| resizeParent.raise(); |
| resizeParent.setWindowFlags(Qt::WindowStaysOnTopHint); |
| resizeChild.setGeometry(50,50,200,200); |
| QPalette pal = resizeParent.palette(); |
| pal.setColor(QPalette::Window, QColor(Qt::white)); |
| resizeParent.setPalette(pal); |
| |
| resizeParent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&resizeParent)); |
| // Disable the size grip on the Mac; otherwise it'll be included when grabbing the window. |
| resizeParent.setFixedSize(resizeParent.size()); |
| resizeChild.show(); |
| QTest::qWait(100); |
| resizeChild.paintedRegion = QRegion(); |
| |
| QTimer::singleShot(100, &resizeChild, SLOT(shrinkMask())); |
| QTest::qWait(200); |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (child.internalWinId()) |
| QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask()); |
| else |
| #endif |
| QTRY_COMPARE(resizeChild.paintedRegion, QRegion()); |
| |
| resizeChild.paintedRegion = QRegion(); |
| const QRegion oldMask = resizeChild.mask(); |
| QTimer::singleShot(0, &resizeChild, SLOT(enlargeMask())); |
| QTest::qWait(100); |
| #ifdef Q_OS_OSX |
| // Mac always issues a full update when calling setMask, and we cannot force it to not do so. |
| if (child.internalWinId()) |
| QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask()); |
| else |
| #endif |
| QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask() - oldMask); |
| } |
| |
| void tst_QWidget::maskedUpdate() |
| { |
| UpdateWidget topLevel; |
| topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| topLevel.resize(200, 200); |
| centerOnScreen(&topLevel); |
| const QRegion topLevelMask(50, 50, 70, 70); |
| topLevel.setMask(topLevelMask); |
| |
| UpdateWidget child(&topLevel); |
| child.setGeometry(20, 20, 180, 180); |
| const QRegion childMask(60, 60, 30, 30); |
| child.setMask(childMask); |
| |
| UpdateWidget grandChild(&child); |
| grandChild.setGeometry(50, 50, 100, 100); |
| const QRegion grandChildMask(20, 20, 10, 10); |
| grandChild.setMask(grandChildMask); |
| |
| topLevel.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); |
| QTRY_VERIFY(topLevel.numPaintEvents > 0); |
| |
| |
| #define RESET_WIDGETS \ |
| topLevel.reset(); \ |
| child.reset(); \ |
| grandChild.reset(); |
| |
| #define CLEAR_MASK(widget) \ |
| widget.clearMask(); \ |
| QTest::qWait(100); \ |
| RESET_WIDGETS; |
| |
| // All widgets are transparent at this point, so any call to update() will result |
| // in composition, i.e. the update propagates to ancestors and children. |
| |
| // TopLevel update. |
| RESET_WIDGETS; |
| topLevel.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, topLevelMask); |
| QTRY_COMPARE(child.paintedRegion, childMask); |
| QTRY_COMPARE(grandChild.paintedRegion, grandChildMask); |
| |
| // Child update. |
| RESET_WIDGETS; |
| child.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, childMask.translated(child.pos())); |
| QTRY_COMPARE(child.paintedRegion, childMask); |
| QTRY_COMPARE(grandChild.paintedRegion, grandChildMask); |
| |
| // GrandChild update. |
| RESET_WIDGETS; |
| grandChild.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, grandChildMask.translated(grandChild.mapTo(&topLevel, QPoint()))); |
| QTRY_COMPARE(child.paintedRegion, grandChildMask.translated(grandChild.pos())); |
| QTRY_COMPARE(grandChild.paintedRegion, grandChildMask); |
| |
| topLevel.setAttribute(Qt::WA_OpaquePaintEvent); |
| child.setAttribute(Qt::WA_OpaquePaintEvent); |
| grandChild.setAttribute(Qt::WA_OpaquePaintEvent); |
| |
| // All widgets are now opaque, which means no composition, i.e. |
| // the update does not propate to ancestors and children. |
| |
| // TopLevel update. |
| RESET_WIDGETS; |
| topLevel.update(); |
| QTest::qWait(10); |
| |
| QRegion expectedTopLevelUpdate = topLevelMask; |
| expectedTopLevelUpdate -= childMask.translated(child.pos()); // Subtract opaque children. |
| QTRY_COMPARE(topLevel.paintedRegion, expectedTopLevelUpdate); |
| QTRY_COMPARE(child.paintedRegion, QRegion()); |
| QTRY_COMPARE(grandChild.paintedRegion, QRegion()); |
| |
| // Child update. |
| RESET_WIDGETS; |
| child.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| QRegion expectedChildUpdate = childMask; |
| expectedChildUpdate -= grandChildMask.translated(grandChild.pos()); // Subtract oapque children. |
| QTRY_COMPARE(child.paintedRegion, expectedChildUpdate); |
| QTRY_COMPARE(grandChild.paintedRegion, QRegion()); |
| |
| // GrandChild update. |
| RESET_WIDGETS; |
| grandChild.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| QTRY_COMPARE(child.paintedRegion, QRegion()); |
| QTRY_COMPARE(grandChild.paintedRegion, grandChildMask); |
| |
| // GrandChild update. |
| CLEAR_MASK(grandChild); |
| grandChild.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| QTRY_COMPARE(child.paintedRegion, QRegion()); |
| QRegion expectedGrandChildUpdate = grandChild.rect(); |
| // Clip with parent's mask. |
| expectedGrandChildUpdate &= childMask.translated(-grandChild.pos()); |
| QCOMPARE(grandChild.paintedRegion, expectedGrandChildUpdate); |
| |
| // GrandChild update. |
| CLEAR_MASK(child); |
| grandChild.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| QTRY_COMPARE(child.paintedRegion, QRegion()); |
| expectedGrandChildUpdate = grandChild.rect(); |
| // Clip with parent's mask. |
| expectedGrandChildUpdate &= topLevelMask.translated(-grandChild.mapTo(&topLevel, QPoint())); |
| QTRY_COMPARE(grandChild.paintedRegion, expectedGrandChildUpdate); |
| |
| // Child update. |
| RESET_WIDGETS; |
| child.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| expectedChildUpdate = child.rect(); |
| // Clip with parent's mask. |
| expectedChildUpdate &= topLevelMask.translated(-child.pos()); |
| expectedChildUpdate -= grandChild.geometry(); // Subtract opaque children. |
| QTRY_COMPARE(child.paintedRegion, expectedChildUpdate); |
| QTRY_COMPARE(grandChild.paintedRegion, QRegion()); |
| |
| // GrandChild update. |
| CLEAR_MASK(topLevel); |
| grandChild.update(); |
| QTest::qWait(10); |
| |
| QTRY_COMPARE(topLevel.paintedRegion, QRegion()); |
| QTRY_COMPARE(child.paintedRegion, QRegion()); |
| QTRY_COMPARE(grandChild.paintedRegion, QRegion(grandChild.rect())); // Full update. |
| } |
| |
| #ifndef QT_NO_CURSOR |
| void tst_QWidget::syntheticEnterLeave() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: This fails. Figure out why."); |
| class MyWidget : public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| void enterEvent(QEvent *) override { ++numEnterEvents; } |
| void leaveEvent(QEvent *) override { ++numLeaveEvents; } |
| int numEnterEvents = 0; |
| int numLeaveEvents = 0; |
| }; |
| |
| QCursor::setPos(m_safeCursorPos); |
| |
| MyWidget window; |
| window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| window.setWindowFlags(Qt::WindowStaysOnTopHint); |
| window.move(200, 200); |
| window.resize(200, 200); |
| |
| MyWidget *child1 = new MyWidget(&window); |
| child1->setPalette(Qt::blue); |
| child1->setAutoFillBackground(true); |
| child1->resize(200, 200); |
| child1->setCursor(Qt::OpenHandCursor); |
| |
| MyWidget *child2 = new MyWidget(&window); |
| child2->resize(200, 200); |
| |
| MyWidget *grandChild = new MyWidget(child2); |
| grandChild->setPalette(Qt::red); |
| grandChild->setAutoFillBackground(true); |
| grandChild->resize(200, 200); |
| grandChild->setCursor(Qt::WaitCursor); |
| |
| window.show(); |
| window.raise(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| |
| #define RESET_EVENT_COUNTS \ |
| window.numEnterEvents = 0; \ |
| window.numLeaveEvents = 0; \ |
| child1->numEnterEvents = 0; \ |
| child1->numLeaveEvents = 0; \ |
| child2->numEnterEvents = 0; \ |
| child2->numLeaveEvents = 0; \ |
| grandChild->numEnterEvents = 0; \ |
| grandChild->numLeaveEvents = 0; |
| |
| // Position the cursor in the middle of the window. |
| const QPoint globalPos = window.mapToGlobal(QPoint(100, 100)); |
| QCursor::setPos(globalPos); // Enter child2 and grandChild. |
| QTest::qWait(300); |
| |
| QCOMPARE(window.numLeaveEvents, 0); |
| QCOMPARE(child2->numLeaveEvents, 0); |
| QCOMPARE(grandChild->numLeaveEvents, 0); |
| QCOMPARE(child1->numLeaveEvents, 0); |
| |
| // This event arrives asynchronously |
| QTRY_COMPARE(window.numEnterEvents, 1); |
| QCOMPARE(child2->numEnterEvents, 1); |
| QCOMPARE(grandChild->numEnterEvents, 1); |
| QCOMPARE(child1->numEnterEvents, 0); |
| |
| RESET_EVENT_COUNTS; |
| child2->hide(); // Leave child2 and grandChild, enter child1. |
| |
| QCOMPARE(window.numLeaveEvents, 0); |
| QCOMPARE(child2->numLeaveEvents, 1); |
| QCOMPARE(grandChild->numLeaveEvents, 1); |
| QCOMPARE(child1->numLeaveEvents, 0); |
| |
| QCOMPARE(window.numEnterEvents, 0); |
| QCOMPARE(child2->numEnterEvents, 0); |
| QCOMPARE(grandChild->numEnterEvents, 0); |
| QCOMPARE(child1->numEnterEvents, 1); |
| |
| RESET_EVENT_COUNTS; |
| child2->show(); // Leave child1, enter child2 and grandChild. |
| |
| QCOMPARE(window.numLeaveEvents, 0); |
| QCOMPARE(child2->numLeaveEvents, 0); |
| QCOMPARE(grandChild->numLeaveEvents, 0); |
| QCOMPARE(child1->numLeaveEvents, 1); |
| |
| QCOMPARE(window.numEnterEvents, 0); |
| QCOMPARE(child2->numEnterEvents, 1); |
| QCOMPARE(grandChild->numEnterEvents, 1); |
| QCOMPARE(child1->numEnterEvents, 0); |
| |
| RESET_EVENT_COUNTS; |
| delete child2; // Enter child1 (and do not send leave events to child2 and grandChild). |
| |
| QCOMPARE(window.numLeaveEvents, 0); |
| QCOMPARE(child1->numLeaveEvents, 0); |
| |
| QCOMPARE(window.numEnterEvents, 0); |
| QCOMPARE(child1->numEnterEvents, 1); |
| } |
| #endif |
| |
| #ifndef QT_NO_CURSOR |
| void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() |
| { |
| if (m_platform == QStringLiteral("wayland")) |
| QSKIP("Wayland: Clients can't set cursor position on wayland."); |
| class SELParent : public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| |
| void mousePressEvent(QMouseEvent *) override { child->show(); } |
| QWidget *child = nullptr; |
| }; |
| |
| class SELChild : public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| void enterEvent(QEvent *) override { ++numEnterEvents; } |
| void mouseMoveEvent(QMouseEvent *event) override |
| { |
| QCOMPARE(event->button(), Qt::NoButton); |
| QCOMPARE(event->buttons(), QApplication::mouseButtons()); |
| QCOMPARE(event->modifiers(), QApplication::keyboardModifiers()); |
| ++numMouseMoveEvents; |
| } |
| void reset() { numEnterEvents = numMouseMoveEvents = 0; } |
| int numEnterEvents = 0, numMouseMoveEvents = 0; |
| }; |
| |
| QCursor::setPos(m_safeCursorPos); |
| |
| SELParent parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.move(200, 200); |
| parent.resize(200, 200); |
| SELChild child(&parent); |
| child.resize(200, 200); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowActive(&parent)); |
| |
| QCursor::setPos(child.mapToGlobal(QPoint(100, 100))); |
| // Make sure the cursor has entered the child. |
| QTRY_VERIFY(child.numEnterEvents > 0); |
| |
| child.hide(); |
| child.reset(); |
| child.show(); |
| |
| // Make sure the child gets enter event and no mouse move event. |
| QTRY_COMPARE(child.numEnterEvents, 1); |
| QCOMPARE(child.numMouseMoveEvents, 0); |
| |
| child.hide(); |
| child.reset(); |
| child.setMouseTracking(true); |
| child.show(); |
| |
| // Make sure the child gets enter event. |
| // Note that we verify event->button() and event->buttons() |
| // in SELChild::mouseMoveEvent(). |
| QTRY_COMPARE(child.numEnterEvents, 1); |
| QCOMPARE(child.numMouseMoveEvents, 0); |
| |
| // Sending synthetic enter/leave trough the parent's mousePressEvent handler. |
| parent.child = &child; |
| |
| child.hide(); |
| child.reset(); |
| QTest::mouseClick(&parent, Qt::LeftButton); |
| |
| // Make sure the child gets enter event. |
| QTRY_COMPARE(child.numEnterEvents, 1); |
| QCOMPARE(child.numMouseMoveEvents, 0); |
| |
| child.hide(); |
| child.reset(); |
| QTest::keyPress(&parent, Qt::Key_Shift); |
| QTest::mouseClick(&parent, Qt::LeftButton); |
| |
| // Make sure the child gets enter event |
| QTRY_COMPARE(child.numEnterEvents, 1); |
| QCOMPARE(child.numMouseMoveEvents, 0); |
| QTest::keyRelease(&child, Qt::Key_Shift); |
| child.hide(); |
| child.reset(); |
| child.setMouseTracking(false); |
| QTest::mouseClick(&parent, Qt::LeftButton); |
| |
| // Make sure the child gets enter event and no mouse move event. |
| QTRY_COMPARE(child.numEnterEvents, 1); |
| QCOMPARE(child.numMouseMoveEvents, 0); |
| } |
| #endif |
| |
| void tst_QWidget::windowFlags() |
| { |
| QWidget w; |
| const auto baseFlags = w.windowFlags(); |
| w.setWindowFlags(w.windowFlags() | Qt::FramelessWindowHint); |
| QVERIFY(w.windowFlags() & Qt::FramelessWindowHint); |
| w.setWindowFlag(Qt::WindowStaysOnTopHint, true); |
| QCOMPARE(w.windowFlags(), baseFlags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); |
| w.setWindowFlag(Qt::FramelessWindowHint, false); |
| QCOMPARE(w.windowFlags(), baseFlags | Qt::WindowStaysOnTopHint); |
| } |
| |
| void tst_QWidget::initialPosForDontShowOnScreenWidgets() |
| { |
| { // Check default position. |
| const QPoint expectedPos(0, 0); |
| QWidget widget; |
| widget.setAttribute(Qt::WA_DontShowOnScreen); |
| widget.winId(); // Make sure QWidgetPrivate::create is called. |
| QCOMPARE(widget.pos(), expectedPos); |
| QCOMPARE(widget.geometry().topLeft(), expectedPos); |
| } |
| |
| { // Explicitly move to a position. |
| const QPoint expectedPos(100, 100); |
| QWidget widget; |
| widget.setAttribute(Qt::WA_DontShowOnScreen); |
| widget.move(expectedPos); |
| widget.winId(); // Make sure QWidgetPrivate::create is called. |
| QCOMPARE(widget.pos(), expectedPos); |
| QCOMPARE(widget.geometry().topLeft(), expectedPos); |
| } |
| } |
| |
| class MyEvilObject : public QObject |
| { |
| Q_OBJECT |
| public: |
| explicit MyEvilObject(QWidget *widgetToCrash) : QObject(), widget(widgetToCrash) |
| { |
| connect(widget, &QObject::destroyed, this, &MyEvilObject::beEvil); |
| delete widget; |
| } |
| QWidget *widget; |
| |
| private slots: |
| void beEvil(QObject *) { widget->update(0, 0, 150, 150); } |
| }; |
| |
| void tst_QWidget::updateOnDestroyedSignal() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| |
| QWidget *child = new QWidget(&widget); |
| child->resize(m_testWidgetSize); |
| child->setAutoFillBackground(true); |
| child->setPalette(Qt::red); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| |
| // Please do not crash. |
| MyEvilObject evil(child); |
| QTest::qWait(200); |
| } |
| |
| void tst_QWidget::toplevelLineEditFocus() |
| { |
| QLineEdit w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setMinimumWidth(m_testWidgetSize.width()); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| |
| QTRY_COMPARE(QApplication::activeWindow(), static_cast<const QWidget *>(&w)); |
| QTRY_COMPARE(QApplication::focusWidget(), static_cast<const QWidget *>(&w)); |
| } |
| |
| void tst_QWidget::focusWidget_task254563() |
| { |
| //having different visibility for widget is important |
| QWidget top; |
| top.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| top.show(); |
| QWidget container(&top); |
| QWidget *widget = new QWidget(&container); |
| widget->show(); |
| |
| widget->setFocus(); //set focus (will set the focus widget up to the toplevel to be 'widget') |
| container.setFocus(); |
| delete widget; // will call clearFocus but that doesn't help |
| QVERIFY(top.focusWidget() != widget); //dangling pointer |
| } |
| |
| // This test case relies on developer build (AUTOTEST_EXPORT). |
| #ifdef QT_BUILD_INTERNAL |
| void tst_QWidget::destroyBackingStore() |
| { |
| UpdateWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| centerOnScreen(&w); |
| w.reset(); |
| w.show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QApplication::processEvents(); |
| QTRY_VERIFY(w.numPaintEvents > 0); |
| w.reset(); |
| w.update(); |
| qt_widget_private(&w)->topData()->repaintManager.reset(new QWidgetRepaintManager(&w)); |
| |
| w.update(); |
| QApplication::processEvents(); |
| |
| QCOMPARE(w.numPaintEvents, 1); |
| |
| // Check one more time, because the second time around does more caching. |
| w.update(); |
| QApplication::processEvents(); |
| QCOMPARE(w.numPaintEvents, 2); |
| } |
| #endif // QT_BUILD_INTERNAL |
| |
| // Helper function |
| QWidgetRepaintManager* repaintManager(QWidget &widget) |
| { |
| QWidgetRepaintManager *repaintManager = nullptr; |
| #ifdef QT_BUILD_INTERNAL |
| if (QTLWExtra *topExtra = qt_widget_private(&widget)->maybeTopData()) |
| repaintManager = topExtra->repaintManager.get(); |
| #endif |
| return repaintManager; |
| } |
| |
| // Tables of 5000 elements do not make sense on Windows Mobile. |
| void tst_QWidget::rectOutsideCoordinatesLimit_task144779() |
| { |
| #ifndef QT_NO_CURSOR |
| QGuiApplication::setOverrideCursor(Qt::BlankCursor); //keep the cursor out of screen grabs |
| #endif |
| QWidget main(nullptr, Qt::FramelessWindowHint); //don't get confused by the size of the window frame |
| main.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QPalette palette; |
| palette.setColor(QPalette::Window, Qt::red); |
| main.setPalette(palette); |
| |
| QRect desktopDimensions = main.screen()->availableGeometry(); |
| QSize mainSize(400, 400); |
| mainSize = mainSize.boundedTo(desktopDimensions.size()); |
| main.resize(mainSize); |
| |
| QWidget *offsetWidget = new QWidget(&main); |
| offsetWidget->setGeometry(0, -(15000 - mainSize.height()), mainSize.width(), 15000); |
| |
| // big widget is too big for the coordinates, it must be limited by wrect |
| // if wrect is not at the right position because of offsetWidget, bigwidget |
| // is not painted correctly |
| QWidget *bigWidget = new QWidget(offsetWidget); |
| bigWidget->setGeometry(0, 0, mainSize.width(), 50000); |
| palette.setColor(QPalette::Window, Qt::green); |
| bigWidget->setPalette(palette); |
| bigWidget->setAutoFillBackground(true); |
| |
| main.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&main)); |
| |
| QPixmap correct(main.size()); |
| correct.fill(Qt::green); |
| const QPixmap mainPixmap = grabFromWidget(&main, QRect(QPoint(0, 0), QSize(-1, -1))); |
| |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QTRY_COMPARE(mainPixmap.toImage().convertToFormat(QImage::Format_RGB32), |
| correct.toImage().convertToFormat(QImage::Format_RGB32)); |
| #ifndef QT_NO_CURSOR |
| QGuiApplication::restoreOverrideCursor(); |
| #endif |
| } |
| |
| void tst_QWidget::setGraphicsEffect() |
| { |
| // Check that we don't have any effect by default. |
| QScopedPointer<QWidget> widget(new QWidget); |
| widget->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QVERIFY(!widget->graphicsEffect()); |
| |
| // SetGet check. |
| QPointer<QGraphicsEffect> blurEffect = new QGraphicsBlurEffect; |
| widget->setGraphicsEffect(blurEffect); |
| QCOMPARE(widget->graphicsEffect(), static_cast<QGraphicsEffect *>(blurEffect)); |
| |
| // Ensure the existing effect is deleted when setting a new one. |
| QPointer<QGraphicsEffect> shadowEffect = new QGraphicsDropShadowEffect; |
| widget->setGraphicsEffect(shadowEffect); |
| QVERIFY(!blurEffect); |
| QCOMPARE(widget->graphicsEffect(), static_cast<QGraphicsEffect *>(shadowEffect)); |
| blurEffect = new QGraphicsBlurEffect; |
| |
| // Ensure the effect is uninstalled when setting it on a new target. |
| QScopedPointer<QWidget> anotherWidget(new QWidget); |
| anotherWidget->setGraphicsEffect(blurEffect); |
| widget->setGraphicsEffect(blurEffect); |
| QVERIFY(!anotherWidget->graphicsEffect()); |
| QVERIFY(!shadowEffect); |
| |
| // Ensure the existing effect is deleted when deleting the widget. |
| widget.reset(); |
| QVERIFY(!blurEffect); |
| anotherWidget.reset(); |
| |
| // Ensure the effect is uninstalled when deleting it |
| widget.reset(new QWidget); |
| blurEffect = new QGraphicsBlurEffect; |
| widget->setGraphicsEffect(blurEffect); |
| delete blurEffect; |
| QVERIFY(!widget->graphicsEffect()); |
| |
| // Ensure the existing effect is uninstalled and deleted when setting a null effect |
| blurEffect = new QGraphicsBlurEffect; |
| widget->setGraphicsEffect(blurEffect); |
| widget->setGraphicsEffect(nullptr); |
| QVERIFY(!widget->graphicsEffect()); |
| QVERIFY(!blurEffect); |
| } |
| |
| void tst_QWidget::activateWindow() |
| { |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) |
| QSKIP("Window activation is not supported."); |
| |
| // Test case for QTBUG-26711 |
| |
| // Create first mainwindow and set it active |
| QScopedPointer<QMainWindow> mainwindow(new QMainWindow); |
| mainwindow->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QLabel* label = new QLabel(mainwindow.data()); |
| label->setMinimumWidth(m_testWidgetSize.width()); |
| mainwindow->setWindowTitle(QStringLiteral("#1 ") + __FUNCTION__); |
| mainwindow->setCentralWidget(label); |
| mainwindow->move(m_availableTopLeft + QPoint(100, 100)); |
| mainwindow->setVisible(true); |
| mainwindow->activateWindow(); |
| QVERIFY(QTest::qWaitForWindowActive(mainwindow.data())); |
| QVERIFY(mainwindow->isActiveWindow()); |
| |
| // Create second mainwindow and set it active |
| QScopedPointer<QMainWindow> mainwindow2(new QMainWindow); |
| mainwindow2->setWindowTitle(QStringLiteral("#2 ") + __FUNCTION__); |
| QLabel* label2 = new QLabel(mainwindow2.data()); |
| label2->setMinimumWidth(m_testWidgetSize.width()); |
| mainwindow2->setCentralWidget(label2); |
| mainwindow2->move(mainwindow->geometry().bottomLeft() + QPoint(0, 50)); |
| mainwindow2->setVisible(true); |
| mainwindow2->activateWindow(); |
| QCoreApplication::processEvents(); |
| |
| QTRY_VERIFY(!mainwindow->isActiveWindow()); |
| QTRY_VERIFY(mainwindow2->isActiveWindow()); |
| |
| // Revert first mainwindow back to visible active |
| mainwindow->setVisible(true); |
| mainwindow->activateWindow(); |
| QCoreApplication::processEvents(); |
| |
| QTRY_VERIFY(mainwindow->isActiveWindow()); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QTRY_VERIFY(!mainwindow2->isActiveWindow()); |
| } |
| |
| void tst_QWidget::openModal_taskQTBUG_5804() |
| { |
| class Widget : public QWidget |
| { |
| public: |
| Widget(QWidget *parent) : QWidget(parent) {} |
| ~Widget() |
| { |
| QMessageBox msgbox; |
| QTimer::singleShot(10, &msgbox, SLOT(accept())); |
| msgbox.exec(); //open a modal dialog |
| } |
| }; |
| |
| QScopedPointer<QWidget> win(new QWidget); |
| win->resize(m_testWidgetSize); |
| win->setWindowTitle(__FUNCTION__); |
| centerOnScreen(win.data()); |
| |
| new Widget(win.data()); |
| win->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(win.data())); |
| } |
| |
| void tst_QWidget::focusProxyAndInputMethods() |
| { |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) |
| QSKIP("Window activation is not supported."); |
| QScopedPointer<QWidget> toplevel(new QWidget(nullptr, Qt::X11BypassWindowManagerHint)); |
| toplevel->setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| toplevel->resize(200, 200); |
| toplevel->setAttribute(Qt::WA_InputMethodEnabled, true); |
| |
| QWidget *child = new QWidget(toplevel.data()); |
| child->setFocusProxy(toplevel.data()); |
| child->setAttribute(Qt::WA_InputMethodEnabled, true); |
| |
| toplevel->setFocusPolicy(Qt::WheelFocus); |
| child->setFocusPolicy(Qt::WheelFocus); |
| |
| QVERIFY(!child->hasFocus()); |
| QVERIFY(!toplevel->hasFocus()); |
| |
| toplevel->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(toplevel.data())); |
| QApplication::setActiveWindow(toplevel.data()); |
| QVERIFY(QTest::qWaitForWindowActive(toplevel.data())); |
| QVERIFY(toplevel->hasFocus()); |
| QVERIFY(child->hasFocus()); |
| QCOMPARE(qApp->focusObject(), toplevel.data()); |
| } |
| |
| #ifdef QT_BUILD_INTERNAL |
| class scrollWidgetWBS : public QWidget |
| { |
| public: |
| void deleteBackingStore() |
| { |
| static_cast<QWidgetPrivate*>(d_ptr.data())->topData()->repaintManager.reset(nullptr); |
| } |
| void enableBackingStore() |
| { |
| if (!static_cast<QWidgetPrivate*>(d_ptr.data())->maybeRepaintManager()) { |
| static_cast<QWidgetPrivate*>(d_ptr.data())->topData()->repaintManager.reset(new QWidgetRepaintManager(this)); |
| static_cast<QWidgetPrivate*>(d_ptr.data())->invalidateBackingStore(this->rect()); |
| repaint(); |
| } |
| } |
| }; |
| #endif |
| |
| // Test case relies on developer build (AUTOTEST_EXPORT). |
| #ifdef QT_BUILD_INTERNAL |
| void tst_QWidget::scrollWithoutBackingStore() |
| { |
| scrollWidgetWBS scrollable; |
| scrollable.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| scrollable.resize(200, 200); |
| QLabel child(QString("@"),&scrollable); |
| child.resize(50,50); |
| scrollable.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&scrollable)); |
| scrollable.scroll(50,50); |
| QCOMPARE(child.pos(),QPoint(50,50)); |
| scrollable.deleteBackingStore(); |
| scrollable.scroll(-25,-25); |
| QCOMPARE(child.pos(),QPoint(25,25)); |
| scrollable.enableBackingStore(); |
| QCOMPARE(child.pos(),QPoint(25,25)); |
| } |
| #endif |
| |
| void tst_QWidget::taskQTBUG_7532_tabOrderWithFocusProxy() |
| { |
| QWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setFocusPolicy(Qt::TabFocus); |
| QWidget *fp = new QWidget(&w); |
| fp->setFocusPolicy(Qt::TabFocus); |
| w.setFocusProxy(fp); |
| QWidget::setTabOrder(&w, fp); |
| |
| // In debug mode, no assertion failure means it's alright. |
| } |
| |
| void tst_QWidget::movedAndResizedAttributes() |
| { |
| // Use Qt::Tool as fully decorated windows have a minimum width of 160 on |
| QWidget w(nullptr, Qt::Tool); |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.show(); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setWindowState(Qt::WindowFullScreen); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setWindowState(Qt::WindowMaximized); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.setWindowState(Qt::WindowMinimized); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.showNormal(); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.showMaximized(); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.showFullScreen(); |
| |
| QVERIFY(!w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.showNormal(); |
| w.move(10,10); |
| QVERIFY(w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(!w.testAttribute(Qt::WA_Resized)); |
| |
| w.resize(100, 100); |
| QVERIFY(w.testAttribute(Qt::WA_Moved)); |
| QVERIFY(w.testAttribute(Qt::WA_Resized)); |
| } |
| |
| void tst_QWidget::childAt() |
| { |
| QWidget parent(nullptr, Qt::FramelessWindowHint); |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.resize(200, 200); |
| |
| QWidget *child = new QWidget(&parent); |
| child->setPalette(Qt::red); |
| child->setAutoFillBackground(true); |
| child->setGeometry(20, 20, 160, 160); |
| |
| QWidget *grandChild = new QWidget(child); |
| grandChild->setPalette(Qt::blue); |
| grandChild->setAutoFillBackground(true); |
| grandChild->setGeometry(-20, -20, 220, 220); |
| |
| QVERIFY(!parent.childAt(19, 19)); |
| QVERIFY(!parent.childAt(180, 180)); |
| QCOMPARE(parent.childAt(20, 20), grandChild); |
| QCOMPARE(parent.childAt(179, 179), grandChild); |
| |
| grandChild->setAttribute(Qt::WA_TransparentForMouseEvents); |
| QCOMPARE(parent.childAt(20, 20), child); |
| QCOMPARE(parent.childAt(179, 179), child); |
| grandChild->setAttribute(Qt::WA_TransparentForMouseEvents, false); |
| |
| child->setMask(QRect(50, 50, 60, 60)); |
| |
| QVERIFY(!parent.childAt(69, 69)); |
| QVERIFY(!parent.childAt(130, 130)); |
| QCOMPARE(parent.childAt(70, 70), grandChild); |
| QCOMPARE(parent.childAt(129, 129), grandChild); |
| |
| child->setAttribute(Qt::WA_MouseNoMask); |
| QCOMPARE(parent.childAt(69, 69), grandChild); |
| QCOMPARE(parent.childAt(130, 130), grandChild); |
| child->setAttribute(Qt::WA_MouseNoMask, false); |
| |
| grandChild->setAttribute(Qt::WA_TransparentForMouseEvents); |
| QCOMPARE(parent.childAt(70, 70), child); |
| QCOMPARE(parent.childAt(129, 129), child); |
| grandChild->setAttribute(Qt::WA_TransparentForMouseEvents, false); |
| |
| grandChild->setMask(QRect(80, 80, 40, 40)); |
| |
| QCOMPARE(parent.childAt(79, 79), child); |
| QCOMPARE(parent.childAt(120, 120), child); |
| QCOMPARE(parent.childAt(80, 80), grandChild); |
| QCOMPARE(parent.childAt(119, 119), grandChild); |
| |
| grandChild->setAttribute(Qt::WA_MouseNoMask); |
| |
| QCOMPARE(parent.childAt(79, 79), grandChild); |
| QCOMPARE(parent.childAt(120, 120), grandChild); |
| } |
| |
| #ifdef Q_OS_OSX |
| |
| void tst_QWidget::taskQTBUG_11373() |
| { |
| QSKIP("QTBUG-52974"); |
| |
| QScopedPointer<QMainWindow> myWindow(new QMainWindow); |
| QWidget * center = new QWidget(); |
| myWindow -> setCentralWidget(center); |
| QWidget * drawer = new QWidget(myWindow.data(), Qt::Drawer); |
| drawer -> hide(); |
| QCOMPARE(drawer->isVisible(), false); |
| myWindow -> show(); |
| myWindow -> raise(); |
| // The drawer shouldn't be visible now. |
| QCOMPARE(drawer->isVisible(), false); |
| myWindow -> setWindowState(Qt::WindowFullScreen); |
| myWindow -> setWindowState(Qt::WindowNoState); |
| // The drawer should still not be visible, since we haven't shown it. |
| QCOMPARE(drawer->isVisible(), false); |
| } |
| |
| #endif |
| |
| void tst_QWidget::taskQTBUG_17333_ResizeInfiniteRecursion() |
| { |
| QTableView tb; |
| tb.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| const char *s = "border: 1px solid;"; |
| tb.setStyleSheet(s); |
| tb.show(); |
| |
| QVERIFY(QTest::qWaitForWindowExposed(&tb)); |
| tb.setGeometry(QRect(100, 100, 0, 100)); |
| // No crash, it works. |
| } |
| |
| void tst_QWidget::nativeChildFocus() |
| { |
| QWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setMinimumWidth(m_testWidgetSize.width()); |
| w.setWindowTitle(__FUNCTION__); |
| QLayout *layout = new QVBoxLayout; |
| w.setLayout(layout); |
| QLineEdit *p1 = new QLineEdit; |
| QLineEdit *p2 = new QLineEdit; |
| layout->addWidget(p1); |
| layout->addWidget(p2); |
| p1->setObjectName("p1"); |
| p2->setObjectName("p2"); |
| centerOnScreen(&w); |
| w.show(); |
| w.activateWindow(); |
| p1->setFocus(); |
| p1->setAttribute(Qt::WA_NativeWindow); |
| p2->setAttribute(Qt::WA_NativeWindow); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| |
| QCOMPARE(QApplication::activeWindow(), &w); |
| QCOMPARE(QApplication::focusWidget(), static_cast<QWidget*>(p1)); |
| } |
| |
| static bool lenientCompare(const QPixmap &actual, const QPixmap &expected) |
| { |
| QImage expectedImage = expected.toImage().convertToFormat(QImage::Format_RGB32); |
| QImage actualImage = actual.toImage().convertToFormat(QImage::Format_RGB32); |
| |
| if (expectedImage.size() != actualImage.size()) { |
| qWarning("Image size comparison failed: expected: %dx%d, got %dx%d", |
| expectedImage.size().width(), expectedImage.size().height(), |
| actualImage.size().width(), actualImage.size().height()); |
| return false; |
| } |
| |
| const int size = actual.width() * actual.height(); |
| const int threshold = QPixmap::defaultDepth() == 16 ? 10 : 2; |
| |
| auto a = reinterpret_cast<const QRgb *>(actualImage.bits()); |
| auto e = reinterpret_cast<const QRgb *>(expectedImage.bits()); |
| for (int i = 0; i < size; ++i) { |
| const QColor ca(a[i]); |
| const QColor ce(e[i]); |
| if (qAbs(ca.red() - ce.red()) > threshold |
| || qAbs(ca.green() - ce.green()) > threshold |
| || qAbs(ca.blue() - ce.blue()) > threshold) { |
| qWarning("Color mismatch at pixel #%d: Expected: %d,%d,%d, got %d,%d,%d", |
| i, ce.red(), ce.green(), ce.blue(), ca.red(), ca.green(), ca.blue()); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void tst_QWidget::grab() |
| { |
| for (int opaque = 0; opaque < 2; ++opaque) { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QImage image(128, 128, opaque ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied); |
| for (int row = 0; row < image.height(); ++row) { |
| QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(row)); |
| for (int col = 0; col < image.width(); ++col) |
| line[col] = qRgba(QRandomGenerator::global()->bounded(255), row, col, opaque ? 255 : 127); |
| } |
| |
| QPalette pal = widget.palette(); |
| pal.setBrush(QPalette::Window, QBrush(image)); |
| widget.setPalette(pal); |
| widget.resize(128, 128); |
| |
| QPixmap expected(64, 64); |
| if (!opaque) |
| expected.fill(Qt::transparent); |
| |
| QPainter p(&expected); |
| p.translate(-64, -64); |
| p.drawTiledPixmap(0, 0, 128, 128, pal.brush(QPalette::Window).texture(), 0, 0); |
| p.end(); |
| |
| QPixmap actual = grabFromWidget(&widget, QRect(64, 64, 64, 64)); |
| QVERIFY(lenientCompare(actual, expected)); |
| |
| actual = grabFromWidget(&widget, QRect(64, 64, -1, -1)); |
| QVERIFY(lenientCompare(actual, expected)); |
| |
| // Make sure a widget that is not yet shown is grabbed correctly. |
| QTreeWidget widget2; |
| actual = widget2.grab(QRect()); |
| widget2.show(); |
| expected = widget2.grab(QRect()); |
| |
| QVERIFY(lenientCompare(actual, expected)); |
| } |
| } |
| |
| /* grabMouse() tests whether mouse grab for a widget without window handle works. |
| * It creates a top level widget with another nested widget inside. The inner widget grabs |
| * the mouse and a series of mouse presses moving over the top level's window is simulated. |
| * Only the inner widget should receive events. */ |
| |
| static inline QString mouseEventLogEntry(const QString &objectName, QEvent::Type t, const QPoint &p, Qt::MouseButtons b) |
| { |
| QString result; |
| QDebug(&result).nospace() << objectName << " Mouse event " << t << " at " << p << " buttons " << b; |
| return result; |
| } |
| |
| class GrabLoggerWidget : public QWidget |
| { |
| public: |
| explicit GrabLoggerWidget(QStringList *log, QWidget *parent = nullptr) : QWidget(parent), m_log(log) {} |
| |
| protected: |
| bool event(QEvent *e) override |
| { |
| switch (e->type()) { |
| case QEvent::MouseButtonPress: |
| case QEvent::MouseMove: |
| case QEvent::MouseButtonRelease: { |
| QMouseEvent *me = static_cast<QMouseEvent *>(e); |
| m_log->push_back(mouseEventLogEntry(objectName(), me->type(), me->pos(), me->buttons())); |
| me->accept(); |
| return true; |
| } |
| default: |
| break; |
| } |
| return QWidget::event(e); |
| } |
| private: |
| QStringList *m_log; |
| }; |
| |
| void tst_QWidget::grabMouse() |
| { |
| QStringList log; |
| GrabLoggerWidget w(&log); |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setObjectName(QLatin1String("tst_qwidget_grabMouse")); |
| w.setWindowTitle(w.objectName()); |
| QLayout *layout = new QVBoxLayout(&w); |
| layout->setContentsMargins(50, 50, 50, 50); |
| GrabLoggerWidget *grabber = new GrabLoggerWidget(&log, &w); |
| const QString grabberObjectName = QLatin1String("tst_qwidget_grabMouse_grabber"); |
| grabber->setObjectName(grabberObjectName); |
| grabber->setMinimumSize(m_testWidgetSize); |
| layout->addWidget(grabber); |
| centerOnScreen(&w); |
| w.show(); |
| QApplication::setActiveWindow(&w); |
| QVERIFY(QTest::qWaitForWindowActive(&w)); |
| |
| QStringList expectedLog; |
| QPoint mousePos = QPoint(w.width() / 2, 10); |
| QTest::mouseMove(w.windowHandle(), mousePos); |
| grabber->grabMouse(); |
| const int step = w.height() / 5; |
| for ( ; mousePos.y() < w.height() ; mousePos.ry() += step) { |
| QTest::mouseClick(w.windowHandle(), Qt::LeftButton, Qt::KeyboardModifiers(), mousePos); |
| // Events should go to the grabber child using its coordinates. |
| const QPoint expectedPos = grabber->mapFromParent(mousePos); |
| expectedLog.push_back(mouseEventLogEntry(grabberObjectName, QEvent::MouseButtonPress, expectedPos, Qt::LeftButton)); |
| expectedLog.push_back(mouseEventLogEntry(grabberObjectName, QEvent::MouseButtonRelease, expectedPos, Qt::NoButton)); |
| } |
| grabber->releaseMouse(); |
| QCOMPARE(log, expectedLog); |
| } |
| |
| void tst_QWidget::grabKeyboard() |
| { |
| QWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.setObjectName(QLatin1String("tst_qwidget_grabKeyboard")); |
| w.setWindowTitle(w.objectName()); |
| QLayout *layout = new QVBoxLayout(&w); |
| QLineEdit *grabber = new QLineEdit(&w); |
| grabber->setMinimumWidth(m_testWidgetSize.width()); |
| layout->addWidget(grabber); |
| QLineEdit *nonGrabber = new QLineEdit(&w); |
| nonGrabber->setMinimumWidth(m_testWidgetSize.width()); |
| layout->addWidget(nonGrabber); |
| centerOnScreen(&w); |
| w.show(); |
| QApplication::setActiveWindow(&w); |
| QVERIFY(QTest::qWaitForWindowActive(&w)); |
| nonGrabber->setFocus(); |
| grabber->grabKeyboard(); |
| QTest::keyClick(w.windowHandle(), Qt::Key_A); |
| grabber->releaseKeyboard(); |
| QCOMPARE(grabber->text().toLower(), QStringLiteral("a")); |
| QVERIFY2(nonGrabber->text().isEmpty(), qPrintable(nonGrabber->text())); |
| } |
| |
| class TouchMouseWidget : public QWidget { |
| public: |
| explicit TouchMouseWidget(QWidget *parent = nullptr) : QWidget(parent) |
| { |
| resize(200, 200); |
| } |
| |
| void setAcceptTouch(bool accept) |
| { |
| m_acceptTouch = accept; |
| setAttribute(Qt::WA_AcceptTouchEvents, accept); |
| } |
| |
| void setAcceptMouse(bool accept) |
| { |
| m_acceptMouse = accept; |
| } |
| |
| protected: |
| bool event(QEvent *e) override |
| { |
| switch (e->type()) { |
| case QEvent::TouchBegin: |
| case QEvent::TouchUpdate: |
| case QEvent::TouchEnd: |
| if (e->type() == QEvent::TouchBegin) |
| ++m_touchBeginCount; |
| else if (e->type() == QEvent::TouchUpdate) |
| ++m_touchUpdateCount; |
| else if (e->type() == QEvent::TouchEnd) |
| ++m_touchEndCount; |
| ++m_touchEventCount; |
| if (m_acceptTouch) |
| e->accept(); |
| else |
| e->ignore(); |
| return true; |
| case QEvent::Gesture: |
| ++m_gestureEventCount; |
| return true; |
| |
| case QEvent::MouseButtonPress: |
| case QEvent::MouseMove: |
| case QEvent::MouseButtonRelease: |
| ++m_mouseEventCount; |
| m_lastMouseEventPos = static_cast<QMouseEvent *>(e)->localPos(); |
| if (m_acceptMouse) |
| e->accept(); |
| else |
| e->ignore(); |
| return true; |
| |
| default: |
| return QWidget::event(e); |
| } |
| } |
| |
| public: |
| int m_touchBeginCount = 0; |
| int m_touchUpdateCount = 0; |
| int m_touchEndCount = 0; |
| int m_touchEventCount = 0; |
| int m_gestureEventCount = 0; |
| bool m_acceptTouch = false; |
| int m_mouseEventCount = 0; |
| bool m_acceptMouse = true; |
| QPointF m_lastMouseEventPos; |
| }; |
| |
| void tst_QWidget::touchEventSynthesizedMouseEvent() |
| { |
| { |
| // Simple case, we ignore the touch events, we get mouse events instead |
| TouchMouseWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle())); |
| QCOMPARE(widget.m_touchEventCount, 0); |
| QCOMPARE(widget.m_mouseEventCount, 0); |
| |
| QTest::touchEvent(&widget, m_touchScreen).press(0, QPoint(10, 10), &widget); |
| QCOMPARE(widget.m_touchEventCount, 0); |
| QCOMPARE(widget.m_mouseEventCount, 1); |
| QCOMPARE(widget.m_lastMouseEventPos, QPointF(10, 10)); |
| QTest::touchEvent(&widget, m_touchScreen).move(0, QPoint(15, 15), &widget); |
| QCOMPARE(widget.m_touchEventCount, 0); |
| QCOMPARE(widget.m_mouseEventCount, 2); |
| QCOMPARE(widget.m_lastMouseEventPos, QPointF(15, 15)); |
| QTest::touchEvent(&widget, m_touchScreen).release(0, QPoint(20, 20), &widget); |
| QCOMPARE(widget.m_touchEventCount, 0); |
| QCOMPARE(widget.m_mouseEventCount, 4); // we receive extra mouse move event |
| QCOMPARE(widget.m_lastMouseEventPos, QPointF(20, 20)); |
| } |
| |
| { |
| // We accept the touch events, no mouse event is generated |
| TouchMouseWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.setAcceptTouch(true); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(widget.windowHandle())); |
| QCOMPARE(widget.m_touchEventCount, 0); |
| QCOMPARE(widget.m_mouseEventCount, 0); |
| |
| QTest::touchEvent(&widget, m_touchScreen).press(0, QPoint(10, 10), &widget); |
| QCOMPARE(widget.m_touchEventCount, 1); |
| QCOMPARE(widget.m_mouseEventCount, 0); |
| QTest::touchEvent(&widget, m_touchScreen).move(0, QPoint(15, 15), &widget); |
| QCOMPARE(widget.m_touchEventCount, 2); |
| QCOMPARE(widget.m_mouseEventCount, 0); |
| QTest::touchEvent(&widget, m_touchScreen).release(0, QPoint(20, 20), &widget); |
| QCOMPARE(widget.m_touchEventCount, 3); |
| QCOMPARE(widget.m_mouseEventCount, 0); |
| } |
| |
| { |
| // Parent accepts touch events, child ignore both mouse and touch |
| // We should see propagation of the TouchBegin |
| TouchMouseWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.setAcceptTouch(true); |
| TouchMouseWidget child(&parent); |
| child.move(5, 5); |
| child.setAcceptMouse(false); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle())); |
| QCOMPARE(parent.m_touchEventCount, 0); |
| QCOMPARE(parent.m_mouseEventCount, 0); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_mouseEventCount, 0); |
| |
| QTest::touchEvent(parent.window(), m_touchScreen).press(0, QPoint(10, 10), &child); |
| QCOMPARE(parent.m_touchEventCount, 1); |
| QCOMPARE(parent.m_mouseEventCount, 0); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_mouseEventCount, 0); |
| } |
| |
| { |
| // Parent accepts mouse events, child ignore both mouse and touch |
| // We should see propagation of the TouchBegin into a MouseButtonPress |
| TouchMouseWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| TouchMouseWidget child(&parent); |
| const QPoint childPos(5, 5); |
| child.move(childPos); |
| child.setAcceptMouse(false); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(parent.windowHandle())); |
| QCOMPARE(parent.m_touchEventCount, 0); |
| QCOMPARE(parent.m_mouseEventCount, 0); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_mouseEventCount, 0); |
| |
| const QPoint touchPos(20, 20); |
| QTest::touchEvent(parent.window(), m_touchScreen).press(0, touchPos, &child); |
| QCOMPARE(parent.m_touchEventCount, 0); |
| QCOMPARE(parent.m_mouseEventCount, 1); |
| QCOMPARE(parent.m_lastMouseEventPos, childPos + touchPos); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_mouseEventCount, 1); // Attempt at mouse event before propagation |
| QCOMPARE(child.m_lastMouseEventPos, touchPos); |
| } |
| } |
| |
| void tst_QWidget::touchUpdateOnNewTouch() |
| { |
| TouchMouseWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.setAcceptTouch(true); |
| QVBoxLayout *layout = new QVBoxLayout; |
| layout->addWidget(new QWidget); |
| widget.setLayout(layout); |
| widget.show(); |
| |
| QWindow* window = widget.windowHandle(); |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| QCOMPARE(widget.m_touchBeginCount, 0); |
| QCOMPARE(widget.m_touchUpdateCount, 0); |
| QCOMPARE(widget.m_touchEndCount, 0); |
| QTest::touchEvent(window, m_touchScreen).press(0, QPoint(20, 20), window); |
| QCOMPARE(widget.m_touchBeginCount, 1); |
| QCOMPARE(widget.m_touchUpdateCount, 0); |
| QCOMPARE(widget.m_touchEndCount, 0); |
| QTest::touchEvent(window, m_touchScreen).move(0, QPoint(25, 25), window); |
| QCOMPARE(widget.m_touchBeginCount, 1); |
| QCOMPARE(widget.m_touchUpdateCount, 1); |
| QCOMPARE(widget.m_touchEndCount, 0); |
| QTest::touchEvent(window, m_touchScreen).stationary(0).press(1, QPoint(40, 40), window); |
| QCOMPARE(widget.m_touchBeginCount, 1); |
| QCOMPARE(widget.m_touchUpdateCount, 2); |
| QCOMPARE(widget.m_touchEndCount, 0); |
| QTest::touchEvent(window, m_touchScreen).stationary(1).release(0, QPoint(25, 25), window); |
| QCOMPARE(widget.m_touchBeginCount, 1); |
| QCOMPARE(widget.m_touchUpdateCount, 3); |
| QCOMPARE(widget.m_touchEndCount, 0); |
| QTest::touchEvent(window, m_touchScreen).release(1, QPoint(40, 40), window); |
| QCOMPARE(widget.m_touchBeginCount, 1); |
| QCOMPARE(widget.m_touchUpdateCount, 3); |
| QCOMPARE(widget.m_touchEndCount, 1); |
| } |
| |
| void tst_QWidget::touchEventsForGesturePendingWidgets() |
| { |
| TouchMouseWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| TouchMouseWidget child(&parent); |
| parent.grabGesture(Qt::TapAndHoldGesture); |
| parent.show(); |
| |
| QWindow* window = parent.windowHandle(); |
| QVERIFY(QTest::qWaitForWindowExposed(window)); |
| QTest::qWait(500); // needed for QApplication::topLevelAt(), which is used by QGestureManager |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_gestureEventCount, 0); |
| QCOMPARE(parent.m_touchEventCount, 0); |
| QCOMPARE(parent.m_gestureEventCount, 0); |
| QTest::touchEvent(window, m_touchScreen).press(0, QPoint(20, 20), window); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_gestureEventCount, 0); |
| QCOMPARE(parent.m_touchBeginCount, 1); // QTapAndHoldGestureRecognizer::create() sets Qt::WA_AcceptTouchEvents |
| QCOMPARE(parent.m_touchUpdateCount, 0); |
| QCOMPARE(parent.m_touchEndCount, 0); |
| QCOMPARE(parent.m_gestureEventCount, 0); |
| QTest::touchEvent(window, m_touchScreen).move(0, QPoint(25, 25), window); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_gestureEventCount, 0); |
| QCOMPARE(parent.m_touchBeginCount, 1); |
| QCOMPARE(parent.m_touchUpdateCount, 0); |
| QCOMPARE(parent.m_touchEndCount, 0); |
| QCOMPARE(parent.m_gestureEventCount, 0); |
| QTest::qWait(1000); |
| QTest::touchEvent(window, m_touchScreen).release(0, QPoint(25, 25), window); |
| QCOMPARE(child.m_touchEventCount, 0); |
| QCOMPARE(child.m_gestureEventCount, 0); |
| QCOMPARE(parent.m_touchBeginCount, 1); |
| QCOMPARE(parent.m_touchUpdateCount, 0); |
| QCOMPARE(parent.m_touchEndCount, 0); |
| QVERIFY(parent.m_gestureEventCount > 0); |
| } |
| |
| void tst_QWidget::styleSheetPropagation() |
| { |
| QTableView tw; |
| tw.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| tw.setStyleSheet("background-color: red;"); |
| for (QObject *child : tw.children()) { |
| if (QWidget *w = qobject_cast<QWidget *>(child)) |
| QCOMPARE(w->style(), tw.style()); |
| } |
| } |
| |
| class DestroyTester : public QObject |
| { |
| Q_OBJECT |
| public: |
| explicit DestroyTester(QObject *parent = nullptr) : QObject(parent) { parentDestroyed = 0; } |
| static int parentDestroyed; |
| public slots: |
| void parentDestroyedSlot() { |
| ++parentDestroyed; |
| } |
| }; |
| |
| int DestroyTester::parentDestroyed = 0; |
| |
| void tst_QWidget::destroyedSignal() |
| { |
| { |
| QWidget *w = new QWidget; |
| DestroyTester *t = new DestroyTester(w); |
| connect(w, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete w; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| } |
| |
| { |
| QWidget *w = new QWidget; |
| DestroyTester *t = new DestroyTester(w); |
| connect(w, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| w->blockSignals(true); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete w; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| } |
| |
| { |
| QObject *o = new QWidget; |
| DestroyTester *t = new DestroyTester(o); |
| connect(o, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete o; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| } |
| |
| { |
| QObject *o = new QWidget; |
| auto t = new DestroyTester; |
| connect(o, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| o->blockSignals(true); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete o; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| } |
| |
| { |
| QWidget *w = new QWidget; |
| auto t = new DestroyTester; |
| connect(w, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete w; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| delete t; |
| } |
| |
| { |
| QWidget *w = new QWidget; |
| auto t = new DestroyTester; |
| connect(w, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| w->blockSignals(true); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete w; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| delete t; |
| } |
| |
| { |
| QObject *o = new QWidget; |
| auto t = new DestroyTester; |
| connect(o, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete o; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| delete t; |
| } |
| |
| { |
| QObject *o = new QWidget; |
| auto t = new DestroyTester; |
| connect(o, &QObject::destroyed, t, &DestroyTester::parentDestroyedSlot); |
| o->blockSignals(true); |
| QCOMPARE(DestroyTester::parentDestroyed, 0); |
| delete o; |
| QCOMPARE(DestroyTester::parentDestroyed, 1); |
| delete t; |
| } |
| |
| } |
| |
| #ifndef QT_NO_CURSOR |
| void tst_QWidget::underMouse() |
| { |
| // Move the mouse cursor to a safe location |
| QCursor::setPos(m_safeCursorPos); |
| |
| ColorWidget topLevelWidget(nullptr, Qt::FramelessWindowHint, Qt::blue); |
| topLevelWidget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| ColorWidget childWidget1(&topLevelWidget, Qt::Widget, Qt::yellow); |
| ColorWidget childWidget2(&topLevelWidget, Qt::Widget, Qt::black); |
| ColorWidget popupWidget(nullptr, Qt::Popup, Qt::green); |
| |
| topLevelWidget.setObjectName("topLevelWidget"); |
| childWidget1.setObjectName("childWidget1"); |
| childWidget2.setObjectName("childWidget2"); |
| popupWidget.setObjectName("popupWidget"); |
| |
| topLevelWidget.setGeometry(100, 100, 300, 300); |
| childWidget1.setGeometry(20, 20, 100, 100); |
| childWidget2.setGeometry(20, 120, 100, 100); |
| popupWidget.setGeometry(50, 100, 50, 50); |
| |
| topLevelWidget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&topLevelWidget)); |
| QWindow *window = topLevelWidget.windowHandle(); |
| |
| QPoint outsideWindowPoint(30, -10); |
| QPoint inWindowPoint(30, 10); |
| QPoint child1Point(30, 50); |
| QPoint child2PointA(30, 150); |
| QPoint child2PointB(31, 151); |
| |
| // Outside window |
| QTest::mouseMove(window, outsideWindowPoint); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| |
| // Enter window, outside children |
| // Note: QTest::mouseMove will not generate enter events for windows, so send one explicitly |
| QWindowSystemInterface::handleEnterEvent(window, inWindowPoint, window->mapToGlobal(inWindowPoint)); |
| QTest::mouseMove(window, inWindowPoint); |
| QVERIFY(topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| |
| // In childWidget1 |
| QTest::mouseMove(window, child1Point); |
| QVERIFY(topLevelWidget.underMouse()); |
| QVERIFY(childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| |
| // In childWidget2 |
| QTest::mouseMove(window, child2PointA); |
| QVERIFY(topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(childWidget2.underMouse()); |
| |
| topLevelWidget.resetCounts(); |
| childWidget1.resetCounts(); |
| childWidget2.resetCounts(); |
| popupWidget.resetCounts(); |
| |
| // Throw up a popup window |
| popupWidget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&popupWidget)); |
| QWindow *popupWindow = popupWidget.windowHandle(); |
| QVERIFY(popupWindow); |
| QCOMPARE(QApplication::activePopupWidget(), &popupWidget); |
| |
| // Send an artificial leave event for window, as it won't get generated automatically |
| // due to cursor not actually being over the window. |
| QWindowSystemInterface::handleLeaveEvent(window); |
| QApplication::processEvents(); |
| |
| // If there is an active popup, undermouse should not be reported (QTBUG-27478), |
| // but opening a popup causes leave for widgets under mouse. |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(!popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 1); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 1); |
| topLevelWidget.resetCounts(); |
| childWidget2.resetCounts(); |
| |
| // Moving around while popup active should not change undermouse or cause |
| // enter and leave events for widgets. |
| QTest::mouseMove(popupWindow, popupWindow->mapFromGlobal(window->mapToGlobal(child2PointB))); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(!popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| |
| QTest::mouseMove(popupWindow, popupWindow->mapFromGlobal(window->mapToGlobal(inWindowPoint))); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(!popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| |
| QTest::mouseMove(popupWindow, popupWindow->mapFromGlobal(window->mapToGlobal(child1Point))); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(!popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| |
| // Note: Mouse moving off-application while there is an active popup cannot be simulated |
| // without actually moving the cursor so it is not tested. |
| |
| // Mouse enters popup, should cause enter to popup. |
| // Once again, need to create artificial enter event. |
| const QPoint popupCenter = popupWindow->geometry().center(); |
| QWindowSystemInterface::handleEnterEvent(popupWindow, popupWindow->mapFromGlobal(popupCenter), popupCenter); |
| QTest::mouseMove(popupWindow, popupCenter); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 1); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| popupWidget.resetCounts(); |
| |
| // Mouse moves around inside popup, no changes |
| QTest::mouseMove(popupWindow, QPoint(5, 5)); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 0); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| |
| // Mouse leaves popup and enters topLevelWidget, should cause leave for popup |
| // but no enter to topLevelWidget. |
| #ifdef Q_OS_DARWIN |
| // Artificial leave event needed for Cocoa. |
| QWindowSystemInterface::handleLeaveEvent(popupWindow); |
| #endif |
| QTest::mouseMove(popupWindow, popupWindow->mapFromGlobal(window->mapToGlobal(inWindowPoint))); |
| QApplication::processEvents(); |
| QVERIFY(!topLevelWidget.underMouse()); |
| QVERIFY(!childWidget1.underMouse()); |
| QVERIFY(!childWidget2.underMouse()); |
| QVERIFY(!popupWidget.underMouse()); |
| QCOMPARE(popupWidget.enters, 0); |
| QCOMPARE(popupWidget.leaves, 1); |
| QCOMPARE(topLevelWidget.enters, 0); |
| QCOMPARE(topLevelWidget.leaves, 0); |
| QCOMPARE(childWidget1.enters, 0); |
| QCOMPARE(childWidget1.leaves, 0); |
| QCOMPARE(childWidget2.enters, 0); |
| QCOMPARE(childWidget2.leaves, 0); |
| } |
| |
| class EnterTestModalDialog : public QDialog |
| { |
| Q_OBJECT |
| public: |
| EnterTestModalDialog() |
| { |
| setGeometry(100, 300, 150, 100); |
| button = new QPushButton(this); |
| button->setGeometry(10, 10, 50, 30); |
| } |
| |
| QPushButton *button; |
| }; |
| |
| class EnterTestMainDialog : public QDialog |
| { |
| Q_OBJECT |
| public: |
| |
| public slots: |
| void buttonPressed() |
| { |
| qApp->installEventFilter(this); |
| modal = new EnterTestModalDialog(); |
| QTimer::singleShot(2000, modal, SLOT(close())); // Failsafe |
| QTimer::singleShot(100, this, SLOT(doMouseMoves())); |
| modal->exec(); |
| delete modal; |
| modal = nullptr; |
| } |
| |
| void doMouseMoves() |
| { |
| QPoint point1(15, 15); |
| QPoint point2(15, 20); |
| QPoint point3(20, 20); |
| QWindow *window = modal->windowHandle(); |
| const QPoint nativePoint1 = QHighDpi::toNativePixels(point1, window->screen()); |
| QWindowSystemInterface::handleEnterEvent(window, nativePoint1); |
| QTest::mouseMove(window, point1); |
| QTest::mouseMove(window, point2); |
| QTest::mouseMove(window, point3); |
| modal->close(); |
| } |
| |
| bool eventFilter(QObject *o, QEvent *e) override |
| { |
| switch (e->type()) { |
| case QEvent::Enter: |
| if (modal && modal->button && o == modal->button) |
| enters++; |
| break; |
| default: |
| break; |
| } |
| return QDialog::eventFilter(o, e); |
| } |
| |
| public: |
| EnterTestModalDialog *modal = nullptr; |
| int enters = 0; |
| }; |
| |
| // A modal dialog launched by clicking a button should not trigger excess enter events |
| // when mousing over it. |
| void tst_QWidget::taskQTBUG_27643_enterEvents() |
| { |
| #ifdef Q_OS_OSX |
| QSKIP("QTBUG-52974: this test can crash!"); |
| #endif |
| // Move the mouse cursor to a safe location so it won't interfere |
| QCursor::setPos(m_safeCursorPos); |
| |
| EnterTestMainDialog dialog; |
| dialog.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QPushButton button(&dialog); |
| |
| connect(&button, &QAbstractButton::clicked, &dialog, &EnterTestMainDialog::buttonPressed); |
| |
| dialog.setGeometry(100, 100, 150, 100); |
| button.setGeometry(10, 10, 100, 50); |
| dialog.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&dialog)); |
| |
| QWindow *window = dialog.windowHandle(); |
| QPoint overButton(25, 25); |
| |
| QWindowSystemInterface::handleEnterEvent(window, overButton, window->mapToGlobal(overButton)); |
| QTest::mouseMove(window, overButton); |
| QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), overButton, 0); |
| |
| // Modal dialog opened in EnterTestMainDialog::buttonPressed()... |
| |
| // Must only register only single enter on modal dialog's button after all said and done |
| QCOMPARE(dialog.enters, 1); |
| } |
| #endif // QT_NO_CURSOR |
| |
| class KeyboardWidget : public QWidget |
| { |
| public: |
| using QWidget::QWidget; |
| void mousePressEvent(QMouseEvent* ev) override |
| { |
| m_modifiers = ev->modifiers(); |
| m_appModifiers = QApplication::keyboardModifiers(); |
| ++m_eventCounter; |
| } |
| Qt::KeyboardModifiers m_modifiers; |
| Qt::KeyboardModifiers m_appModifiers; |
| int m_eventCounter = 0; |
| }; |
| |
| void tst_QWidget::keyboardModifiers() |
| { |
| KeyboardWidget w; |
| w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| w.resize(300, 300); |
| w.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&w)); |
| QTest::mouseClick(&w, Qt::LeftButton, Qt::ControlModifier); |
| QCOMPARE(w.m_eventCounter, 1); |
| QCOMPARE(int(w.m_modifiers), int(Qt::ControlModifier)); |
| QCOMPARE(int(w.m_appModifiers), int(Qt::ControlModifier)); |
| } |
| |
| class DClickWidget : public QWidget |
| { |
| public: |
| void mouseDoubleClickEvent(QMouseEvent *) override |
| { |
| triggered = true; |
| } |
| bool triggered = false; |
| }; |
| |
| void tst_QWidget::mouseDoubleClickBubbling_QTBUG29680() |
| { |
| DClickWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget child(&parent); |
| parent.resize(200, 200); |
| child.resize(200, 200); |
| parent.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| |
| QTest::mouseDClick(&child, Qt::LeftButton); |
| |
| QTRY_VERIFY(parent.triggered); |
| } |
| |
| void tst_QWidget::largerThanScreen_QTBUG30142() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(200, 4000); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QVERIFY2(widget.frameGeometry().y() >= 0, |
| msgComparisonFailed(widget.frameGeometry().y(), " >=", 0)); |
| |
| QWidget widget2; |
| widget2.resize(10000, 400); |
| widget2.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget2)); |
| QVERIFY2(widget2.frameGeometry().x() >= 0, |
| msgComparisonFailed(widget.frameGeometry().x(), " >=", 0)); |
| } |
| |
| void tst_QWidget::resizeStaticContentsChildWidget_QTBUG35282() |
| { |
| QWidget widget; |
| widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| widget.resize(200,200); |
| |
| UpdateWidget childWidget(&widget); |
| childWidget.setAttribute(Qt::WA_StaticContents); |
| childWidget.setAttribute(Qt::WA_OpaquePaintEvent); |
| childWidget.setGeometry(250, 250, 500, 500); |
| |
| widget.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| if (m_platform == QStringLiteral("winrt")) |
| QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort); |
| QCOMPARE(childWidget.numPaintEvents, 0); |
| childWidget.reset(); |
| |
| widget.resize(1000,1000); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QGuiApplication::sync(); |
| QVERIFY2(childWidget.numPaintEvents >= 1, |
| msgComparisonFailed(childWidget.numPaintEvents, ">=", 1)); |
| } |
| |
| void tst_QWidget::qmlSetParentHelper() |
| { |
| #ifdef QT_BUILD_INTERNAL |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| QWidget child; |
| QVERIFY(QAbstractDeclarativeData::setWidgetParent); |
| QAbstractDeclarativeData::setWidgetParent(&child, &parent); |
| QCOMPARE(child.parentWidget(), &parent); |
| QAbstractDeclarativeData::setWidgetParent(&child, nullptr); |
| QVERIFY(!child.parentWidget()); |
| #else |
| QSKIP("Needs QT_BUILD_INTERNAL"); |
| #endif |
| } |
| |
| void tst_QWidget::testForOutsideWSRangeFlag() |
| { |
| QSKIP("Test assumes QWindows can have 0x0 size, see QTBUG-61953"); |
| |
| // QTBUG-49445 |
| { |
| QWidget widget; |
| widget.resize(0, 0); |
| widget.show(); |
| QTest::qWait(100); // Wait for a while... |
| QVERIFY(!widget.windowHandle()->isExposed()); // The window should not be visible |
| QVERIFY(widget.isVisible()); // The widget should be in visible state |
| } |
| { |
| QWidget widget; |
| |
| QWidget native(&widget); |
| native.setAttribute(Qt::WA_NativeWindow); |
| native.resize(0, 0); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QVERIFY(!native.windowHandle()->isExposed()); |
| } |
| { |
| QWidget widget; |
| QWidget native(&widget); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QVERIFY(native.isVisible()); |
| |
| native.resize(0, 0); |
| native.setAttribute(Qt::WA_NativeWindow); |
| QTest::qWait(100); // Wait for a while... |
| QVERIFY(!native.windowHandle()->isExposed()); |
| } |
| |
| // QTBUG-48321 |
| { |
| QWidget widget; |
| |
| QWidget native(&widget); |
| native.setAttribute(Qt::WA_NativeWindow); |
| |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QVERIFY(native.windowHandle()->isExposed()); |
| |
| native.resize(0, 0); |
| QTest::qWait(100); // Wait for a while... |
| QVERIFY(!native.windowHandle()->isExposed()); |
| } |
| |
| // QTBUG-51788 |
| { |
| QWidget widget; |
| widget.setLayout(new QGridLayout); |
| widget.layout()->addWidget(new QLineEdit); |
| widget.resize(0, 0); |
| widget.show(); |
| // The layout should change the size, so the widget must be visible! |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| } |
| } |
| |
| class TabletWidget : public QWidget |
| { |
| public: |
| TabletWidget(QWidget *parent) : QWidget(parent) { } |
| |
| int tabletEventCount = 0; |
| int pressEventCount = 0; |
| int moveEventCount = 0; |
| int releaseEventCount = 0; |
| int trackingChangeEventCount = 0; |
| qint64 uid = -1; |
| |
| protected: |
| void tabletEvent(QTabletEvent *event) override { |
| ++tabletEventCount; |
| uid = event->uniqueId(); |
| switch (event->type()) { |
| case QEvent::TabletMove: |
| ++moveEventCount; |
| break; |
| case QEvent::TabletPress: |
| ++pressEventCount; |
| break; |
| case QEvent::TabletRelease: |
| ++releaseEventCount; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| bool event(QEvent *ev) override { |
| if (ev->type() == QEvent::TabletTrackingChange) |
| ++trackingChangeEventCount; |
| return QWidget::event(ev); |
| } |
| }; |
| |
| void tst_QWidget::tabletTracking() |
| { |
| QWidget parent; |
| parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); |
| parent.resize(200,200); |
| // QWidgetWindow::handleTabletEvent doesn't deliver tablet events to the window's widget, only to a child. |
| // So it doesn't do any good to show a TabletWidget directly: it needs a parent. |
| TabletWidget widget(&parent); |
| widget.resize(200,200); |
| parent.showNormal(); |
| QVERIFY(QTest::qWaitForWindowExposed(&parent)); |
| widget.setAttribute(Qt::WA_TabletTracking); |
| QTRY_COMPARE(widget.trackingChangeEventCount, 1); |
| QVERIFY(widget.hasTabletTracking()); |
| |
| QWindow *window = parent.windowHandle(); |
| QPointF local(10, 10); |
| QPointF global = window->mapToGlobal(local.toPoint()); |
| QPointF deviceLocal = QHighDpi::toNativeLocalPosition(local, window); |
| QPointF deviceGlobal = QHighDpi::toNativePixels(global, window->screen()); |
| qint64 uid = 1234UL; |
| |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::NoButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.moveEventCount, 1); |
| QCOMPARE(widget.uid, uid); |
| |
| local += QPoint(10, 10); |
| deviceLocal += QPoint(10, 10); |
| deviceGlobal += QPoint(10, 10); |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::NoButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.moveEventCount, 2); |
| |
| widget.setTabletTracking(false); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.trackingChangeEventCount, 2); |
| |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::LeftButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.pressEventCount, 1); |
| |
| local += QPoint(10, 10); |
| deviceLocal += QPoint(10, 10); |
| deviceGlobal += QPoint(10, 10); |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::LeftButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.moveEventCount, 3); |
| |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::NoButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.releaseEventCount, 1); |
| |
| local += QPoint(10, 10); |
| deviceLocal += QPoint(10, 10); |
| deviceGlobal += QPoint(10, 10); |
| QWindowSystemInterface::handleTabletEvent(window, ulong(QDateTime::currentMSecsSinceEpoch()), deviceLocal, deviceGlobal, |
| QTabletEvent::Stylus, QTabletEvent::Pen, Qt::NoButton, 0, 0, 0, 0, 0, 0, uid, Qt::NoModifier); |
| QCoreApplication::processEvents(); |
| QTRY_COMPARE(widget.moveEventCount, 3); |
| } |
| |
| class CloseCountingWidget : public QWidget |
| { |
| public: |
| int closeCount = 0; |
| void closeEvent(QCloseEvent *ev) override; |
| }; |
| |
| void CloseCountingWidget::closeEvent(QCloseEvent *ev) |
| { |
| ++closeCount; |
| ev->accept(); |
| } |
| |
| void tst_QWidget::closeEvent() |
| { |
| CloseCountingWidget widget; |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| // Yes we call the close() function twice. This mimics the behavior of QTBUG-43344 where |
| // QApplication first closes all windows and then QCocoaApplication flushes window system |
| // events, triggering more close events. |
| widget.windowHandle()->close(); |
| widget.windowHandle()->close(); |
| QCOMPARE(widget.closeCount, 1); |
| } |
| |
| void tst_QWidget::closeWithChildWindow() |
| { |
| QWidget widget; |
| auto childWidget = new QWidget(&widget); |
| childWidget->setAttribute(Qt::WA_NativeWindow); |
| childWidget->windowHandle()->create(); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| widget.windowHandle()->close(); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| // Check that the child window inside the window is now visible |
| QVERIFY(childWidget->isVisible()); |
| |
| // Now explicitly hide the childWidget |
| childWidget->hide(); |
| widget.windowHandle()->close(); |
| widget.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&widget)); |
| QVERIFY(!childWidget->isVisible()); |
| } |
| |
| QTEST_MAIN(tst_QWidget) |
| #include "tst_qwidget.moc" |