diff -r 000000000000 -r 1918ee327afb tests/auto/qwidget/tst_qwidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qwidget/tst_qwidget.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,9577 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#if defined(QT3_SUPPORT) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../shared/util.h" + + +#ifdef Q_WS_S60 +#include // EEikStatusPaneUidTitle +#include // CAknTitlePane +#include // CAknContextPane +#include // CEikStatusPane +#include // CEikButtonGroupContainer +#endif + +#ifdef Q_WS_QWS +# include +#endif + +// I *MUST* have QtTest afterwards or this test won't work with newer headers +#if defined(Q_WS_MAC) +# include +#undef verify +#include "tst_qwidget_mac_helpers.h" // Abstract the ObjC stuff out so not everyone must run an ObjC++ compile. +#endif + +#include + +#if defined(Q_WS_WIN) +# include +# if !defined(Q_OS_WINCE) +#define Q_CHECK_PAINTEVENTS \ + if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ + QSKIP("desktop is not visible, this test would fail", SkipSingle); +# else +# define Q_CHECK_PAINTEVENTS +# endif +#elif defined(Q_WS_X11) +# include +# include +#elif defined(Q_WS_QWS) +# include +#endif + +#if !defined(Q_WS_WIN) +#define Q_CHECK_PAINTEVENTS +#endif + +#if defined(Q_OS_WINCE_WM) +#include +// taken from qguifunctions_wce.cpp +#define SPI_GETPLATFORMTYPE 257 +bool qt_wince_is_platform(const QString &platformString) { + wchar_t tszPlatform[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, + sizeof(tszPlatform)/sizeof(*tszPlatform),tszPlatform,0)) + if (0 == _tcsicmp(reinterpret_cast (platformString.utf16()), tszPlatform)) + return true; + return false; +} +bool qt_wince_is_smartphone() { + return qt_wince_is_platform(QString::fromLatin1("Smartphone")); +} +#endif + +#ifdef Q_WS_MAC +#include +bool macHasAccessToWindowsServer() +{ + SecuritySessionId mySession; + SessionAttributeBits sessionInfo; + SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo); + return (sessionInfo & sessionHasGraphicAccess); +} +#endif + + +#if defined(Bool) +#undef Bool +#endif + +// Will try to wait for the condition while allowing event processing +// for a maximum of 2 seconds. +#define WAIT_FOR_CONDITION(expr, expected) \ + do { \ + const int step = 100; \ + for (int i = 0; i < 2000 && expr != expected; i+=step) { \ + QTest::qWait(step); \ + } \ + } while(0) + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QWidget : public QObject +{ + Q_OBJECT + +public: + tst_QWidget(); + virtual ~tst_QWidget(); + + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void getSetCheck(); + void fontPropagation(); + void fontPropagation2(); + void palettePropagation(); + void palettePropagation2(); + void enabledPropagation(); + void acceptDropsPropagation(); + void isEnabledTo(); + void visible(); + void visible_setWindowOpacity(); + void isVisibleTo(); + void isHidden(); + void fonts(); + void mapToGlobal(); + void mapFromAndTo_data(); + void mapFromAndTo(); + void checkFocus(); + void focusChainOnHide(); + void focusChainOnReparent(); + void setTabOrder(); + void activation(); + void reparent(); + void windowState(); + void showMaximized(); + void showFullScreen(); + void showMinimized(); + void showMinimizedKeepsFocus(); + void icon(); + void hideWhenFocusWidgetIsChild(); + void normalGeometry(); + void setGeometry(); + void windowOpacity(); + void raise(); + void lower(); + void stackUnder(); + void testContentsPropagation(); + void saveRestoreGeometry(); + + void restoreVersion1Geometry_data(); + void restoreVersion1Geometry(); + + void windowTitle(); + void windowModified(); + void windowIconText(); + + void widgetAt(); +#ifdef Q_WS_MAC + void retainHIView(); + void sheetOpacity(); + 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 winIdChangeEvent(); +#ifdef Q_OS_SYMBIAN + void reparentCausesChildWinIdChange(); +#else + void persistentWinId(); +#endif + void qobject_castInDestroyedSlot(); + + void showHideEvent_data(); + void showHideEvent(); + + void lostUpdatesOnHide(); + + void update(); + void isOpaque(); + +#ifndef Q_WS_MAC + void scroll(); +#endif + + // tests QWidget::setGeometry() on windows only + void setWindowGeometry_data(); + void setWindowGeometry(); + + // tests QWidget::move() and resize() on windows only + void windowMoveResize_data(); + void windowMoveResize(); + + void moveChild_data(); + void moveChild(); + void showAndMoveChild(); + + void subtractOpaqueSiblings(); + +#ifdef Q_WS_WIN + void getDC(); +#ifndef Q_OS_WINCE + void setGeometry_win(); +#endif +#endif + + void setLocale(); + void deleteStyle(); + void multipleToplevelFocusCheck(); + void setFocus(); + void setCursor(); + void setToolTip(); + void testWindowIconChangeEventPropagation(); +#ifdef Q_WS_X11 + void minAndMaxSizeWithX11BypassWindowManagerHint(); + void showHideShow(); + void clean_qt_x11_enforce_cursor(); +#endif + + void compatibilityChildInsertedEvents(); + 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(); +#if defined(Q_WS_WIN) || defined(Q_WS_X11) + void alienWidgets(); +#endif + void adjustSize(); + void adjustSize_data(); + void updateGeometry(); + void updateGeometry_data(); + void sendUpdateRequestImmediately(); + void painterRedirection(); + void doubleRepaint(); +#ifndef Q_WS_MAC + void resizeInPaintEvent(); +#endif + + void setMaskInResizeEvent(); + void moveInResizeEvent(); + +#if defined(Q_WS_WIN) || defined(Q_WS_X11) + // We don't support immediate repaint right after show on + // other platforms. Must be compatible with Qt 4.3. + void immediateRepaintAfterShow(); + void immediateRepaintAfterInvalidateBuffer(); +#endif + void effectiveWinId(); + void customDpi(); + void customDpiProperty(); + + void quitOnCloseAttribute(); + void moveRect(); + +#if defined (Q_WS_WIN) + void gdiPainting(); + void paintOnScreenPossible(); +#endif + void reparentStaticWidget(); +#ifdef Q_WS_QWS + void updateOutsideSurfaceClip(); +#endif + void translucentWidget(); + + void setClearAndResizeMask(); + void maskedUpdate(); +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) + void syntheticEnterLeave(); + void taskQTBUG_4055_sendSyntheticEnterLeave(); +#endif + void windowFlags(); + void initialPosForDontShowOnScreenWidgets(); +#ifdef Q_WS_X11 + void paintOutsidePaintEvent(); +#endif + void updateOnDestroyedSignal(); + void toplevelLineEditFocus(); + void inputFocus_task257832(); + + void focusWidget_task254563(); + void rectOutsideCoordinatesLimit_task144779(); + void setGraphicsEffect(); + + void destroyBackingStore(); + + void activateWindow(); + +#ifdef Q_OS_SYMBIAN + void cbaVisibility(); +#endif + +private: + bool ensureScreenSize(int width, int height); + QWidget *testWidget; +}; + +bool tst_QWidget::ensureScreenSize(int width, int height) +{ + QSize available; +#ifdef Q_WS_QWS + available = QDesktopWidget().availableGeometry().size(); + if (available.width() < width || available.height() < height) { + QScreen *screen = QScreen::instance(); + if (!screen) + return false; + screen->setMode(width, height, screen->depth()); + } +#endif // Q_WS_QWS + + available = QDesktopWidget().availableGeometry().size(); + return (available.width() >= width && available.height() >= height); +} + +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : QInputContext() {} + QString identifierName() { return QString("NoName"); } + QString language() { return QString("NoLanguage"); } + void reset() {} + bool isComposing() const { return false; } +}; + +// Testing get/set functions +void tst_QWidget::getSetCheck() +{ + QWidget obj1; + QWidget child1(&obj1); + // QStyle * QWidget::style() + // void QWidget::setStyle(QStyle *) + QWindowsStyle *var1 = new QWindowsStyle; + obj1.setStyle(var1); + QCOMPARE(static_cast(var1), obj1.style()); + obj1.setStyle((QStyle *)0); + QVERIFY(var1 != obj1.style()); + QVERIFY(0 != obj1.style()); // 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); +#ifndef Q_WS_QWS //QWS doesn't allow toplevels to be bigger than the screen +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)obj1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#else + QCOMPARE(obj1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#endif +#endif + + 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); +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)child1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#else + QCOMPARE(child1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#endif + + // 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); +#ifndef Q_WS_QWS //QWS doesn't allow toplevels to be bigger than the screen +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)obj1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#else + QCOMPARE(obj1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#endif +#endif + + 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); +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)child1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#else + QCOMPARE(child1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium +#endif + + + +// 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); +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)obj1.maximumWidth(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX +#else + QCOMPARE(obj1.maximumWidth(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX +#endif + + // 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); +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + QCOMPARE((long)obj1.maximumHeight(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX +#else + QCOMPARE(obj1.maximumHeight(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX +#endif + + // 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.1f); + QCOMPARE(1.0, obj1.windowOpacity()); // 1.0 is the fullest opacity possible + + // QWidget * QWidget::focusProxy() + // void QWidget::setFocusProxy(QWidget *) + QWidget *var9 = new QWidget(); + obj1.setFocusProxy(var9); + QCOMPARE(var9, obj1.focusProxy()); + obj1.setFocusProxy((QWidget *)0); + QCOMPARE((QWidget *)0, obj1.focusProxy()); + delete var9; + + // const QRect & QWidget::geometry() + // void QWidget::setGeometry(const QRect &) + qApp->processEvents(); + QRect var10(10, 10, 100, 100); + obj1.setGeometry(var10); + qApp->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(var11), obj1.layout()); + obj1.setLayout((QLayout *)0); + QCOMPARE(static_cast(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((QLayout *)0, obj1.layout()); + + // bool QWidget::acceptDrops() + // void QWidget::setAcceptDrops(bool) + obj1.setAcceptDrops(false); + QCOMPARE(false, obj1.acceptDrops()); + obj1.setAcceptDrops(true); + QCOMPARE(true, obj1.acceptDrops()); + + // QInputContext * QWidget::inputContext() + // void QWidget::setInputContext(QInputContext *) + MyInputContext *var13 = new MyInputContext; + obj1.setInputContext(var13); + QCOMPARE((QInputContext *)0, obj1.inputContext()); // The widget by default doesn't have the WA_InputMethodEnabled attribute + obj1.setAttribute(Qt::WA_InputMethodEnabled); + obj1.setInputContext(var13); + QCOMPARE(static_cast(var13), obj1.inputContext()); + obj1.setInputContext((QInputContext *)0); + QCOMPARE(qApp->inputContext(), obj1.inputContext()); + QVERIFY(qApp->inputContext() != var13); + //delete var13; // No delete, since QWidget takes ownership + + // bool QWidget::autoFillBackground() + // void QWidget::setAutoFillBackground(bool) + obj1.setAutoFillBackground(false); + QCOMPARE(false, obj1.autoFillBackground()); + obj1.setAutoFillBackground(true); + QCOMPARE(true, obj1.autoFillBackground()); + + delete var1; +#if defined (Q_WS_WIN) && !defined(Q_OS_WINCE) + obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); + HWND handle = obj1.winId(); + long flags = GetWindowLong(handle, GWL_STYLE); + QVERIFY(flags & WS_POPUP); +#endif +} + +tst_QWidget::tst_QWidget() +{ + QFont font; + font.setBold(true); + font.setPointSize(42); + qApp->setFont(font, "QPropagationTestWidget"); + + QPalette palette; + palette.setColor(QPalette::ToolTipBase, QColor(12, 13, 14)); + palette.setColor(QPalette::Text, QColor(21, 22, 23)); + qApp->setPalette(palette, "QPropagationTestWidget"); + + testWidget = 0; +} + +tst_QWidget::~tst_QWidget() +{ +} + +class BezierViewer : public QWidget { +public: + BezierViewer( QWidget* parent=0, const char* name=0 ); + void paintEvent( QPaintEvent* ); + void setPoints( const QPolygonF& poly ); +private: + QPolygonF points; + +}; + +void tst_QWidget::initTestCase() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif + // Create the test class + testWidget = new BezierViewer( 0, "testObject"); + testWidget->resize(200,200); +#ifdef QT3_SUPPORT + qApp->setMainWidget(testWidget); +#endif + testWidget->show(); + QTest::qWaitForWindowShown(testWidget); + QTest::qWait(50); +} + +void tst_QWidget::cleanupTestCase() +{ + delete testWidget; + testWidget = 0; +} + +void tst_QWidget::init() +{ +// TODO: Add initialization code here. +// This will be executed immediately before each test is run. + testWidget->setFont(QFont()); + testWidget->setPalette(QPalette()); +} + +void tst_QWidget::cleanup() +{ +} + +// Helper class... + +BezierViewer::BezierViewer( QWidget* parent, const char* name ) + : QWidget( parent ) +{ + setObjectName(name); + QPalette pal; + pal.setColor(backgroundRole(), Qt::white); + setPalette(pal); +} + + +void BezierViewer::setPoints( const QPolygonF& a ) +{ + points = a; +} + +#include "private/qbezier_p.h" +void BezierViewer::paintEvent( QPaintEvent* ) +{ + if ( points.size() != 4 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QPolygon::bezier: The array must have 4 control points" ); +#endif + return; + } + + /* Calculate Bezier curve */ + QPolygonF bezier = QBezier::fromPoints(points.at(0),points.at(1),points.at(2),points.at(3)).toPolygon(); + + QPainter painter( this ); + + /* Calculate scale to fit in window */ + QRectF br = bezier.boundingRect() | points.boundingRect(); + QRectF pr = rect(); + int scl = qMax( qMin(pr.width()/br.width(), pr.height()/br.height()), qreal(1.) ); + int border = scl-1; + + /* Scale Bezier curve vertices */ + for ( QPolygonF::Iterator it = bezier.begin(); it != bezier.end(); ++it ) { + it->setX( (it->x()-br.x()) * scl + border ); + it->setY( (it->y()-br.y()) * scl + border ); + } + + /* Draw grid */ + painter.setPen( Qt::lightGray ); + int i; + for ( i = border; i <= pr.width(); i += scl ) { + painter.drawLine( i, 0, i, pr.height() ); + } + for ( int j = border; j <= pr.height(); j += scl ) { + painter.drawLine( 0, j, pr.width(), j ); + } + + /* Write number of vertices */ + painter.setPen( Qt::red ); + painter.setFont( QFont("Helvetica", 14, QFont::DemiBold, TRUE ) ); + QString caption; + caption.setNum( bezier.size() ); + caption += QString::fromLatin1( " vertices" ); + painter.drawText( 10, pr.height()-10, caption ); + + /* Draw Bezier curve */ + painter.setPen( Qt::black ); + painter.drawPolyline( bezier ); + + /* Scale and draw control points */ + painter.setPen( Qt::darkGreen ); + for ( QPolygonF::Iterator p1 = points.begin(); p1 != points.end(); ++p1 ) { + int x = (p1->x()-br.x()) * scl + border; + int y = (p1->y()-br.y()) * scl + border; + painter.drawLine( x-4, y-4, x+4, y+4 ); + painter.drawLine( x+4, y-4, x-4, y+4 ); + } + + /* Draw vertices */ + painter.setPen( Qt::red ); + painter.setBrush( Qt::red ); + for ( QPolygonF::Iterator p2 = bezier.begin(); p2 != bezier.end(); ++p2 ) + painter.drawEllipse( p2->x()-1, p2->y()-1, 3, 3 ); +} + +void tst_QWidget::fontPropagation() +{ + QFont font = testWidget->font(); + QWidget* childWidget = new QWidget( testWidget ); + 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); + 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: + QPropagationTestWidget(QWidget *parent = 0) + : QWidget(parent) + { } +}; + +void tst_QWidget::fontPropagation2() +{ + // ! Note, the code below is executed in tst_QWidget's constructor. + // QFont font; + // font.setBold(true); + // font.setPointSize(42); + // qApp->setFont(font, "QPropagationTestWidget"); + + QWidget *root = new QWidget; + QWidget *child0 = new QWidget(root); + 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); + qApp->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::palettePropagation() +{ + QPalette palette = testWidget->palette(); + QWidget* childWidget = new QWidget( testWidget ); + 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"); + + QWidget *root = new QWidget; + QWidget *child0 = new QWidget(root); + 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(); + QTest::qWait(100); + + // 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); + qApp->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() +{ + QWidget* childWidget = new QWidget( testWidget ); + 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::acceptDropsPropagation() +{ +#ifdef QT_NO_DRAGANDDROP + QSKIP("Drag'n drop disabled in this build", SkipAll); +#else + QWidget *childWidget = new QWidget(testWidget); + 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* 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 ) ); +} + +void tst_QWidget::visible() +{ + // Ensure that the testWidget is hidden for this test at the + // start + + testWidget->hide(); + QVERIFY( !testWidget->isVisible() ); + QWidget* childWidget = new QWidget( testWidget ); + QVERIFY( !childWidget->isVisible() ); + + testWidget->show(); + 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::visible_setWindowOpacity() +{ + testWidget->hide(); + QVERIFY( !testWidget->isVisible() ); + testWidget->setWindowOpacity(0.5); +#ifdef Q_OS_WIN + QVERIFY(::IsWindowVisible(testWidget->winId()) == FALSE); +#endif + testWidget->setWindowOpacity(1.0); +} + +void tst_QWidget::isVisibleTo() +{ + // Ensure that the testWidget is hidden for this test at the + // start + + testWidget->hide(); + 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 + + testWidget->hide(); + QVERIFY( testWidget->isHidden() ); + QWidget* childWidget = new QWidget( testWidget ); + 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() +{ + // 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) ); + QVERIFY( cleanTestWidget->font() == newFont ); + + cleanTestWidget->setFont(QFont()); + QVERIFY( !cleanTestWidget->testAttribute(Qt::WA_SetFont) ); + QVERIFY( cleanTestWidget->font() == originalFont ); +} + +void tst_QWidget::mapToGlobal() +{ +#if !defined(QT3_SUPPORT) + QSKIP("No Qt3 Support", SkipAll); +#else + QPoint vis = testWidget->mapToGlobal(QPoint(0,0)); + testWidget->hide(); + QCOMPARE(testWidget->mapToGlobal(QPoint(0,0)), vis); + testWidget->show(); + + // test in a layout and witha move + Q3HBox * qhb = new Q3HBox(testWidget); + QWidget * qw = new QWidget(qhb); + qw->move(6,12); + QPoint wVis = qw->mapToGlobal(QPoint(0,0)); + qw->hide(); + QCOMPARE(qw->mapToGlobal(QPoint(0,0)), wVis); + delete qhb; +#endif // QT3_SUPPORT +} + +void tst_QWidget::mapFromAndTo_data() +{ + QTest::addColumn("windowHidden"); + QTest::addColumn("subWindow1Hidden"); + QTest::addColumn("subWindow2Hidden"); + QTest::addColumn("subSubWindowHidden"); + QTest::addColumn("windowMinimized"); + QTest::addColumn("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.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_WINCE) && !defined(Q_OS_SYMBIAN) //still no proper minimizing + //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 (int i = 0; i <8; ++i) { + QCOMPARE(w, expectedOriginalChain[i]); + 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 (int i = 0; i <5; ++i) { + QCOMPARE(w, expectedNewChain[i]); + 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 (int i = 0; i <5; ++i) { + QCOMPARE(w, expectedOldChain[i]); + w = w->nextInFocusChain(); + } + for (int i = 4; i >= 0; --i) { + w = w->previousInFocusChain(); + QCOMPARE(w, expectedOldChain[i]); + } +} + + +void tst_QWidget::focusChainOnHide() +{ + testWidget->hide(); // We do not want to get disturbed by other widgets + // focus should move to the next widget in the focus chain when we hide it. + QWidget *parent = new QWidget(); + parent->setObjectName(QLatin1String("Parent")); + parent->setFocusPolicy(Qt::StrongFocus); + QWidget *child = new QWidget(parent); + child->setObjectName(QLatin1String("child")); + child->setFocusPolicy(Qt::StrongFocus); + QWidget::setTabOrder(child, parent); + + parent->show(); + qApp->setActiveWindow(parent->window()); + child->activateWindow(); + child->setFocus(); + qApp->processEvents(); + + WAIT_FOR_CONDITION(child->hasFocus(), true); + QCOMPARE(child->hasFocus(), true); + child->hide(); + qApp->processEvents(); + + WAIT_FOR_CONDITION(parent->hasFocus(), true); + QCOMPARE(parent->hasFocus(), true); + QCOMPARE(parent, qApp->focusWidget()); + + delete parent; + testWidget->show(); //don't disturb later tests +} + +void tst_QWidget::checkFocus() +{ +#if !defined(QT3_SUPPORT) + QSKIP("No Qt3 Support", SkipAll); +#else + // This is a very specific test for a specific bug, the bug was + // that when setEnabled(FALSE) then setEnabled(TRUE) was called on + // the parent of a child widget which had focus while hidden, then + // when the widget was shown, the focus would be in the wrong place. + + Q3HBox widget; + QLineEdit *focusWidget = new QLineEdit( &widget ); + new QLineEdit( &widget ); + new QPushButton( &widget ); + focusWidget->setFocus(); + widget.setEnabled( FALSE ); + widget.setEnabled( TRUE ); + widget.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); +#endif + QTest::qWait( 100 ); + widget.activateWindow(); + // next call is necessary since the window manager may not give the focus to the widget when + // it is shown, which causes the QVERIFY to fail + QApplication::setActiveWindow(&widget); + QVERIFY( qApp->focusWidget() == focusWidget ); +#endif // QT3_SUPPORT +} + +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: + Composite(QWidget* parent = 0, const char* name = 0) + : QFrame(parent) + { + setObjectName(name); + //QHBoxLayout* hbox = new QHBoxLayout(this, 2, 0); + //hbox->setAutoAdd(true); + QHBoxLayout* hbox = new QHBoxLayout(this); + + lineEdit = new QLineEdit(this); + hbox->addWidget(lineEdit); + + button = new QPushButton(this); + hbox->addWidget(button); + button->setFocusPolicy( Qt::NoFocus ); + + setFocusProxy( lineEdit ); + setFocusPolicy( Qt::StrongFocus ); + + setTabOrder(lineEdit, button); + } + +private: + QLineEdit* lineEdit; + QPushButton* button; +}; + +#define NUM_WIDGETS 4 + +void tst_QWidget::setTabOrder() +{ + QTest::qWait(100); + + Container container; + + Composite* comp[NUM_WIDGETS]; + + QLineEdit *firstEdit = new QLineEdit(&container); + container.box->addWidget(firstEdit); + + int i = 0; + for(i = 0; i < NUM_WIDGETS; i++) { + comp[i] = new Composite(&container); + container.box->addWidget(comp[i]); + } + + QLineEdit *lastEdit = new QLineEdit(&container); + container.box->addWidget(lastEdit); + + container.setTabOrder(lastEdit, comp[NUM_WIDGETS-1]); + for(i = NUM_WIDGETS-1; i > 0; i--) { + container.setTabOrder(comp[i], comp[i-1]); + } + container.setTabOrder(comp[0], firstEdit); + + int current = NUM_WIDGETS-1; + lastEdit->setFocus(); + + container.show(); + container.activateWindow(); + qApp->setActiveWindow(&container); +#ifdef Q_WS_X11 + QTest::qWaitForWindowShown(&container); + QTest::qWait(50); +#endif + + QTest::qWait(100); + + QTRY_VERIFY(lastEdit->hasFocus()); + container.tab(); + do { + QVERIFY(comp[current]->focusProxy()->hasFocus()); + container.tab(); + current--; + } while (current >= 0); + + QVERIFY(firstEdit->hasFocus()); +} + +void tst_QWidget::activation() +{ +#if !defined(Q_WS_WIN) + QSKIP("This test is Windows-only.", SkipAll); +#endif + Q_CHECK_PAINTEVENTS + +#if defined(Q_OS_WINCE) + int waitTime = 1000; +#else + int waitTime = 100; +#endif + +#ifdef Q_OS_WINCE + qApp->processEvents(); +#endif + QWidget widget1; + widget1.setWindowTitle("Widget1"); + + QWidget widget2; + widget2.setWindowTitle("Widget2"); + + widget1.show(); + widget2.show(); + + QTest::qWait(waitTime); + QVERIFY(qApp->activeWindow() == &widget2); + widget2.showMinimized(); + QTest::qWait(waitTime); + + QVERIFY(qApp->activeWindow() == &widget1); + widget2.showMaximized(); + QTest::qWait(waitTime); + QVERIFY(qApp->activeWindow() == &widget2); + widget2.showMinimized(); + QTest::qWait(waitTime); + QVERIFY(qApp->activeWindow() == &widget1); + widget2.showNormal(); + QTest::qWait(waitTime); +#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) + QEXPECT_FAIL("", "MS introduced new behavior after XP", Continue); +#endif + QTest::qWait(waitTime); + QVERIFY(qApp->activeWindow() == &widget2); + widget2.hide(); + QTest::qWait(waitTime); + QVERIFY(qApp->activeWindow() == &widget1); +} + +void tst_QWidget::windowState() +{ +#ifdef Q_WS_X11 + QSKIP("Many window managers do not support window state properly, which causes this " + "test to fail.", SkipAll); +#else +#ifdef Q_OS_WINCE_WM + QPoint pos(500, 500); + QSize size(200, 200); + if (qt_wince_is_smartphone()) { //small screen + pos = QPoint(10,10); + size = QSize(100,100); + } +#elif defined(Q_WS_S60) + QPoint pos = QPoint(10,10); + QSize size = QSize(100,100); +#else + const QPoint pos(500, 500); + const QSize size(200, 200); +#endif + + QWidget widget1; + widget1.move(pos); + widget1.resize(size); + QCOMPARE(widget1.pos(), pos); + QCOMPARE(widget1.size(), size); + QTest::qWait(100); + widget1.setWindowTitle("Widget1"); + QCOMPARE(widget1.pos(), pos); + QCOMPARE(widget1.size(), size); + +#define VERIFY_STATE(s) QCOMPARE(int(widget1.windowState() & stateMask), int(s)) + + const int stateMask = Qt::WindowMaximized|Qt::WindowMinimized|Qt::WindowFullScreen; + + widget1.setWindowState(Qt::WindowMaximized); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowMaximized); + + widget1.show(); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowMaximized); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); + QTest::qWait(100); + QVERIFY(!(widget1.windowState() & Qt::WindowMaximized)); + QCOMPARE(widget1.pos(), pos); + + widget1.setWindowState(Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowMinimized); + + widget1.setWindowState(widget1.windowState() | Qt::WindowMaximized); + QTest::qWait(100); + VERIFY_STATE((Qt::WindowMinimized|Qt::WindowMaximized)); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowMaximized); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); + QTest::qWait(100); + QVERIFY(!(widget1.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized))); + QCOMPARE(widget1.pos(), pos); + + widget1.setWindowState(Qt::WindowFullScreen); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowFullScreen); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMinimized)); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowFullScreen); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); + QTest::qWait(100); + VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized)); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized|Qt::WindowMinimized)); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMinimized); + QTest::qWait(100); + VERIFY_STATE((Qt::WindowFullScreen|Qt::WindowMaximized)); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowFullScreen); + QTest::qWait(100); + VERIFY_STATE(Qt::WindowMaximized); + + widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized); + QTest::qWait(100); + QVERIFY(!(widget1.windowState() & stateMask)); + + QCOMPARE(widget1.pos(), pos); + QCOMPARE(widget1.size(), size); +#endif +} + +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)); + +#if !defined(Q_WS_QWS) && !defined(Q_OS_WINCE) && !defined(Q_WS_S60) +//embedded may choose a different size to fit on the screen. + QCOMPARE(layouted.size(), layouted.sizeHint()); +#endif + 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; + widget.setGeometry(0, 0, 10, 10); + widget.showMaximized(); + QTRY_VERIFY(widget.size().width() > 20 && widget.size().height() > 20); + } + +#ifdef QT3_SUPPORT +#if !defined(Q_WS_QWS) +//embedded respects max/min sizes by design -- maybe wrong design, but that's the way it is now. + { + Q3HBox box; + QWidget widget(&box); + widget.setMinimumSize(500, 500); + box.showMaximized(); + QVERIFY(box.isMaximized()); + } + + { + Q3HBox box; + QWidget widget(&box); + widget.setMaximumSize(500, 500); + + box.showMaximized(); + QVERIFY(box.isMaximized()); + } +#endif +#endif // QT3_SUPPORT +} + +void tst_QWidget::showFullScreen() +{ + 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); + + plain.showNormal(); + QVERIFY(!(plain.windowState() & Qt::WindowFullScreen)); + + layouted.showFullScreen(); + QVERIFY(layouted.windowState() & Qt::WindowFullScreen); + + layouted.showNormal(); + QVERIFY(!(layouted.windowState() & Qt::WindowFullScreen)); + +#if !defined(Q_WS_QWS) && !defined(Q_OS_WINCE) && !defined (Q_WS_S60) +//embedded may choose a different size to fit on the screen. + QCOMPARE(layouted.size(), layouted.sizeHint()); +#endif + + layouted.showFullScreen(); + QVERIFY(layouted.isFullScreen()); + QVERIFY(layouted.isVisible()); + + layouted.hide(); + QVERIFY(layouted.isFullScreen()); + QVERIFY(!layouted.isVisible()); + + layouted.showFullScreen(); + QVERIFY(layouted.isFullScreen()); + QVERIFY(layouted.isVisible()); + + layouted.showMinimized(); + QVERIFY(layouted.isMinimized()); + QVERIFY(layouted.isFullScreen()); + + layouted.showFullScreen(); + QVERIFY(!layouted.isMinimized()); + QVERIFY(layouted.isFullScreen()); + QVERIFY(layouted.isVisible()); + + layouted.showMinimized(); + QVERIFY(layouted.isMinimized()); + QVERIFY(layouted.isFullScreen()); + + layouted.showFullScreen(); + QVERIFY(!layouted.isMinimized()); + QVERIFY(layouted.isFullScreen()); + QVERIFY(layouted.isVisible()); + + { + QWidget frame; + QWidget widget(&frame); + widget.showFullScreen(); + QVERIFY(widget.isFullScreen()); + } + +#ifdef QT3_SUPPORT +#if !defined(Q_WS_QWS) +//embedded respects max/min sizes by design -- maybe wrong design, but that's the way it is now. + { + Q3HBox box; + QWidget widget(&box); + widget.setMinimumSize(500, 500); + box.showFullScreen(); + QVERIFY(box.isFullScreen()); + } + + { + Q3HBox box; + QWidget widget(&box); + widget.setMaximumSize(500, 500); + + box.showFullScreen(); + QVERIFY(box.isFullScreen()); + } +#endif +#endif // QT3_SUPPORT +} + +class ResizeWidget : public QWidget { +public: + ResizeWidget(QWidget *p = 0) : QWidget(p) + { + m_resizeEventCount = 0; + } +protected: + void resizeEvent(QResizeEvent *e){ + QCOMPARE(size(), e->size()); + ++m_resizeEventCount; + } + +public: + int m_resizeEventCount; +}; + +void tst_QWidget::resizeEvent() +{ + { + QWidget wParent; + ResizeWidget wChild(&wParent); + wParent.show(); + QCOMPARE (wChild.m_resizeEventCount, 1); // initial resize event before paint + wParent.hide(); + wChild.resize(QSize(640,480)); + QCOMPARE (wChild.m_resizeEventCount, 1); + wParent.show(); + QCOMPARE (wChild.m_resizeEventCount, 2); + } + + { + ResizeWidget wTopLevel; + wTopLevel.show(); + QCOMPARE (wTopLevel.m_resizeEventCount, 1); // initial resize event before paint for toplevels + wTopLevel.hide(); + wTopLevel.resize(QSize(640,480)); + QCOMPARE (wTopLevel.m_resizeEventCount, 1); + wTopLevel.show(); + QCOMPARE (wTopLevel.m_resizeEventCount, 2); + } +} + +void tst_QWidget::showMinimized() +{ + QWidget plain; + plain.move(100, 100); + plain.resize(200, 200); + QPoint pos = plain.pos(); + + plain.showMinimized(); + QVERIFY(plain.isMinimized()); + QVERIFY(plain.isVisible()); + QCOMPARE(plain.pos(), pos); + + plain.showNormal(); + QVERIFY(!plain.isMinimized()); + QVERIFY(plain.isVisible()); + QCOMPARE(plain.pos(), pos); + + plain.showMinimized(); + QVERIFY(plain.isMinimized()); + QVERIFY(plain.isVisible()); + QCOMPARE(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() +{ + //here we test that minimizing a widget and restoring it doesn't change the focus inside of it + { + QWidget window; + QWidget child1(&window), child2(&window); + child1.setFocusPolicy(Qt::StrongFocus); + child2.setFocusPolicy(Qt::StrongFocus); + window.show(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + child2.setFocus(); + QTest::qWait(50); + + QTRY_COMPARE(window.focusWidget(), &child2); + QTRY_COMPARE(qApp->focusWidget(), &child2); + + window.showMinimized(); + QTest::qWait(10); + 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; + QWidget *child = new QWidget(&window); + child->setFocusPolicy(Qt::StrongFocus); + window.show(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + child->setFocus(); + QTest::qWait(50); + QTRY_COMPARE(window.focusWidget(), child); + QTRY_COMPARE(qApp->focusWidget(), child); + + delete child; + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(qApp->focusWidget(), static_cast(0)); + } + + //testing reparenting the focus widget + { + QWidget window; + QWidget *child = new QWidget(&window); + child->setFocusPolicy(Qt::StrongFocus); + window.show(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + child->setFocus(); + QTest::qWait(50); + QTRY_COMPARE(window.focusWidget(), child); + QTRY_COMPARE(qApp->focusWidget(), child); + + child->setParent(0); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(qApp->focusWidget(), static_cast(0)); + } + + //testing setEnabled(false) + { + QWidget window; + QWidget *child = new QWidget(&window); + child->setFocusPolicy(Qt::StrongFocus); + window.show(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + child->setFocus(); + QTest::qWait(10); + QTRY_COMPARE(window.focusWidget(), child); + QTRY_COMPARE(qApp->focusWidget(), child); + + child->setEnabled(false); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(qApp->focusWidget(), static_cast(0)); + } + + //testing clearFocus + { + QWidget window; + QWidget *firstchild = new QWidget(&window); + firstchild->setFocusPolicy(Qt::StrongFocus); + QWidget *child = new QWidget(&window); + child->setFocusPolicy(Qt::StrongFocus); + window.show(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + child->setFocus(); + QTest::qWait(10); + QTRY_COMPARE(window.focusWidget(), child); + QTRY_COMPARE(qApp->focusWidget(), child); + + child->clearFocus(); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(qApp->focusWidget(), static_cast(0)); + + window.showMinimized(); + QTest::qWait(30); + QTRY_VERIFY(window.isMinimized()); +#ifdef Q_WS_QWS + QEXPECT_FAIL("", "QWS does not implement showMinimized()", Continue); +#endif + QCOMPARE(window.focusWidget(), static_cast(0)); +#ifdef Q_WS_QWS + QEXPECT_FAIL("", "QWS does not implement showMinimized()", Continue); +#endif + QTRY_COMPARE(qApp->focusWidget(), static_cast(0)); + + window.showNormal(); + qApp->setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + QTest::qWait(30); +#ifdef Q_WS_MAC + if (!macHasAccessToWindowsServer()) + QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue); +#endif + QTRY_COMPARE(window.focusWidget(), firstchild); +#ifdef Q_WS_MAC + if (!macHasAccessToWindowsServer()) + QEXPECT_FAIL("", "When not having WindowServer access, we lose focus.", Continue); +#endif + QTRY_COMPARE(qApp->focusWidget(), firstchild); + } +} + + +void tst_QWidget::reparent() +{ + QWidget parent; + parent.setWindowTitle("Toplevel"); + parent.setGeometry(300, 300, 200, 150); + + QWidget child(0); + 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("childTLW"); + childTLW.setGeometry(100, 100, 50, 50); + QPalette pal2; + pal2.setColor(childTLW.backgroundRole(), Qt::yellow); + childTLW.setPalette(pal2); + + parent.show(); + childTLW.show(); + QTest::qWaitForWindowShown(&parent); + +#ifdef Q_OS_WINCE + parent.move(50, 50); +#else + parent.move(300, 300); +#endif + + QPoint childPos = parent.mapToGlobal(child.pos()); + QPoint tlwPos = childTLW.pos(); + + child.setParent(0, child.windowFlags() & ~Qt::WindowType_Mask); + child.setGeometry(childPos.x(), childPos.y(), child.width(), child.height()); + child.show(); + +#ifdef Q_WS_X11 + // 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 +#else + QCOMPARE(child.geometry().topLeft(), childPos); +#endif + QTRY_COMPARE(childTLW.pos(), tlwPos); + +#ifdef Q_WS_WIN + QWidget childTLWChild(&childTLW); + childTLWChild.setObjectName("childTLWChild"); + + QWidget grandChild(&child); + grandChild.setObjectName("grandChild"); + grandChild.setGeometry(10, 10, 160, 110); + QPalette pal3; + pal3.setColor(grandChild.backgroundRole(), Qt::red); + grandChild.setPalette(pal3); + //grandChild.setPaletteBackgroundColor(Qt::red); + + QWidget grandChildTLW(&grandChild, Qt::Window); + grandChildTLW.setObjectName("grandChildTLW"); + grandChildTLW.setGeometry(200, 200, 50, 50); + QPalette pal4; + pal4.setColor(grandChildTLW.backgroundRole(), Qt::yellow); + grandChildTLW.setPalette(pal4); + //grandChildTLW.setPaletteBackgroundColor(Qt::yellow); + + QWidget grandChildTLWChild(&grandChildTLW); + grandChildTLWChild.setObjectName("grandChildTLWChild"); + + QVERIFY(IsWindow(childTLW.winId())); + QVERIFY(IsWindow(childTLWChild.winId())); + QVERIFY(IsWindow(grandChildTLW.winId())); + QVERIFY(IsWindow(grandChildTLWChild.winId())); + + parent.show(); + + QVERIFY(IsWindow(childTLW.winId())); + QVERIFY(IsWindow(childTLWChild.winId())); + QVERIFY(IsWindow(grandChildTLW.winId())); + QVERIFY(IsWindow(grandChildTLWChild.winId())); + + child.setParent(&parent); + child.move(10,10); + child.show(); + + // this appears to stabelize results + qApp->processEvents(); + + QVERIFY(IsWindow(childTLW.winId())); + QVERIFY(IsWindow(childTLWChild.winId())); + + QVERIFY(IsWindow(grandChildTLW.winId())); + QVERIFY(IsWindow(grandChildTLWChild.winId())); +#else + QSKIP("This test makes only sense on Windows", SkipAll); +#endif +} + +void tst_QWidget::icon() +{ +#if defined(Q_WS_QWS) + QSKIP("Qt/Embedded does it differently", SkipAll); +#else + QPixmap p(20,20); + p.fill(Qt::red); + 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()); +#endif +} + +void tst_QWidget::hideWhenFocusWidgetIsChild() +{ + testWidget->activateWindow(); + QWidget *parentWidget = new QWidget(testWidget); + 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); + parentWidget->show(); + QLineEdit *edit2 = new QLineEdit(testWidget); + edit2->setObjectName("edit2"); + edit2->show(); + edit2->move(110, 100); + edit->setFocus(); + qApp->processEvents(); + QString actualFocusWidget, expectedFocusWidget; +#ifdef Q_WS_X11 + if (!qApp->focusWidget()) + QSKIP("Your window manager is too broken for this test", SkipAll); +#endif + QVERIFY(qApp->focusWidget()); + actualFocusWidget.sprintf("%p %s %s", qApp->focusWidget(), qApp->focusWidget()->objectName().toLatin1().constData(), qApp->focusWidget()->metaObject()->className()); + expectedFocusWidget.sprintf("%p %s %s", edit, edit->objectName().toLatin1().constData(), edit->metaObject()->className()); + QCOMPARE(actualFocusWidget, expectedFocusWidget); + + parentWidget->hide(); + qApp->processEvents(); + actualFocusWidget.sprintf("%p %s %s", qApp->focusWidget(), qApp->focusWidget()->objectName().toLatin1().constData(), qApp->focusWidget()->metaObject()->className()); + expectedFocusWidget.sprintf("%p %s %s", edit2, edit2->objectName().toLatin1().constData(), edit2->metaObject()->className()); + QCOMPARE(actualFocusWidget, expectedFocusWidget); + + delete edit2; + delete parentWidget; +} + +void tst_QWidget::normalGeometry() +{ +#ifdef Q_OS_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#endif + QWidget parent; + parent.setWindowTitle("NormalGeometry parent"); + QWidget *child = new QWidget(&parent); + + QCOMPARE(parent.normalGeometry(), parent.geometry()); + QCOMPARE(child->normalGeometry(), QRect()); + + parent.setGeometry(100, 100, 200, 200); + parent.show(); + QTest::qWaitForWindowShown(&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); + QTest::qWait(10); + 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.showMaximized(); + QTest::qWait(10); + QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); + QTRY_VERIFY(parent.geometry() != geom); + QCOMPARE(parent.normalGeometry(), geom); + + parent.showNormal(); + QTest::qWait(10); + 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); + 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; + QWidget child(&tlw); + + QRect tr(100,100,200,200); + QRect cr(50,50,50,50); + tlw.setGeometry(tr); + child.setGeometry(cr); + tlw.show(); + QTest::qWait(50); + QCOMPARE(tlw.geometry().size(), tr.size()); + QCOMPARE(child.geometry(), cr); + + tlw.setParent(0, Qt::Window|Qt::FramelessWindowHint); + tr = QRect(0,0,100,100); + tr.moveTopLeft(QApplication::desktop()->availableGeometry().topLeft()); + tlw.setGeometry(tr); + QCOMPARE(tlw.geometry(), tr); + tlw.show(); + QTest::qWait(50); + if (tlw.frameGeometry() != tlw.geometry()) + QSKIP("Your window manager is too broken for this test", SkipAll); + QCOMPARE(tlw.geometry(), tr); + +} + +void tst_QWidget::windowOpacity() +{ +#ifdef Q_OS_WINCE + QSKIP( "Windows CE does not support windowOpacity", SkipAll); +#endif + 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: + UpdateWidget(QWidget *parent = 0) : QWidget(parent) { + reset(); + } + + void paintEvent(QPaintEvent *e) { + paintedRegion += e->region(); + ++numPaintEvents; + if (resizeInPaintEvent) { + resizeInPaintEvent = false; + resize(size() + QSize(2, 2)); + } + } + + bool event(QEvent *event) + { + 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 = 0; + numZOrderChangeEvents = 0; + numUpdateRequestEvents = 0; + updateOnActivationChangeAndFocusIn = false; + resizeInPaintEvent = false; + paintedRegion = QRegion(); + } + + int numPaintEvents; + int numZOrderChangeEvents; + int numUpdateRequestEvents; + bool updateOnActivationChangeAndFocusIn; + bool resizeInPaintEvent; + QRegion paintedRegion; +}; + +void tst_QWidget::lostUpdatesOnHide() +{ +#ifndef Q_WS_MAC + UpdateWidget widget; + widget.setAttribute(Qt::WA_DontShowOnScreen); + widget.show(); + widget.hide(); + QTest::qWait(50); + widget.show(); + QTest::qWait(50); + + QCOMPARE(widget.numPaintEvents, 1); +#endif +} + +void tst_QWidget::raise() +{ +#ifdef QT_MAC_USE_COCOA + QSKIP("Cocoa has no Z-Order for views, we hack it, but it results in paint events.", SkipAll); +#endif + QTest::qWait(10); + QWidget *parent = new QWidget(0); + QList allChildren; + + UpdateWidget *child1 = new UpdateWidget(parent); + child1->setAutoFillBackground(true); + allChildren.append(child1); + + UpdateWidget *child2 = new UpdateWidget(parent); + child2->setAutoFillBackground(true); + allChildren.append(child2); + + UpdateWidget *child3 = new UpdateWidget(parent); + child3->setAutoFillBackground(true); + allChildren.append(child3); + + UpdateWidget *child4 = new UpdateWidget(parent); + child4->setAutoFillBackground(true); + allChildren.append(child4); + + parent->show(); + QTest::qWaitForWindowShown(parent); + QTest::qWait(10); + + QList list1; + list1 << child1 << child2 << child3 << child4; + QVERIFY(parent->children() == list1); + QCOMPARE(allChildren.count(), list1.count()); + + foreach (UpdateWidget *child, allChildren) { + int expectedPaintEvents = child == child4 ? 1 : 0; + if (expectedPaintEvents == 0) { + QVERIFY(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); + + foreach (UpdateWidget *child, 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 list2; + list2 << child1 << child3 << child4 << child2; + QVERIFY(parent->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; + parent->setParent(&topLevel); + topLevel.show(); + QTest::qWaitForWindowShown(&topLevel); + QTest::qWait(50); + + UpdateWidget *onTop = new UpdateWidget(&topLevel); + onTop->reset(); + onTop->resize(topLevel.size()); + onTop->setAutoFillBackground(true); + onTop->show(); + QTest::qWait(50); + QTRY_VERIFY(onTop->numPaintEvents > 0); + onTop->reset(); + + // Reset all the children. + foreach (UpdateWidget *child, allChildren) + child->reset(); + + for (int i = 0; i < 5; ++i) + child3->raise(); + QTest::qWait(50); + + QCOMPARE(onTop->numPaintEvents, 0); + QCOMPARE(onTop->numZOrderChangeEvents, 0); + + QList list3; + list3 << child1 << child4 << child2 << child3; + QVERIFY(parent->children() == list3); + + foreach (UpdateWidget *child, 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() +{ +#ifdef QT_MAC_USE_COCOA + QSKIP("Cocoa has no Z-Order for views, we hack it, but it results in paint events.", SkipAll); +#endif + QWidget *parent = new QWidget(0); + QList allChildren; + + UpdateWidget *child1 = new UpdateWidget(parent); + child1->setAutoFillBackground(true); + allChildren.append(child1); + + UpdateWidget *child2 = new UpdateWidget(parent); + child2->setAutoFillBackground(true); + allChildren.append(child2); + + UpdateWidget *child3 = new UpdateWidget(parent); + child3->setAutoFillBackground(true); + allChildren.append(child3); + + UpdateWidget *child4 = new UpdateWidget(parent); + child4->setAutoFillBackground(true); + allChildren.append(child4); + + parent->show(); + QTest::qWaitForWindowShown(parent); + QTest::qWait(100); + + QList list1; + list1 << child1 << child2 << child3 << child4; + QVERIFY(parent->children() == list1); + QCOMPARE(allChildren.count(), list1.count()); + + foreach (UpdateWidget *child, allChildren) { + int expectedPaintEvents = child == child4 ? 1 : 0; + if (expectedPaintEvents == 0) { + QVERIFY(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); + + foreach (UpdateWidget *child, 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 list2; + list2 << child4 << child1 << child2 << child3; + QVERIFY(parent->children() == list2); + + delete parent; +} + +void tst_QWidget::stackUnder() +{ +#ifdef QT_MAC_USE_COCOA + QSKIP("Cocoa has no Z-Order for views, we hack it, but it results in paint events.", SkipAll); +#endif + QTest::qWait(10); + QWidget *parent = new QWidget(0); + QList allChildren; + + UpdateWidget *child1 = new UpdateWidget(parent); + child1->setAutoFillBackground(true); + allChildren.append(child1); + + UpdateWidget *child2 = new UpdateWidget(parent); + child2->setAutoFillBackground(true); + allChildren.append(child2); + + UpdateWidget *child3 = new UpdateWidget(parent); + child3->setAutoFillBackground(true); + allChildren.append(child3); + + UpdateWidget *child4 = new UpdateWidget(parent); + child4->setAutoFillBackground(true); + allChildren.append(child4); + + parent->show(); + QTest::qWaitForWindowShown(parent); + QTest::qWait(10); +#ifdef Q_WS_QWS + QApplication::sendPostedEvents(); //glib workaround +#endif + + QList list1; + list1 << child1 << child2 << child3 << child4; + QVERIFY(parent->children() == list1); + + foreach (UpdateWidget *child, allChildren) { + int expectedPaintEvents = child == child4 ? 1 : 0; +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) + 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); + + QList list2; + list2 << child1 << child4 << child2 << child3; + QVERIFY(parent->children() == list2); + + foreach (UpdateWidget *child, 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); + + QList list3; + list3 << child4 << child2 << child1 << child3; + QVERIFY(parent->children() == list3); + + foreach (UpdateWidget *child, allChildren) { + int expectedZOrderChangeEvents = child == child1 ? 1 : 0; + if (child == child3) { +#ifdef Q_OS_WINCE + qApp->processEvents(); +#endif +#ifndef Q_WS_MAC + QEXPECT_FAIL(0, "Task 153869", Continue); +#endif + QCOMPARE(child->numPaintEvents, 0); + } else { + QCOMPARE(child->numPaintEvents, 0); + } + QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); + child->reset(); + } + + delete parent; +} + +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: + ContentsPropagationWidget(QWidget *parent = 0) : QWidget(parent) + { + 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) { + foreach (QObject *child, children()) + qobject_cast(child)->setAutoFillBackground(!enable); + } + +protected: + void paintEvent(QPaintEvent *) + { + int w = width(), h = height(); + drawPolygon(this, w, h); + } + + QSize sizeHint() const { return QSize(500, 500); } +}; + +void tst_QWidget::testContentsPropagation() +{ +#ifdef Q_WS_MAC + QSKIP("Pixmap is not antialiased whereas widget is.", SkipAll); +#endif + ContentsPropagationWidget widget; +#ifdef Q_WS_QWS + widget.resize(500,500); +#else + widget.setFixedSize(500, 500); +#endif + widget.setContentsPropagation(false); + QPixmap widgetSnapshot = QPixmap::grabWidget(&widget); + + 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 = QPixmap::grabWidget(&widget); + //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_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#endif + const QPoint position(100, 100); + const QSize size(200, 200); + + QByteArray savedGeometry; + + { + QWidget widget; + widget.move(position); + widget.resize(size); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + + QTRY_COMPARE(widget.pos(), position); + QCOMPARE(widget.size(), size); + savedGeometry = widget.saveGeometry(); + } + + { + QWidget widget; + + 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) == false); + QVERIFY(widget.restoreGeometry(one) == false); + QVERIFY(widget.restoreGeometry(two) == false); + QVERIFY(widget.restoreGeometry(three) == false); + QVERIFY(widget.restoreGeometry(four) == false); + QVERIFY(widget.restoreGeometry(garbage) == false); + + QVERIFY(widget.restoreGeometry(savedGeometry)); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + + QTRY_COMPARE(widget.pos(), position); + QCOMPARE(widget.size(), size); + widget.show(); + QCOMPARE(widget.pos(), position); + QCOMPARE(widget.size(), size); + } + + { + QWidget widget; + widget.move(position); + widget.resize(size); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(500); + 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() +{ + QTest::addColumn("fileName"); + QTest::addColumn("expectedWindowState"); + QTest::addColumn("expectedPosition"); + QTest::addColumn("expectedSize"); + QTest::addColumn("expectedNormalGeometry"); + const QPoint position(100, 100); + const QSize size(200, 200); + const QRect normalGeometry(102, 124, 200, 200); + + QTest::newRow("geometry.dat") << ":geometry.dat" << uint(Qt::WindowNoState) << position << size << normalGeometry; + QTest::newRow("geometry-maximized.dat") << ":geometry-maximized.dat" << uint(Qt::WindowMaximized) << position << size << normalGeometry; + QTest::newRow("geometry-fullscreen.dat") << ":geometry-fullscreen.dat" << uint(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() +{ +#ifdef Q_OS_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#endif + + QFETCH(QString, fileName); + QFETCH(uint, expectedWindowState); + QFETCH(QPoint, expectedPosition); + QFETCH(QSize, expectedSize); + QFETCH(QRect, expectedNormalGeometry); + + // WindowActive is uninteresting for this test + const uint 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; + + QVERIFY(widget.restoreGeometry(savedGeometry)); + + QCOMPARE(uint(widget.windowState() & WindowStateMask), expectedWindowState); + if (expectedWindowState == Qt::WindowNoState) { + QCOMPARE(widget.pos(), expectedPosition); + QCOMPARE(widget.size(), expectedSize); + } + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(100); + + if (expectedWindowState == Qt::WindowNoState) { + QTRY_COMPARE(widget.pos(), expectedPosition); + QTRY_COMPARE(widget.size(), expectedSize); + } + + widget.showNormal(); + QTest::qWait(10); + + if (expectedWindowState != Qt::WindowNoState) { + // restoring from maximized or fullscreen, we can only restore to the normal geometry + QTRY_COMPARE(widget.geometry(), expectedNormalGeometry); + } else { + QTRY_COMPARE(widget.pos(), expectedPosition); + QTRY_COMPARE(widget.size(), expectedSize); + } + +#if 0 + // Code for saving a new geometry*.dat files + { + QWidget widgetToSave; + widgetToSave.move(expectedPosition); + widgetToSave.resize(expectedSize); + widgetToSave.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); +#endif + 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() +{ + Q_CHECK_PAINTEVENTS + + QWidget *w1 = new QWidget(0, Qt::X11BypassWindowManagerHint); + w1->setGeometry(0,0,150,150); + w1->setObjectName("w1"); + + QWidget *w2 = new QWidget(0, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); + w2->setGeometry(50,50,100,100); + w2->setObjectName("w2"); + w1->show(); + QTest::qWaitForWindowShown(w1); + qApp->processEvents(); + QWidget *wr; + QTRY_VERIFY((wr = QApplication::widgetAt(100, 100))); + QCOMPARE(wr->objectName(), QString("w1")); + + w2->show(); + QTest::qWaitForWindowShown(w2); + qApp->processEvents(); + qApp->processEvents(); + qApp->processEvents(); + QTRY_VERIFY((wr = QApplication::widgetAt(100, 100))); + QCOMPARE(wr->objectName(), QString("w2")); + + w2->lower(); + qApp->processEvents(); + QTRY_VERIFY((wr = QApplication::widgetAt(100, 100))); + QCOMPARE(wr->objectName(), QString("w1")); + + w2->raise(); + qApp->processEvents(); + QTRY_VERIFY((wr = QApplication::widgetAt(100, 100))); + QCOMPARE(wr->objectName(), QString("w2")); + + + QWidget *w3 = new QWidget(w2); + w3->setGeometry(10,10,50,50); + w3->setObjectName("w3"); + w3->show(); + qApp->processEvents(); + QTRY_VERIFY((wr = QApplication::widgetAt(100,100))); + QCOMPARE(wr->objectName(), QString("w3")); + + w3->setAttribute(Qt::WA_TransparentForMouseEvents); + qApp->processEvents(); + QTRY_VERIFY((wr = QApplication::widgetAt(100, 100))); + QCOMPARE(wr->objectName(), QString("w2")); + + QRegion rgn = QRect(QPoint(0,0), w2->size()); + QPoint point = w2->mapFromGlobal(QPoint(100,100)); + rgn -= QRect(point, QSize(1,1)); + w2->setMask(rgn); + qApp->processEvents(); + QTest::qWait(10); +#if defined(Q_OS_WINCE) + QEXPECT_FAIL("", "Windows CE does only support rectangular regions", Continue); //See also task 147191 +#endif +#if defined(Q_OS_SYMBIAN) + QEXPECT_FAIL("", "Symbian/S60 does only support rectangular regions", Continue); //See also task 147191 +#endif + QTRY_COMPARE(QApplication::widgetAt(100,100)->objectName(), w1->objectName()); + QTRY_COMPARE(QApplication::widgetAt(101,101)->objectName(), w2->objectName()); + + QBitmap bitmap(w2->size()); + QPainter p(&bitmap); + p.fillRect(bitmap.rect(), Qt::color1); + p.setPen(Qt::color0); + p.drawPoint(w2->mapFromGlobal(QPoint(100,100))); + p.end(); + w2->setMask(bitmap); + qApp->processEvents(); + QTest::qWait(10); +#if defined(Q_OS_WINCE) + QEXPECT_FAIL("", "Windows CE does only support rectangular regions", Continue); //See also task 147191 +#endif +#if defined(Q_OS_SYMBIAN) + QEXPECT_FAIL("", "Symbian/S60 does only support rectangular regions", Continue); //See also task 147191 +#endif + QTRY_VERIFY(QApplication::widgetAt(100,100) == w1); + QTRY_VERIFY(QApplication::widgetAt(101,101) == w2); + + delete w2; + delete w1; +} + +#if defined(Q_WS_X11) +bool getProperty(Display *display, Window target, Atom type, Atom property, + unsigned char** data, unsigned long* count) +{ + Atom atom_return; + int size; + unsigned long nitems, bytes_left; + + int ret = XGetWindowProperty(display, target, property, + 0l, 1l, false, + type, &atom_return, &size, + &nitems, &bytes_left, data); + if (ret != Success || nitems < 1) + return false; + + if (bytes_left != 0) { + XFree(*data); + unsigned long remain = ((size / 8) * nitems) + bytes_left; + ret = XGetWindowProperty(display, target, + property, 0l, remain, false, + type, &atom_return, &size, + &nitems, &bytes_left, data); + if (ret != Success) + return false; + } + + *count = nitems; + return true; +} + +QString textPropertyToString(Display *display, XTextProperty& text_prop) +{ + QString ret; + if (text_prop.value && text_prop.nitems > 0) { + if (text_prop.encoding == XA_STRING) { + ret = reinterpret_cast(text_prop.value); + } else { + text_prop.nitems = strlen(reinterpret_cast(text_prop.value)); + char **list; + int num; + if (XmbTextPropertyToTextList(display, &text_prop, &list, &num) == Success + && num > 0 && *list) { + ret = QString::fromLocal8Bit(*list); + XFreeStringList(list); + } + } + } + return ret; +} + +#endif + +#if defined(Q_WS_S60) +// Returns the application's status pane control, if not present returns NULL. +static CCoeControl* GetStatusPaneControl( TInt aPaneId ) +{ + const TUid paneUid = { aPaneId }; + + CEikStatusPane* statusPane = CEikonEnv::Static()->AppUiFactory()->StatusPane(); + if (statusPane && statusPane->PaneCapabilities(paneUid).IsPresent()){ + CCoeControl* control = NULL; + // ControlL shouldn't leave because the pane is present + TRAPD(err, control = statusPane->ControlL(paneUid)); + return err != KErrNone ? NULL : control; + } + return NULL; +} +// Returns the application's title pane, if not present returns NULL. +static CAknTitlePane* TitlePane() +{ + return static_cast(GetStatusPaneControl(EEikStatusPaneUidTitle)); +} + +// Returns the application's title pane, if not present returns NULL. +static CAknContextPane* ContextPane() +{ + return static_cast(GetStatusPaneControl(EEikStatusPaneUidContext)); +} +#endif + +static QString visibleWindowTitle(QWidget *window, Qt::WindowState state = Qt::WindowNoState) +{ + QString vTitle; + +#ifdef Q_WS_WIN + Q_UNUSED(state); + const size_t maxTitleLength = 256; + wchar_t title[maxTitleLength]; + GetWindowText(window->winId(), title, maxTitleLength); + vTitle = QString::fromWCharArray(title); +#elif defined(Q_WS_X11) + /* + We can't check what the window manager displays, but we can + check what we tell the window manager to display. This will + have to do. + */ + Atom UTF8_STRING = XInternAtom(window->x11Info().display(), "UTF8_STRING", false); + Atom _NET_WM_NAME = XInternAtom(window->x11Info().display(), "_NET_WM_NAME", false); + Atom _NET_WM_ICON_NAME = XInternAtom(window->x11Info().display(), "_NET_WM_ICON_NAME", false); + uchar *data = 0; + ulong length = 0; + if (state == Qt::WindowMinimized) { + if (getProperty(window->x11Info().display(), window->winId(), + UTF8_STRING, _NET_WM_ICON_NAME, &data, &length)) { + vTitle = QString::fromUtf8((char *) data, length); + XFree(data); + } else { + XTextProperty text_prop; + if (XGetWMIconName(window->x11Info().display(), window->winId(), &text_prop)) { + vTitle = textPropertyToString(window->x11Info().display(), text_prop); + XFree((char *) text_prop.value); + } + } + } else { + if (getProperty(window->x11Info().display(), window->winId(), + UTF8_STRING, _NET_WM_NAME, &data, &length)) { + vTitle = QString::fromUtf8((char *) data, length); + XFree(data); + } else { + XTextProperty text_prop; + if (XGetWMName(window->x11Info().display(), window->winId(), &text_prop)) { + vTitle = textPropertyToString(window->x11Info().display(), text_prop); + XFree((char *) text_prop.value); + } + } + } +#elif defined(Q_WS_MAC) + vTitle = nativeWindowTitle(window, state); +#elif defined(Q_WS_QWS) + if (qwsServer) { + const QWSWindow *win = 0; + const QList windows = qwsServer->clientWindows(); + for (int i = 0; i < windows.count(); ++i) { + const QWSWindow* w = windows.at(i); + if (w->winId() == window->winId()) { + win = w; + break; + } + } + if (win) + vTitle = win->caption(); + } +#elif defined (Q_WS_S60) + CAknTitlePane* titlePane = TitlePane(); + if(titlePane) + { + const TDesC* nTitle = titlePane->Text(); + vTitle = QString::fromUtf16(nTitle->Ptr(), nTitle->Length()); + } +#endif + + return vTitle; +} + +void tst_QWidget::windowTitle() +{ + QWidget widget(0); + widget.setWindowTitle("Application Name"); + widget.winId(); // Make sure the window is created... + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowTitle("Application Name *"); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name *")); + + widget.setWindowTitle("Application Name[*]"); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowTitle("Application Name[*][*]"); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*]")); + + widget.setWindowTitle("Application Name[*][*][*]"); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*]")); + + widget.setWindowTitle("Application Name[*][*][*][*]"); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*][*]")); +} + +void tst_QWidget::windowIconText() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("Symbian/S60 windows don't have window icon text", SkipAll); +#endif + QWidget widget(0); + + widget.setWindowTitle("Application Name"); + widget.setWindowIconText("Application Minimized"); + widget.showNormal(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + widget.showMinimized(); +#if defined(Q_WS_QWS) || defined(Q_OS_WINCE) + QEXPECT_FAIL(0, "Qt/Embedded/WinCE does not implement showMinimized()", Continue); + //See task 147193 for WinCE +#endif + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget, Qt::WindowMinimized), + QString("Application Minimized")); + + widget.setWindowTitle("Application Name[*]"); + widget.setWindowIconText("Application Minimized[*]"); + widget.showNormal(); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + widget.showMinimized(); +#if defined (Q_WS_QWS) || defined(Q_OS_WINCE) + QEXPECT_FAIL(0, "Qt/Embedded/WinCE does not implement showMinimized()", Continue); + //See task 147193 for WinCE +#endif + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget, Qt::WindowMinimized), + QString("Application Minimized")); + + widget.setWindowModified(true); + widget.showNormal(); + QApplication::instance()->processEvents(); + if (widget.style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, &widget)) + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name*")); + else + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + widget.showMinimized(); +#if defined (Q_WS_QWS) || defined(Q_OS_WINCE) + QEXPECT_FAIL(0, "Qt/Embedded/WinCE does not implement showMinimized()", Continue); + //See task 147193 for WinCE +#endif + QApplication::instance()->processEvents(); +#ifdef Q_WS_MAC + QCOMPARE(visibleWindowTitle(&widget, Qt::WindowMinimized), + QString("Application Minimized")); + QVERIFY(nativeWindowModified(&widget)); +#else + QCOMPARE(visibleWindowTitle(&widget, Qt::WindowMinimized), + QString("Application Minimized*")); +#endif +} + +void tst_QWidget::windowModified() +{ + QWidget widget(0); + widget.show(); + QTest::qWaitForWindowShown(&widget); +#ifndef Q_WS_MAC + QTest::ignoreMessage(QtWarningMsg, "QWidget::setWindowModified: The window title does not contain a '[*]' placeholder"); +#endif + widget.setWindowTitle("Application Name"); + QTest::qWait(10); + QTRY_COMPARE(visibleWindowTitle(&widget), QString("Application Name")); + +#ifdef Q_WS_MAC + widget.setWindowModified(true); + QVERIFY(nativeWindowModified(&widget)); +#else + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowTitle("Application Name[*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + if (widget.style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, &widget)) + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name*")); + else + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowTitle("Application[*] Name[*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application* Name*")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name")); + + widget.setWindowTitle("Application Name[*][*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*]")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*]")); + + widget.setWindowTitle("Application[*][*] Name[*][*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application[*] Name[*]")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application[*] Name[*]")); + + widget.setWindowTitle("Application[*] Name[*][*][*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application* Name[*]*")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application Name[*]")); + + widget.setWindowTitle("Application[*][*][*] Name[*][*][*]"); + + widget.setWindowModified(true); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application[*]* Name[*]*")); + + widget.setWindowModified(false); + QApplication::instance()->processEvents(); + QCOMPARE(visibleWindowTitle(&widget), QString("Application[*] Name[*]")); +#endif +} + +void tst_QWidget::task110173() +{ + QWidget w; + + 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(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(200); +} + +class Widget : public QWidget +{ +public: + Widget() : deleteThis(false) { setFocusPolicy(Qt::StrongFocus); } + void actionEvent(QActionEvent *) { if (deleteThis) delete this; } + void changeEvent(QEvent *) { if (deleteThis) delete this; } + void closeEvent(QCloseEvent *) { if (deleteThis) delete this; } + void hideEvent(QHideEvent *) { if (deleteThis) delete this; } + void focusOutEvent(QFocusEvent *) { if (deleteThis) delete this; } + void keyPressEvent(QKeyEvent *) { if (deleteThis) delete this; } + void keyReleaseEvent(QKeyEvent *) { if (deleteThis) delete this; } + void mouseDoubleClickEvent(QMouseEvent *) { if (deleteThis) delete this; } + void mousePressEvent(QMouseEvent *) { if (deleteThis) delete this; } + void mouseReleaseEvent(QMouseEvent *) { if (deleteThis) delete this; } + void mouseMoveEvent(QMouseEvent *) { if (deleteThis) delete this; } + + bool deleteThis; +}; + +void tst_QWidget::testDeletionInEventHandlers() +{ + // closeEvent + QPointer w = new Widget; + w->deleteThis = true; + w->close(); + QVERIFY(w == 0); + delete w; + + // focusOut (crashes) + //w = new Widget; + //w->show(); + //w->setFocus(); + //QVERIFY(qApp->focusWidget() == w); + //w->deleteThis = true; + //w->clearFocus(); + //QVERIFY(w == 0); + + // key press + w = new Widget; + w->show(); + w->deleteThis = true; + QTest::keyPress(w, Qt::Key_A); + QVERIFY(w == 0); + delete w; + + // key release + w = new Widget; + w->show(); + w->deleteThis = true; + QTest::keyRelease(w, Qt::Key_A); + QVERIFY(w == 0); + delete w; + + // mouse press + w = new Widget; + w->show(); + w->deleteThis = true; + QTest::mousePress(w, Qt::LeftButton); + QVERIFY(w == 0); + delete w; + + // mouse release + w = new Widget; + w->show(); + w->deleteThis = true; + QTest::mouseRelease(w, Qt::LeftButton); + QVERIFY(w == 0); + delete w; + + // mouse double click + w = new Widget; + w->show(); + w->deleteThis = true; + QTest::mouseDClick(w, Qt::LeftButton); + QVERIFY(w == 0); + delete w; + + // hide event (crashes) + //w = new Widget; + //w->show(); + //w->deleteThis = true; + //w->hide(); + //QVERIFY(w == 0); + + // action event + w = new Widget; + w->deleteThis = true; + w->addAction(new QAction(w)); + QVERIFY(w == 0); + delete w; + + // change event + w = new Widget; + w->show(); + w->deleteThis = true; + w->setMouseTracking(true); + QVERIFY(w == 0); + delete w; + + w = new Widget; + w->setMouseTracking(true); + w->show(); + w->deleteThis = true; + QMouseEvent me(QEvent::MouseMove, QPoint(0, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(w, &me); + QVERIFY(w == 0); + delete w; +} + +#ifdef Q_WS_MAC + +bool testAndRelease(const HIViewRef view) +{ +// qDebug() << CFGetRetainCount(view); + if (CFGetRetainCount(view) != 2) + return false; + CFRelease(view); + CFRelease(view); + return true; +} + +typedef QPair WidgetViewPair; + +WidgetViewPair createAndRetain(QWidget * const parent = 0) +{ + QWidget * const widget = new QWidget(parent); + const HIViewRef view = (HIViewRef)widget->winId(); + // Retain twice so we can safely call CFGetRetaintCount even if the retain count + // is off by one because of a double release. + CFRetain(view); + CFRetain(view); + return qMakePair(widget, view); +} + +/* + Test that retaining and releasing the HIView returned by QWidget::winId() + works even if the widget itself is deleted. +*/ +void tst_QWidget::retainHIView() +{ + // Single window + { + const WidgetViewPair window = createAndRetain(); + delete window.first; + QVERIFY(testAndRelease(window.second)); + } + + // Child widget + { + const WidgetViewPair parent = createAndRetain(); + const WidgetViewPair child = createAndRetain(parent.first); + + delete parent.first; + QVERIFY(testAndRelease(parent.second)); + QVERIFY(testAndRelease(child.second)); + } + + // Multiple children + { + const WidgetViewPair parent = createAndRetain(); + const WidgetViewPair child1 = createAndRetain(parent.first); + const WidgetViewPair child2 = createAndRetain(parent.first); + + delete parent.first; + QVERIFY(testAndRelease(parent.second)); + QVERIFY(testAndRelease(child1.second)); + QVERIFY(testAndRelease(child2.second)); + } + + // Grandchild widget + { + const WidgetViewPair parent = createAndRetain(); + const WidgetViewPair child = createAndRetain(parent.first); + const WidgetViewPair grandchild = createAndRetain(child.first); + + delete parent.first; + QVERIFY(testAndRelease(parent.second)); + QVERIFY(testAndRelease(child.second)); + QVERIFY(testAndRelease(grandchild.second)); + } + + // Reparent child widget + { + const WidgetViewPair parent1 = createAndRetain(); + const WidgetViewPair parent2 = createAndRetain(); + const WidgetViewPair child = createAndRetain(parent1.first); + + child.first->setParent(parent2.first); + + delete parent1.first; + QVERIFY(testAndRelease(parent1.second)); + delete parent2.first; + QVERIFY(testAndRelease(parent2.second)); + QVERIFY(testAndRelease(child.second)); + } + + // Reparent window + { + const WidgetViewPair window1 = createAndRetain(); + const WidgetViewPair window2 = createAndRetain(); + const WidgetViewPair child1 = createAndRetain(window1.first); + const WidgetViewPair child2 = createAndRetain(window2.first); + + window2.first->setParent(window1.first); + + delete window2.first; + QVERIFY(testAndRelease(window2.second)); + QVERIFY(testAndRelease(child2.second)); + delete window1.first; + QVERIFY(testAndRelease(window1.second)); + QVERIFY(testAndRelease(child1.second)); + } + + // Delete child widget + { + const WidgetViewPair parent = createAndRetain(); + const WidgetViewPair child = createAndRetain(parent.first); + + delete child.first; + QVERIFY(testAndRelease(child.second)); + delete parent.first; + QVERIFY(testAndRelease(parent.second)); + } +} + +void tst_QWidget::sheetOpacity() +{ + QWidget tmpWindow; + QWidget sheet(&tmpWindow, Qt::Sheet); + tmpWindow.show(); + sheet.show(); + QCOMPARE(int(sheet.windowOpacity() * 255), 242); // 95% + sheet.setParent(0, Qt::Dialog); + QCOMPARE(int(sheet.windowOpacity() * 255), 255); +} + +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 = QPixmap::grabWindow(widget->winId()).toImage(); + + const QImage masked = image.copy(mask); + QImage red(masked); + red.fill(QColor(Qt::red).rgb()); + + return (masked == red); +} + +void tst_QWidget::setMask() +{ + testWidget->hide(); // get this out of the way. + + { + 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; + bool gotPaintEvent; + QRegion paintedRegion; + + StaticWidget(QWidget *parent = 0) + :QWidget(parent) + { + setAttribute(Qt::WA_StaticContents); + setAttribute(Qt::WA_OpaquePaintEvent); + setPalette(Qt::red); // Make sure we have an opaque palette. + setAutoFillBackground(true); + gotPaintEvent = false; + } + + void paintEvent(QPaintEvent *e) + { + paintedRegion += e->region(); + gotPaintEvent = true; +// qDebug() << "paint" << e->region(); + // Look for a full update, set partial to false if found. + foreach(QRect r, e->region().rects()) { + partial = (r != rect()); + if (partial == false) + 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() +{ + QWidget parent; + parent.resize(400, 400); + + StaticWidget staticWidget(&parent); + staticWidget.gotPaintEvent = false; + staticWidget.move(150, 150); + staticWidget.resize(150, 150); + parent.show(); + QTest::qWaitForWindowShown(&parent); + QTest::qWait(20); + QTRY_COMPARE(staticWidget.gotPaintEvent, true); + + 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.move(staticWidget.pos() + QPoint(-10, 10)); + QTest::qWait(20); + QCOMPARE(staticWidget.gotPaintEvent, false); + + staticWidget.gotPaintEvent = false; + staticWidget.resize(staticWidget.size() + QSize(10, 10)); + QTest::qWait(20); + QCOMPARE(staticWidget.gotPaintEvent, true); + 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)); + QTest::qWait(20); + QCOMPARE(staticWidget.gotPaintEvent, true); + 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)); + QTest::qWait(20); + QCOMPARE(staticWidget.gotPaintEvent, true); + 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)); + QTest::qWait(20); + QCOMPARE(staticWidget.gotPaintEvent, true); + 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 defined(Q_WS_MAC) || defined(Q_WS_QWS) + QSKIP("We do not yet have static contents support for *top-levels* on this platform", SkipAll); +#endif + + StaticWidget topLevel; + topLevel.gotPaintEvent = false; + topLevel.show(); + QTest::qWaitForWindowShown(&topLevel); + QTest::qWait(10); + QTRY_COMPARE(topLevel.gotPaintEvent, true); + + topLevel.gotPaintEvent = false; + topLevel.partial = false; + topLevel.paintedRegion = QRegion(); + +#ifndef Q_WS_WIN + 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 + const QRect frame = topLevel.frameGeometry(); + MoveWindow(topLevel.winId(), frame.x(), frame.y(), + frame.width() + 10, frame.height() + 10, + true); +#endif + + QTest::qWait(100); + + // Expected update region: New rect - old rect. + QRegion expectedUpdateRegion(topLevel.rect()); + expectedUpdateRegion -= QRect(QPoint(), topLevel.size() - QSize(10, 10)); + + QTRY_COMPARE(topLevel.gotPaintEvent, true); + QCOMPARE(topLevel.partial, true); + QCOMPARE(topLevel.paintedRegion, expectedUpdateRegion); +} + +class SiblingDeleter : public QWidget +{ +public: + inline SiblingDeleter(QWidget *sibling, QWidget *parent) + : QWidget(parent), sibling(sibling) {} + inline virtual ~SiblingDeleter() { delete sibling; } + +private: + QPointer sibling; +}; + + +void tst_QWidget::childDeletesItsSibling() +{ + QWidget *commonParent = new QWidget(0); + QPointer child = new QWidget(0); + QPointer siblingDeleter = new SiblingDeleter(child, commonParent); + child->setParent(commonParent); + delete commonParent; // don't crash + QVERIFY(!child); + QVERIFY(!siblingDeleter); + +} + +#ifdef Q_WS_QWS +# define SET_SAFE_SIZE(w) \ + do { \ + QSize safeSize(qt_screen->width() - 250, qt_screen->height() - 250); \ + if (!safeSize.isValid()) \ + QSKIP("Screen size too small", SkipAll); \ + if (defaultSize.width() > safeSize.width() || defaultSize.height() > safeSize.height()) { \ + defaultSize = safeSize; \ + w.resize(defaultSize); \ + w.setAttribute(Qt::WA_Resized, false); \ + } \ + } while (false) +#else +# define SET_SAFE_SIZE(w) +#endif + + +void tst_QWidget::setMinimumSize() +{ + QWidget w; + QSize defaultSize = w.size(); + SET_SAFE_SIZE(w); + + 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)); + +#ifdef Q_OS_WINCE + QSKIP("Setting a minimum size larger than the desktop does not work", SkipAll); +#endif + QSize nonDefaultSize = defaultSize + QSize(5,5); + w.setMinimumSize(nonDefaultSize); + w.show(); + QTest::qWait(50); + QVERIFY(w.height() >= nonDefaultSize.height()); + QVERIFY(w.width() >= nonDefaultSize.width()); +} + +void tst_QWidget::setMaximumSize() +{ + QWidget w; + QSize defaultSize = w.size(); + SET_SAFE_SIZE(w); + + 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)); + +#if 0 + //we don't enforce maximum size on show, apparently + QSize nonDefaultSize = defaultSize - QSize(5,5); + w.setMaximumSize(nonDefaultSize); + w.show(); + QTest::qWait(50); + qDebug() << nonDefaultSize << w.size(); + QVERIFY(w.height() <= nonDefaultSize.height()); + QVERIFY(w.width() <= nonDefaultSize.width()); +#endif +} + +void tst_QWidget::setFixedSize() +{ + QWidget w; + QSize defaultSize = w.size(); + SET_SAFE_SIZE(w); + + 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.show(); + QTest::qWait(50); + QVERIFY(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: + WinIdChangeWidget(QWidget *p = 0) + : QWidget(p) + , m_winIdChangeEventCount(0) + { + + } +protected: + bool event(QEvent *e) + { + if (e->type() == QEvent::WinIdChange) { + ++m_winIdChangeEventCount; + return true; + } + return QWidget::event(e); + } +public: + int m_winIdChangeEventCount; +}; + +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.m_winIdChangeEventCount, 1); + } + + { + // Changing parent of a native widget + // Should cause winId of child to change, on all platforms + QWidget parent1, parent2; + WinIdChangeWidget child(&parent1); + const WId winIdBefore = child.winId(); + QCOMPARE(child.m_winIdChangeEventCount, 1); + child.setParent(&parent2); + const WId winIdAfter = child.internalWinId(); + QVERIFY(winIdBefore != winIdAfter); + QCOMPARE(child.m_winIdChangeEventCount, 2); + } + + { + // Changing grandparent of a native widget + // Should cause winId of grandchild to change only on Symbian + QWidget grandparent1, grandparent2; + QWidget parent(&grandparent1); + WinIdChangeWidget child(&parent); + const WId winIdBefore = child.winId(); + QCOMPARE(child.m_winIdChangeEventCount, 1); + parent.setParent(&grandparent2); + const WId winIdAfter = child.internalWinId(); +#ifdef Q_OS_SYMBIAN + QVERIFY(winIdBefore != winIdAfter); + QCOMPARE(child.m_winIdChangeEventCount, 2); +#else + QCOMPARE(winIdBefore, winIdAfter); + QCOMPARE(child.m_winIdChangeEventCount, 1); +#endif + } + + { + // 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.m_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.m_winIdChangeEventCount, 1); + const Qt::WindowFlags flags = child.windowFlags(); + child.setWindowFlags(flags | Qt::Window); + const WId winIdAfter = child.internalWinId(); + QVERIFY(winIdBefore != winIdAfter); + QCOMPARE(child.m_winIdChangeEventCount, 2); + } +} + +#ifdef Q_OS_SYMBIAN +void tst_QWidget::reparentCausesChildWinIdChange() +{ + QWidget *parent = new QWidget; + QWidget *w1 = new QWidget; + QWidget *w2 = new QWidget; + QWidget *w3 = new QWidget; + w1->setParent(parent); + w2->setParent(w1); + w3->setParent(w2); + + WId winId1 = w1->winId(); + WId winId2 = w2->winId(); + WId winId3 = w3->winId(); + + // reparenting causes winIds of the widget being reparented, and all of its children, to change + w1->setParent(0); + QVERIFY(w1->winId() != winId1); + winId1 = w1->winId(); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w1->setParent(parent); + QVERIFY(w1->winId() != winId1); + winId1 = w1->winId(); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w2->setParent(0); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w2->setParent(parent); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w2->setParent(w1); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w3->setParent(0); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w3->setParent(w1); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w3->setParent(w2); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + delete parent; +} +#else +void tst_QWidget::persistentWinId() +{ + QWidget *parent = new QWidget; + QWidget *w1 = new QWidget; + QWidget *w2 = new QWidget; + QWidget *w3 = new QWidget; + w1->setParent(parent); + w2->setParent(w1); + w3->setParent(w2); + + WId winId1 = w1->winId(); + WId winId2 = w2->winId(); + WId winId3 = w3->winId(); + + // reparenting should change the winId of the widget being reparented, but not of its children + w1->setParent(0); + QVERIFY(w1->winId() != winId1); + winId1 = w1->winId(); + QCOMPARE(w2->winId(), winId2); + QCOMPARE(w3->winId(), winId3); + + w1->setParent(parent); + QVERIFY(w1->winId() != winId1); + winId1 = w1->winId(); + QCOMPARE(w2->winId(), winId2); + QCOMPARE(w3->winId(), winId3); + + w2->setParent(0); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QCOMPARE(w3->winId(), winId3); + + w2->setParent(parent); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QCOMPARE(w3->winId(), winId3); + + w2->setParent(w1); + QVERIFY(w2->winId() != winId2); + winId2 = w2->winId(); + QCOMPARE(w3->winId(), winId3); + + w3->setParent(0); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w3->setParent(w1); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + w3->setParent(w2); + QVERIFY(w3->winId() != winId3); + winId3 = w3->winId(); + + delete parent; +} +#endif // Q_OS_SYMBIAN + +class ShowHideEventWidget : public QWidget +{ +public: + int numberOfShowEvents, numberOfHideEvents; + + ShowHideEventWidget(QWidget *parent = 0) + : QWidget(parent), numberOfShowEvents(0), numberOfHideEvents(0) + { } + + void create() + { QWidget::create(); } + + void showEvent(QShowEvent *) + { ++numberOfShowEvents; } + + void hideEvent(QHideEvent *) + { ++numberOfHideEvents; } +}; + +void tst_QWidget::showHideEvent_data() +{ + QTest::addColumn("show"); + QTest::addColumn("hide"); + QTest::addColumn("create"); + QTest::addColumn("expectedShowEvents"); + QTest::addColumn("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; + 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::update() +{ + QTest::qWait(10); // Wait for the initStuff to do it's stuff. + Q_CHECK_PAINTEVENTS + + UpdateWidget w; + w.setGeometry(50, 50, 100, 100); + w.show(); + QTest::qWaitForWindowShown(&w); + + QApplication::processEvents(); + QApplication::processEvents(); + + 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 + { + 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); +#ifdef QT_MAC_USE_COCOA + QEXPECT_FAIL(0, "Cocoa compositor paints the content view", Continue); +#endif + QCOMPARE(w.paintedRegion, expectedVisible); +#ifdef QT_MAC_USE_COCOA + QEXPECT_FAIL(0, "Cocoa compositor says to paint this.", Continue); +#endif + 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); +#ifdef QT_MAC_USE_COCOA + QEXPECT_FAIL(0, "Cocoa compositor paints the content view", Continue); +#endif + QCOMPARE(w.paintedRegion, + w.visibleRegion() & sibling.visibleRegion().translated(siblingOffset)); +#ifdef QT_MAC_USE_COCOA + QEXPECT_FAIL(0, "Cocoa compositor paints the content view", Continue); +#endif + 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 QT_MAC_USE_COCOA + 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)); + } +} + +static inline bool isOpaque(QWidget *widget) +{ + if (!widget) + return false; + return qt_widget_private(widget)->isOpaque; +} + +void tst_QWidget::isOpaque() +{ +#ifndef Q_WS_MAC + 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_WS_MAC +/* + Test that scrolling of a widget invalidates the correct regions +*/ +void tst_QWidget::scroll() +{ + UpdateWidget updateWidget; + updateWidget.resize(500, 500); + updateWidget.reset(); + updateWidget.show(); + QTest::qWaitForWindowShown(&updateWidget); + QTest::qWait(50); + qApp->processEvents(); + QTRY_VERIFY(updateWidget.numPaintEvents > 0); + + { + updateWidget.reset(); + updateWidget.scroll(10, 10); + qApp->processEvents(); + QRegion dirty(QRect(0, 0, 500, 10)); + dirty += QRegion(QRect(0, 10, 10, 490)); + QCOMPARE(updateWidget.paintedRegion, dirty); + } + + { + updateWidget.reset(); + updateWidget.update(0, 0, 10, 10); + updateWidget.scroll(0, 10); + qApp->processEvents(); + QRegion dirty(QRect(0, 0, 500, 10)); + dirty += QRegion(QRect(0, 10, 10, 10)); + QCOMPARE(updateWidget.paintedRegion, dirty); + } + + { + updateWidget.reset(); + updateWidget.update(0, 0, 100, 100); + updateWidget.scroll(10, 10, QRect(50, 50, 100, 100)); + qApp->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)); + QCOMPARE(updateWidget.paintedRegion, dirty); + } + + { + updateWidget.reset(); + updateWidget.update(0, 0, 100, 100); + updateWidget.scroll(10, 10, QRect(100, 100, 100, 100)); + qApp->processEvents(); + QRegion dirty(QRect(0, 0, 100, 100)); + dirty += QRegion(QRect(100, 100, 100, 10)); + dirty += QRegion(QRect(100, 110, 10, 90)); + QCOMPARE(updateWidget.paintedRegion, dirty); + } +} +#endif + +class DestroyedSlotChecker : public QObject +{ + Q_OBJECT + +public: + bool wasQWidget; + + DestroyedSlotChecker() + : wasQWidget(false) + { + } + +public slots: + void destroyedSlot(QObject *object) + { + wasQWidget = (qobject_cast(object) != 0 || object->isWidgetType()); + } +}; + +/* + Test that qobject_cast returns 0 in a slot + connected to QObject::destroyed. +*/ +void tst_QWidget::qobject_castInDestroyedSlot() +{ + DestroyedSlotChecker checker; + QWidget *widget = new QWidget(); + + QObject::connect(widget, SIGNAL(destroyed(QObject *)), &checker, SLOT(destroyedSlot(QObject *))); + delete widget; + + QVERIFY(checker.wasQWidget == true); +} + +Q_DECLARE_METATYPE(QList) + +void tst_QWidget::setWindowGeometry_data() +{ + QTest::addColumn >("rects"); + QTest::addColumn("windowFlags"); + + QList > rects; + rects << (QList() + << QRect(100, 100, 200, 200) + << QApplication::desktop()->availableGeometry().adjusted(100, 100, -100, -100) + << QRect(130, 100, 0, 200) + << QRect(100, 50, 200, 0) + << QRect(130, 50, 0, 0)) + << (QList() + << QApplication::desktop()->availableGeometry().adjusted(100, 100, -100, -100) + << QRect(130, 100, 0, 200) + << QRect(100, 50, 200, 0) + << QRect(130, 50, 0, 0) + << QRect(100, 100, 200, 200)) + << (QList() + << QRect(130, 100, 0, 200) + << QRect(100, 50, 200, 0) + << QRect(130, 50, 0, 0) + << QRect(100, 100, 200, 200) + << QApplication::desktop()->availableGeometry().adjusted(100, 100, -100, -100)) + << (QList() + << QRect(100, 50, 200, 0) + << QRect(130, 50, 0, 0) + << QRect(100, 100, 200, 200) + << QApplication::desktop()->availableGeometry().adjusted(100, 100, -100, -100) + << QRect(130, 100, 0, 200)) + << (QList() + << QRect(130, 50, 0, 0) + << QRect(100, 100, 200, 200) + << QApplication::desktop()->availableGeometry().adjusted(100, 100, -100, -100) + << QRect(130, 100, 0, 200) + << QRect(100, 50, 200, 0)); + + QList windowFlags; + windowFlags << 0 + << Qt::FramelessWindowHint +#ifdef Q_WS_X11 + << Qt::X11BypassWindowManagerHint +#endif + ; + + foreach (QList l, rects) { + QRect rect = l.first(); + foreach (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).toAscii()) + << l + << windowFlag; + } + } +} + +void tst_QWidget::setWindowGeometry() +{ +#ifdef Q_WS_X11 + //Since WindowManager operation are all assync, and we have no way to know if the window + // manager has finished playing with the window geometry, this test can't be reliable. + QSKIP("Window Manager behaviour are too random for this test", SkipAll); +#endif + QFETCH(QList, 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 + foreach (QRect r, rects) { + widget.setGeometry(r); + QTest::qWait(100); + QCOMPARE(widget.geometry(), r); + } + } + + { + // setGeometry() first, then show() + QWidget widget; + if (windowFlags != 0) + widget.setWindowFlags(Qt::WindowFlags(windowFlags)); + + widget.setGeometry(rect); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(20); + QTRY_COMPARE(widget.geometry(), rect); + + // setGeometry() while shown + foreach (QRect r, 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() + foreach (QRect r, 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(); + QTest::qWaitForWindowShown(&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); + } + + { + // show() first, then setGeometry() + QWidget widget; + if (windowFlags != 0) + widget.setWindowFlags(Qt::WindowFlags(windowFlags)); + + widget.show(); + QTest::qWaitForWindowShown(&widget); + widget.setGeometry(rect); + QTest::qWait(10); + QTRY_COMPARE(widget.geometry(), rect); + + // setGeometry() while shown + foreach (QRect r, 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() + foreach (QRect r, 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(); + QTest::qWaitForWindowShown(&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_WS_WIN) && !defined(Q_OS_WINCE) +void tst_QWidget::setGeometry_win() +{ + QWidget widget; + widget.setGeometry(0, 600, 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(widget.internalWinId(), &rt); + QVERIFY(rt.left <= 0); + QVERIFY(rt.top <= 0); +} +#endif + +void tst_QWidget::windowMoveResize_data() +{ + setWindowGeometry_data(); +} + +void tst_QWidget::windowMoveResize() +{ +#ifdef Q_WS_X11 + //Since WindowManager operation are all assync, and we have no way to know if the window + // manager has finished playing with the window geometry, this test can't be reliable. + QSKIP("Window Manager behaviour are too random for this test", SkipAll); +#endif +#ifdef Q_OS_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#endif + QFETCH(QList, 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 + foreach (QRect r, 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; + if (windowFlags != 0) + widget.setWindowFlags(Qt::WindowFlags(windowFlags)); + + widget.move(rect.topLeft()); + widget.resize(rect.size()); + widget.show(); + + QTest::qWait(10); +#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) + QEXPECT_FAIL("130,50 0x0, flags 0", + "Showing a window with 0x0 size shifts it up.", + Continue); +#endif + QTRY_COMPARE(widget.pos(), rect.topLeft()); + QTRY_COMPARE(widget.size(), rect.size()); + + // move() while shown + foreach (QRect r, rects) { +#ifdef Q_WS_X11 + if ((widget.width() == 0 || widget.height() == 0) && r.width() != 0 && r.height() != 0) { + QEXPECT_FAIL("130,100 0x200, flags 0", + "First resize after show of zero-sized gets wrong win_gravity.", + Continue); + QEXPECT_FAIL("100,50 200x0, flags 0", + "First resize after show of zero-sized gets wrong win_gravity.", + Continue); + QEXPECT_FAIL("130,50 0x0, flags 0", + "First resize after show of zero-sized gets wrong win_gravity.", + Continue); + } +#endif + 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(); + QTest::qWait(10); + QTRY_COMPARE(widget.pos(), rect.topLeft()); + QTRY_COMPARE(widget.size(), rect.size()); + + // move() after hide() + foreach (QRect r, rects) { + widget.move(r.topLeft()); + widget.resize(r.size()); + QApplication::processEvents(); +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + 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(); + QTest::qWaitForWindowShown(&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.show(); + QTest::qWaitForWindowShown(&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 + foreach (QRect r, 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() + foreach (QRect r, rects) { + widget.move(r.topLeft()); + widget.resize(r.size()); + QApplication::processEvents(); +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + 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(); + QTest::qWaitForWindowShown(&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: + ColorWidget(QWidget *parent = 0, const QColor &c = QColor(Qt::red)) + : QWidget(parent, Qt::FramelessWindowHint), color(c) + { + QPalette opaquePalette = palette(); + opaquePalette.setColor(backgroundRole(), color); + setPalette(opaquePalette); + setAutoFillBackground(true); + } + + void paintEvent(QPaintEvent *e) { + r += e->region(); + } + + void reset() { + r = QRegion(); + } + + QColor color; + QRegion r; +}; + +template +void verifyColor(R const& region, C const& color) +{ + const QRegion r = QRegion(region); + for (int i = 0; i < r.rects().size(); ++i) { + const QRect rect = r.rects().at(i); + for (int t = 0; t < 5; t++) { + const QPixmap pixmap = QPixmap::grabWindow(QDesktopWidget().winId(), + rect.left(), rect.top(), + rect.width(), rect.height()); + QCOMPARE(pixmap.size(), rect.size()); + QPixmap expectedPixmap(pixmap); /* ensure equal formats */ + expectedPixmap.fill(color); + if (pixmap.toImage().pixel(0,0) != QColor(color).rgb() && t < 4 ) + { QTest::qWait(200); continue; } + QCOMPARE(pixmap.toImage().pixel(0,0), QColor(color).rgb()); + QCOMPARE(pixmap, expectedPixmap); + break; + } + } +} + +void tst_QWidget::moveChild_data() +{ + QTest::addColumn("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() +{ + QFETCH(QPoint, offset); + + ColorWidget parent; + // prevent custom styles + parent.setStyle(new QWindowsStyle); + ColorWidget child(&parent, Qt::blue); + +#ifndef Q_OS_WINCE + parent.setGeometry(QRect(QPoint(QApplication::desktop()->availableGeometry(&parent).topLeft()), + QSize(100, 100))); +#else + parent.setGeometry(60, 60, 150, 150); +#endif + child.setGeometry(25, 25, 50, 50); + QPoint childOffset = child.mapToGlobal(QPoint()); + + parent.show(); + QTest::qWaitForWindowShown(&parent); + QTest::qWait(30); + const QPoint tlwOffset = parent.geometry().topLeft(); + +#ifdef QT_MAC_USE_COCOA + QEXPECT_FAIL(0, "Cocoa compositor paints the entire content view, even when opaque", Continue); +#endif + QTRY_COMPARE(parent.r, QRegion(parent.rect()) - child.geometry()); + QTRY_COMPARE(child.r, QRegion(child.rect())); + verifyColor(child.geometry().translated(tlwOffset), + child.color); + verifyColor(QRegion(parent.geometry()) - child.geometry().translated(tlwOffset), + parent.color); + parent.reset(); + child.reset(); + + // move + + const QRect oldGeometry = child.geometry(); + + QPoint pos = child.pos() + offset; + child.move(pos); + QTest::qWait(100); + QTRY_COMPARE(pos, child.pos()); + + QCOMPARE(parent.r, QRegion(oldGeometry) - child.geometry()); +#if !defined(Q_WS_MAC) + // should be scrolled in backingstore + QCOMPARE(child.r, QRegion()); +#endif + verifyColor(child.geometry().translated(tlwOffset), + child.color); + verifyColor(QRegion(parent.geometry()) - child.geometry().translated(tlwOffset), + parent.color); +} + +void tst_QWidget::showAndMoveChild() +{ + QWidget parent(0, Qt::FramelessWindowHint); + // prevent custom styles + parent.setStyle(new QWindowsStyle); + + QDesktopWidget desktop; + QRect desktopDimensions = desktop.availableGeometry(&parent); + + parent.setGeometry(desktopDimensions); + parent.setPalette(Qt::red); + parent.show(); + QTest::qWaitForWindowShown(&parent); + QTest::qWait(10); + + const QPoint tlwOffset = parent.geometry().topLeft(); + 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); + qApp->processEvents(); + + verifyColor(child.geometry().translated(tlwOffset), Qt::blue); + verifyColor(QRegion(parent.geometry()) - child.geometry().translated(tlwOffset), Qt::red); +} + +void tst_QWidget::subtractOpaqueSiblings() +{ +#ifdef QT_MAC_USE_COCOA + QSKIP("Cocoa only has rect granularity.", SkipAll); +#else + QWidget w; + w.setGeometry(50, 50, 300, 300); + + ColorWidget *large = new ColorWidget(&w, Qt::red); + large->setGeometry(50, 50, 200, 200); + + ColorWidget *medium = new ColorWidget(large, Qt::gray); + medium->setGeometry(50, 50, 100, 100); + + ColorWidget *tall = new ColorWidget(&w, Qt::blue); + tall->setGeometry(100, 30, 50, 100); + + w.show(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(10); + + large->reset(); + medium->reset(); + tall->reset(); + + medium->update(); + QTest::qWait(10); + + // 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()); +#endif +} + +void tst_QWidget::deleteStyle() +{ + QWidget widget; + widget.setStyle(new QWindowsStyle); + widget.show(); + delete widget.style(); + qApp->processEvents(); +} + +#ifdef Q_WS_WIN +void tst_QWidget::getDC() +{ + QWidget widget; + widget.setGeometry(0, 0, 2, 4); + + HDC dc = widget.getDC(); + QVERIFY(dc != 0); + + widget.releaseDC(dc); +} +#endif + +class TopLevelFocusCheck: public QWidget +{ + Q_OBJECT +public: + QLineEdit* edit; + TopLevelFocusCheck(QWidget* parent = 0) : QWidget(parent) + { + edit = new QLineEdit(this); + edit->hide(); + edit->installEventFilter(this); + } + +public slots: + void mouseDoubleClickEvent ( QMouseEvent * /*event*/ ) + { + edit->show(); + edit->setFocus(Qt::OtherFocusReason); + qApp->processEvents(); + } + bool eventFilter(QObject *obj, QEvent *event) + { + if (obj == edit && event->type()== QEvent::FocusOut) { + edit->hide(); + return true; + } + return false; + } +}; + +void tst_QWidget::multipleToplevelFocusCheck() +{ + TopLevelFocusCheck w1; + TopLevelFocusCheck w2; + + w1.resize(200, 200); + w1.show(); + QTest::qWaitForWindowShown(&w1); + w2.resize(200,200); + w2.show(); + QTest::qWaitForWindowShown(&w2); + + QTest::qWait(100); + + QApplication::setActiveWindow(&w1); + w1.activateWindow(); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&w1)); + QTest::qWait(50); + QTest::mouseDClick(&w1, Qt::LeftButton); + QTRY_COMPARE(QApplication::focusWidget(), static_cast(w1.edit)); + + w2.activateWindow(); + QApplication::setActiveWindow(&w2); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&w2)); + QTest::mouseClick(&w2, Qt::LeftButton); +#ifdef Q_WS_QWS + QEXPECT_FAIL("", "embedded toplevels take focus anyway", Continue); +#endif + QTRY_COMPARE(QApplication::focusWidget(), (QWidget *)0); + + QTest::mouseDClick(&w2, Qt::LeftButton); + QTRY_COMPARE(QApplication::focusWidget(), static_cast(w2.edit)); + + w1.activateWindow(); + QApplication::setActiveWindow(&w1); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&w1)); + QTest::mouseDClick(&w1, Qt::LeftButton); + QTRY_COMPARE(QApplication::focusWidget(), static_cast(w1.edit)); + + w2.activateWindow(); + QApplication::setActiveWindow(&w2); + QApplication::processEvents(); + QTRY_COMPARE(QApplication::activeWindow(), static_cast(&w2)); + QTest::mouseClick(&w2, Qt::LeftButton); + QTRY_COMPARE(QApplication::focusWidget(), (QWidget *)0); +} + +void tst_QWidget::setFocus() +{ + { + // move focus to another window + testWidget->activateWindow(); + QApplication::setActiveWindow(testWidget); + if (testWidget->focusWidget()) + testWidget->focusWidget()->clearFocus(); + else + testWidget->clearFocus(); + + // window and children never shown, nobody gets focus + QWidget window; + + 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(), static_cast(0)); + + child2.setFocus(); + QVERIFY(!child2.hasFocus()); + QCOMPARE(window.focusWidget(), &child2); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + } + + { + // window and children show, but window not active, nobody gets focus + QWidget window; + + 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); + if (testWidget->focusWidget()) + testWidget->focusWidget()->clearFocus(); + else + testWidget->clearFocus(); + + child1.setFocus(); + QVERIFY(!child1.hasFocus()); + QCOMPARE(window.focusWidget(), &child1); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child2.setFocus(); + QVERIFY(!child2.hasFocus()); + QCOMPARE(window.focusWidget(), &child2); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + } + + { + // window and children show, but window *is* active, children get focus + QWidget window; + + QWidget child1(&window); + child1.setFocusPolicy(Qt::StrongFocus); + + QWidget child2(&window); + child2.setFocusPolicy(Qt::StrongFocus); + + window.show(); +#ifdef Q_WS_X11 + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); +#else + window.activateWindow(); + QApplication::processEvents(); +#endif + + 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); + } + + { + // window shown and active, children created, don't get focus, but get focus when shown + QWidget window; + + window.show(); +#ifdef Q_WS_X11 + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); +#else + window.activateWindow(); +#endif + + QWidget child1(&window); + child1.setFocusPolicy(Qt::StrongFocus); + + QWidget child2(&window); + child2.setFocusPolicy(Qt::StrongFocus); + + child1.setFocus(); + QVERIFY(!child1.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child1.show(); +#ifdef Q_WS_X11 + QApplication::setActiveWindow(&child1); + child1.activateWindow(); +#endif + 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.show(); +#ifdef Q_WS_X11 + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); +#else + window.activateWindow(); +#endif + + QWidget child1(&window); + child1.setFocusPolicy(Qt::StrongFocus); + + QWidget child2(&window); + child2.setFocusPolicy(Qt::StrongFocus); + + child1.setFocus(); + QVERIFY(!child1.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child1.hide(); + QVERIFY(!child1.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child1.show(); + QVERIFY(!child1.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child2.setFocus(); + QVERIFY(!child2.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child2.hide(); + QVERIFY(!child2.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + + child2.show(); + QVERIFY(!child2.hasFocus()); + QCOMPARE(window.focusWidget(), static_cast(0)); + QCOMPARE(QApplication::focusWidget(), static_cast(0)); + } +} + +class EventSpy : public QObject +{ +public: + EventSpy(QWidget *widget, QEvent::Type event) + : m_widget(widget), eventToSpy(event), m_count(0) + { + if (m_widget) + m_widget->installEventFilter(this); + } + + QWidget *widget() const { return m_widget; } + int count() const { return m_count; } + void clear() { m_count = 0; } + +protected: + bool eventFilter(QObject *object, QEvent *event) + { + if (event->type() == eventToSpy) + ++m_count; + return QObject::eventFilter(object, event); + } + +private: + QWidget *m_widget; + QEvent::Type eventToSpy; + int m_count; +}; + +void tst_QWidget::setCursor() +{ +#ifndef QT_NO_CURSOR + { + QWidget window; + 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; + 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; + 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; + QWidget child(&window); + + window.show(); + 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; + QWidget window2; + QWidget child(&window); + + window.setCursor(Qt::WaitCursor); + + child.setParent(0); + 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; + QWidget window2; + QWidget child(&window); + + window.setCursor(Qt::WaitCursor); + window.show(); + + child.setParent(0); + 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 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; + EventSpy 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); + +#ifdef Q_OS_WINCE_WM + QSKIP("Mouse over doesn't work on Windows mobile.", SkipAll); +#endif + + for (int pass = 0; pass < 2; ++pass) { + QWidget *popup = new QWidget(0, Qt::Popup); + popup->resize(150, 50); + QFrame *frame = new QFrame(popup); + frame->setGeometry(0, 0, 50, 50); + frame->setFrameStyle(QFrame::Box | QFrame::Plain); + EventSpy spy1(frame, QEvent::ToolTip); + EventSpy spy2(popup, QEvent::ToolTip); + frame->setMouseTracking(pass == 0 ? false : true); + frame->setToolTip(QLatin1String("TOOLTIP FRAME")); + popup->setToolTip(QLatin1String("TOOLTIP POPUP")); + popup->show(); + QTest::qWaitForWindowShown(popup); + QTest::qWait(10); + QTest::mouseMove(frame); + 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(popup); + delete popup; + } +} + +void tst_QWidget::testWindowIconChangeEventPropagation() +{ + // Create widget hierarchy. + QWidget topLevelWidget; + QWidget topLevelChild(&topLevelWidget); + + QDialog dialog(&topLevelWidget); + QWidget dialogChild(&dialog); + + QWidgetList widgets; + widgets << &topLevelWidget << &topLevelChild + << &dialog << &dialogChild; + QCOMPARE(widgets.count(), 4); + + // Create spy lists. + QList applicationEventSpies; + QList widgetEventSpies; + foreach (QWidget *widget, widgets) { + applicationEventSpies.append(new EventSpy(widget, QEvent::ApplicationWindowIconChange)); + widgetEventSpies.append(new EventSpy(widget, 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 + EventSpy *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(); + } + + // Set icon on a top-level widget. + topLevelWidget.setWindowIcon(*new QIcon); + + for (int i = 0; i < widgets.count(); ++i) { + // Check QEvent::ApplicationWindowIconChange + EventSpy *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. + for (int i = 0; i < widgets.count(); ++i) { + delete applicationEventSpies.at(i); + delete widgetEventSpies.at(i); + } + qApp->setWindowIcon(QIcon()); +} + +#ifdef Q_WS_X11 +void tst_QWidget::minAndMaxSizeWithX11BypassWindowManagerHint() +{ + // Same size as in QWidget::create_sys(). + const QSize desktopSize = QApplication::desktop()->size(); + const QSize originalSize(desktopSize.width() / 2, desktopSize.height() * 4 / 10); + + { // Maximum size. + QWidget widget(0, Qt::X11BypassWindowManagerHint); + + const QSize newMaximumSize = widget.size().boundedTo(originalSize) - QSize(10, 10); + widget.setMaximumSize(newMaximumSize); + QCOMPARE(widget.size(), newMaximumSize); + + widget.show(); + qt_x11_wait_for_window_manager(&widget); + QCOMPARE(widget.size(), newMaximumSize); + } + + { // Minimum size. + QWidget widget(0, Qt::X11BypassWindowManagerHint); + + const QSize newMinimumSize = widget.size().expandedTo(originalSize) + QSize(10, 10); + widget.setMinimumSize(newMinimumSize); + QCOMPARE(widget.size(), newMinimumSize); + + widget.show(); + qt_x11_wait_for_window_manager(&widget); + QCOMPARE(widget.size(), newMinimumSize); + } +} + +class ShowHideShowWidget : public QWidget +{ + Q_OBJECT + + int state; +public: + bool gotExpectedMapNotify; + + ShowHideShowWidget() + : state(0), gotExpectedMapNotify(false) + { + startTimer(1000); + } + + void timerEvent(QTimerEvent *) + { + switch (state++) { + case 0: + show(); + break; + case 1: + emit done(); + break; + } + } + + bool x11Event(XEvent *event) + { + if (state == 1 && event->type == MapNotify) + gotExpectedMapNotify = true; + return false; + } + +signals: + void done(); +}; + +void tst_QWidget::showHideShow() +{ + ShowHideShowWidget w; + w.show(); + w.hide(); + + QEventLoop eventLoop; + connect(&w, SIGNAL(done()), &eventLoop, SLOT(quit())); + eventLoop.exec(); + + QVERIFY(w.gotExpectedMapNotify); +} + +void tst_QWidget::clean_qt_x11_enforce_cursor() +{ + { + QWidget window; + QWidget *w = new QWidget(&window); + QWidget *child = new QWidget(w); + child->setAttribute(Qt::WA_SetCursor, true); + + window.show(); + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&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. +} +#endif + +class EventRecorder : public QObject +{ + Q_OBJECT + +public: + typedef QList > EventList; + + EventRecorder(QObject *parent = 0) + : QObject(parent) + { } + + EventList eventList() + { + return events; + } + + void clear() + { + events.clear(); + } + + bool eventFilter(QObject *object, QEvent *event) + { + QWidget *widget = qobject_cast(object); + if (widget && !event->spontaneous()) + events.append(qMakePair(widget, event->type())); + return false; + } + +private: + EventList events; +}; + +void tst_QWidget::compatibilityChildInsertedEvents() +{ + EventRecorder::EventList expected; + bool accessibilityEnabled = false; +#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) + accessibilityEnabled = AXAPIEnabled(); +#endif + + // 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(qApp->desktop()->availableGeometry().bottomRight()); + QTest::qWait(100); + + { + // no children created, not shown + QWidget widget; + 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)); + QCOMPARE(spy.eventList(), expected); + } + + { + // no children, shown + QWidget widget; + EventRecorder spy; + widget.installEventFilter(&spy); + + QCoreApplication::postEvent(&widget, new QEvent(QEvent::Type(QEvent::User + 1))); + + widget.show(); + expected = + EventRecorder::EventList() + << qMakePair(&widget, QEvent::Polish) + << qMakePair(&widget, QEvent::Move) + << qMakePair(&widget, QEvent::Resize) + << qMakePair(&widget, QEvent::Show); + + if (accessibilityEnabled) + expected << qMakePair(&widget, QEvent::AccessibilityPrepare); + expected << qMakePair(&widget, QEvent::ShowToParent); + QCOMPARE(spy.eventList(), expected); + spy.clear(); + + QCoreApplication::sendPostedEvents(); + expected = + EventRecorder::EventList() + << qMakePair(&widget, QEvent::PolishRequest) + << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) +#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_QWS) || defined(Q_WS_S60) + << qMakePair(&widget, QEvent::UpdateRequest) +#endif + ; + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, not shown + QWidget widget; + 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(); + + QCoreApplication::sendPostedEvents(); + expected = + EventRecorder::EventList() +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInsertedRequest) + << qMakePair(&widget, QEvent::ChildInserted) + << qMakePair(&widget, QEvent::ChildInserted) +#endif + << 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)); + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, widget shown + QWidget widget; + 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.show(); + expected = + EventRecorder::EventList() + << qMakePair(&widget, QEvent::Polish) +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInserted) + << qMakePair(&widget, QEvent::ChildInserted) +#endif + << qMakePair(&widget, QEvent::ChildPolished) + << qMakePair(&widget, QEvent::ChildPolished) + << qMakePair(&widget, QEvent::Move) + << qMakePair(&widget, QEvent::Resize) + << qMakePair(&widget, QEvent::Show); + + if (accessibilityEnabled) + expected << qMakePair(&widget, QEvent::AccessibilityPrepare); + expected << qMakePair(&widget, QEvent::ShowToParent); + QCOMPARE(spy.eventList(), expected); + spy.clear(); + + QCoreApplication::sendPostedEvents(); + expected = + EventRecorder::EventList() +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInsertedRequest) +#endif + << qMakePair(&widget, QEvent::PolishRequest) + << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) + << qMakePair(&widget, QEvent::Type(QEvent::User + 2)) +#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_QWS) || defined(Q_WS_S60) + << qMakePair(&widget, QEvent::UpdateRequest) +#endif + ; + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, but one is reparented away, not shown + QWidget widget; + 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(0); + + 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() +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInsertedRequest) + << qMakePair(&widget, QEvent::ChildInserted) +#endif + << 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)); + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, but one is reparented away, then widget is shown + QWidget widget; + 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(0); + + 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.show(); + expected = + EventRecorder::EventList() + << qMakePair(&widget, QEvent::Polish) +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInserted) +#endif + << qMakePair(&widget, QEvent::ChildPolished) + << qMakePair(&widget, QEvent::Move) + << qMakePair(&widget, QEvent::Resize) + << qMakePair(&widget, QEvent::Show); + + if (accessibilityEnabled) + expected << qMakePair(&widget, QEvent::AccessibilityPrepare); + expected << qMakePair(&widget, QEvent::ShowToParent); + QCOMPARE(spy.eventList(), expected); + spy.clear(); + + QCoreApplication::sendPostedEvents(); + expected = + EventRecorder::EventList() +#ifdef QT_HAS_QT3SUPPORT + << qMakePair(&widget, QEvent::ChildInsertedRequest) +#endif + << qMakePair(&widget, QEvent::PolishRequest) + << qMakePair(&widget, QEvent::Type(QEvent::User + 1)) + << qMakePair(&widget, QEvent::Type(QEvent::User + 2)) +#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_QWS) || defined(Q_WS_S60) + << qMakePair(&widget, QEvent::UpdateRequest) +#endif + ; + QCOMPARE(spy.eventList(), expected); + } +} + +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; + // 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(); + QTest::qWaitForWindowShown(&source); + + // Render the entire source into target. + RenderWidget target(&source); + target.resize(source.size()); + target.show(); + + qApp->processEvents(); + qApp->sendPostedEvents(); + QTest::qWait(250); + + QImage sourceImage = QPixmap::grabWidget(&source).toImage(); + qApp->processEvents(); + QImage targetImage = QPixmap::grabWidget(&target).toImage(); + qApp->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(); + qApp->processEvents(); + qApp->sendPostedEvents(); + + targetImage = QPixmap::grabWidget(&target).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.resize(100, 100); + // prevent custom styles + window.setStyle(new QWindowsStyle); + window.show(); + QTest::qWaitForWindowShown(&window); + QWidget child(&window); + child.resize(window.size()); + child.show(); + + qApp->processEvents(); + QCOMPARE(QPixmap::grabWidget(&child), QPixmap::grabWidget(&window)); + } + + { // Check that the target offset is correct. + QWidget widget; + widget.resize(200, 200); + widget.setAutoFillBackground(true); + widget.setPalette(Qt::red); + // prevent custom styles + widget.setStyle(new QWindowsStyle); + widget.show(); + QTest::qWaitForWindowShown(&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_WS_WIN + return; +#endif + if (!widget) + return; + + QWidget *navigationBar = qFindChild(widget, 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() +{ + QCalendarWidget *calendar = new QCalendarWidget; + // 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->show(); + QTest::qWaitForWindowShown(calendar); + + // Create a dummy focus widget to get rid of focus rect in reference image. + QLineEdit dummyFocusWidget; + dummyFocusWidget.show(); + QTest::qWaitForWindowShown(&dummyFocusWidget); + qApp->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); + qApp->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(); + qApp->processEvents(); + QTest::qWait(30); + workaroundPaletteIssue(calendar); + + { // 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). + delete calendar; + calendar = new QCalendarWidget; + calendar->setFont(f); + workaroundPaletteIssue(calendar); + + { // 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(); + qApp->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 = qFindChild(calendar, 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 = qFindChild(navigationBar, 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, QPoint()), nextMonthButton->size()); + QCOMPARE(testImage, referenceImage.copy(buttonRect)); + + // Restore palette. + nextMonthButton->setPalette(originalPalette); + } + + // Navigation bar isn't explicitly hidden anymore. + navigationBar->show(); + qApp->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); + qApp->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(); + QVERIFY(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; + // prevent custom styles + widget.setStyle(new QWindowsStyle); + 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::NonCosmeticDefaultPen | QPainter::TextAntialiasing); + QPainter::RenderHints oldRenderHints = painter.renderHints(); + widget.render(&painter); + QCOMPARE(painter.renderHints(), oldRenderHints); +} + +void tst_QWidget::render_task188133() +{ + QMainWindow mainWindow; +#if defined(QT3_SUPPORT) + mainWindow.setCentralWidget(new Q3TextEdit); +#endif + // Make sure QWidget::render does not trigger QWidget::repaint/update + // and asserts for Qt::WA_WState_Created. + QPixmap pixmap = QPixmap::grabWidget(&mainWindow); +} + +void tst_QWidget::render_task211796() +{ + class MyWidget : public QWidget + { + void resizeEvent(QResizeEvent *) + { + QPixmap pixmap(size()); + render(&pixmap); + } + }; + + { // Please don't die in a resize recursion. + MyWidget widget; + widget.resize(200, 200); + widget.show(); + } + + { // Same check with a deeper hierarchy. + QWidget 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); +} + +void tst_QWidget::render_windowOpacity() +{ +#ifdef Q_OS_WINCE + QSKIP("Window Opacity is not supported on Windows CE", SkipAll); +#endif + + 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()); +#ifdef Q_WS_X11 + if (expected.depth() < 24) { + QSKIP("This test won't give correct results with dithered pixmaps", SkipAll); + } +#endif + 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: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + painter.setOpacity(opacity); + QCOMPARE(painter.opacity(), opacity); + painter.fillRect(rect(), Qt::red); + } + qreal opacity; + }; + + MyWidget widget; + widget.resize(50, 50); + widget.opacity = opacity; + 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_WS_MAC + 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("autoFillBackground"); + QTest::addColumn("usePaintEvent"); + QTest::addColumn("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); + + Q_ASSERT_X(expectedColor != QColor(Qt::red), Q_FUNC_INFO, + "Qt::red is the reference color for the image, pick another color"); + + class MyWidget : public QWidget + { + public: + bool usePaintEvent; + void paintEvent(QPaintEvent *) + { + if (usePaintEvent) + QPainter(this).fillRect(rect(), Qt::green); + } + }; + + MyWidget widget; + widget.usePaintEvent = 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("size"); + QTest::addColumn("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); + const QRegion blueArea(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 *) + { + 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 *) + { + 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); + + const QTransform worldTransform = painter.worldTransform(); + const QTransform deviceTransform = painter.deviceTransform(); + + // 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(); + QVERIFY(oldSize != newSize); + + QLabel label2("why does it always rain on me?"); + 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("initial"); + QTest::addColumn("position"); + + QPoint p = QApplication::desktop()->availableGeometry().topLeft(); + + QTest::newRow("1") << p << (p + QPoint(10, 10)); + QTest::newRow("2") << (p + QPoint(10,10)) << p; +} + +void tst_QWidget::moveWindowInShowEvent() +{ +#ifdef Q_OS_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#endif + QFETCH(QPoint, initial); + QFETCH(QPoint, position); + + class MoveWindowInShowEventWidget : public QWidget + { + public: + QPoint position; + void showEvent(QShowEvent *) + { + move(position); + } + }; + + MoveWindowInShowEventWidget widget; + widget.resize(QSize(qApp->desktop()->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.show(); + #ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); + #endif + QTest::qWait(100); + // it should have moved + QCOMPARE(widget.pos(), position); +} + +void tst_QWidget::repaintWhenChildDeleted() +{ +#ifdef Q_WS_WIN + if (QSysInfo::WindowsVersion & QSysInfo::WV_VISTA) { + QTest::qWait(1000); + } +#endif + ColorWidget w(0, Qt::red); +#if !defined(Q_OS_WINCE) && !defined(Q_WS_S60) + QPoint startPoint = QApplication::desktop()->availableGeometry(&w).topLeft(); + startPoint.rx() += 50; + startPoint.ry() += 50; + w.setGeometry(QRect(startPoint, QSize(100, 100))); +#else + w.setGeometry(60, 60, 110, 110); +#endif + w.show(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(10); + QTRY_COMPARE(w.r, QRegion(w.rect())); + w.r = QRegion(); + + { + const QPoint tlwOffset = w.geometry().topLeft(); + ColorWidget child(&w, Qt::blue); + child.setGeometry(10, 10, 10, 10); + child.show(); + QTest::qWait(10); + QTRY_COMPARE(child.r, QRegion(child.rect())); + w.r = QRegion(); + } + + QTest::qWait(10); + QTRY_COMPARE(w.r, QRegion(10, 10, 10, 10)); +} + +// task 175114 +void tst_QWidget::hideOpaqueChildWhileHidden() +{ + ColorWidget w(0, Qt::red); +#if !defined(Q_OS_WINCE) && !defined(Q_WS_S60) + QPoint startPoint = QApplication::desktop()->availableGeometry(&w).topLeft(); + startPoint.rx() += 50; + startPoint.ry() += 50; + w.setGeometry(QRect(startPoint, QSize(100, 100))); +#else + w.setGeometry(60, 60, 110, 110); +#endif + + ColorWidget child(&w, Qt::blue); + child.setGeometry(10, 10, 80, 80); + + ColorWidget child2(&child, Qt::white); + child2.setGeometry(10, 10, 60, 60); + + w.show(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(10); + QTRY_COMPARE(child2.r, QRegion(child2.rect())); + child.r = QRegion(); + child2.r = QRegion(); + w.r = QRegion(); + + child.hide(); + child2.hide(); + QTest::qWait(100); + + QCOMPARE(w.r, QRegion(child.geometry())); + + child.show(); + QTest::qWait(100); + QCOMPARE(child.r, QRegion(child.rect())); + QCOMPARE(child2.r, QRegion()); +} + +void tst_QWidget::updateWhileMinimized() +{ +#if defined(Q_OS_WINCE) || defined(Q_WS_QWS) + QSKIP("This test doesn't make sense without support for showMinimized()", SkipAll); +#endif + + UpdateWidget widget; + // Filter out activation change and focus events to avoid update() calls in QWidget. + widget.updateOnActivationChangeAndFocusIn = false; + widget.reset(); + widget.show(); + QTest::qWaitForWindowShown(&widget); + QApplication::processEvents(); + 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); + QCOMPARE(widget.numPaintEvents, 0); + + // Restore window. + widget.showNormal(); + QTest::qWait(30); + QTRY_COMPARE(widget.numPaintEvents, 1); + QCOMPARE(widget.paintedRegion, QRegion(0, 0, 50, 50)); +} + +#if defined(Q_WS_WIN) || defined(Q_WS_X11) +class PaintOnScreenWidget: public QWidget +{ +public: + PaintOnScreenWidget(QWidget *parent = 0, Qt::WindowFlags f = 0) + :QWidget(parent, f) + { + } +#if defined(Q_WS_WIN) + // This is the only way to enable PaintOnScreen on Windows. + QPaintEngine * paintEngine () const {return 0;} +#endif +}; + +void tst_QWidget::alienWidgets() +{ + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + QWidget parent; + QWidget child(&parent); + QWidget grandChild(&child); + QWidget greatGrandChild(&grandChild); + parent.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&parent); +#endif + + // 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; + QWidget widget(&window); + QWidget child(&widget); + child.setAttribute(Qt::WA_NativeWindow); + child.setAttribute(Qt::WA_DontCreateNativeAncestors); + window.show(); + QVERIFY(child.testAttribute(Qt::WA_Mapped)); + widget.hide(); + QVERIFY(!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); + QVERIFY(paintOnScreen.testAttribute(Qt::WA_NativeWindow)); + QVERIFY(paintOnScreen.internalWinId()); + QVERIFY(paintOnScreenChild.testAttribute(Qt::WA_NativeWindow)); + QVERIFY(paintOnScreenChild.internalWinId()); + + // Check that widgets with the Qt::MSWindowsOwnDC attribute set + // are native. + QWidget msWindowsOwnDC(&parent, Qt::MSWindowsOwnDC); + 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()); +#ifdef Q_WS_X11 + widget.handle(); +#else + widget.getDC(); +#endif + QVERIFY(widget.internalWinId()); + } + +#ifdef Q_WS_X11 +#ifndef QT_NO_XRENDER + { // Enforce a native window when calling QWidget::x11PictureHandle(). + QWidget widget(&parent); + widget.show(); + QVERIFY(widget.testAttribute(Qt::WA_WState_Created)); + QVERIFY(!widget.internalWinId()); + widget.x11PictureHandle(); + QVERIFY(widget.internalWinId()); + } +#endif + + { // Make sure we don't create native windows when setting Qt::WA_X11NetWmWindowType attributes + // on alien widgets (see task 194231). + QWidget dummy; + QVERIFY(dummy.winId()); + QWidget widget(&dummy); + widget.setAttribute(Qt::WA_X11NetWmWindowTypeToolBar); + QVERIFY(!widget.internalWinId()); + } +#endif + + + { // Make sure we create native ancestors when setting Qt::WA_PaintOnScreen before show(). + QWidget topLevel; + 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(); + QVERIFY(child.internalWinId()); + QVERIFY(grandChild.internalWinId()); + QVERIFY(greatGrandChild.internalWinId()); + } + + { // Ensure that widgets reparented into Qt::WA_PaintOnScreen widgets become native. + QWidget topLevel; + 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; + 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()); + QVERIFY(child.internalWinId()); + QVERIFY(child.testAttribute(Qt::WA_NativeWindow)); + QVERIFY(grandChild.internalWinId()); + QVERIFY(grandChild.testAttribute(Qt::WA_NativeWindow)); + QVERIFY(paintOnScreenWidget.internalWinId()); + 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; + QWidget *toolBar = new QWidget(&mainWindow); + QWidget *dockWidget = new QWidget(&mainWindow); + QWidget *centralWidget = new QWidget(&mainWindow); + + 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)); + } +} +#endif // Q_WS_WIN / Q_WS_X11 + +class ASWidget : public QWidget +{ +public: + ASWidget(QSize sizeHint, QSizePolicy sizePolicy, bool layout, bool hfwLayout, QWidget *parent = 0) + : QWidget(parent), mySizeHint(sizeHint) + { + setSizePolicy(sizePolicy); + if (layout) { + QSizePolicy sp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sp.setHeightForWidth(hfwLayout); + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->setMargin(0); + vbox->addWidget(new ASWidget(sizeHint + QSize(30, 20), sp, false, false)); + setLayout(vbox); + } + } + + QSize sizeHint() const { + if (layout()) + return layout()->totalSizeHint(); + return mySizeHint; + } + int heightForWidth(int width) const { + if (sizePolicy().hasHeightForWidth()) { + return width * 2; + } else { + return -1; + } + } + + QSize mySizeHint; +}; + +void tst_QWidget::adjustSize_data() +{ + const int MagicW = 200; + const int MagicH = 100; + + QTest::addColumn("sizeHint"); + QTest::addColumn("hPolicy"); + QTest::addColumn("vPolicy"); + QTest::addColumn("hfwSP"); + QTest::addColumn("layout"); + QTest::addColumn("hfwLayout"); + QTest::addColumn("haveParent"); + QTest::addColumn("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); + + 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 : 0); + child->resize(123, 456); + child->adjustSize(); + if (expectedSize == QSize(100000, 100000)) { + QVERIFY(child->size().width() < sizeHint.width()); + QVERIFY(child->size().height() < sizeHint.height()); + } else { +#if defined (Q_OS_WINCE) + if (!haveParent) { + const QRect& desktopRect = qApp->desktop()->availableGeometry(); + expectedSize.setWidth(qMin(expectedSize.width(), desktopRect.width())); + expectedSize.setHeight(qMin(expectedSize.height(), desktopRect.height())); + } +#endif + QCOMPARE(child->size(), expectedSize); + } + + delete parent; +} + +class TestLayout : public QVBoxLayout +{ + Q_OBJECT +public: + TestLayout(QWidget *w = 0) : QVBoxLayout(w) + { + invalidated = false; + } + + void invalidate() + { + invalidated = true; + } + + bool invalidated; +}; + +void tst_QWidget::updateGeometry_data() +{ + QTest::addColumn("minSize"); + QTest::addColumn("shouldInvalidate"); + QTest::addColumn("maxSize"); + QTest::addColumn("shouldInvalidate2"); + QTest::addColumn("verticalSizePolicy"); + QTest::addColumn("shouldInvalidate3"); + QTest::addColumn("setVisible"); + QTest::addColumn("shouldInvalidate4"); + + QTest::newRow("setMinimumSize") + << QSize(100, 100) << true + << QSize() << false + << int(QSizePolicy::Preferred) << false + << true << false; + QTest::newRow("setMaximumSize") + << QSize() << false + << QSize(100, 100) << true + << int(QSizePolicy::Preferred) << false + << true << false; + QTest::newRow("setMinimumSize, then maximumSize to a different size") + << QSize(100, 100) << true + << QSize(300, 300) << true + << int(QSizePolicy::Preferred) << false + << true << false; + QTest::newRow("setMinimumSize, then maximumSize to the same size") + << QSize(100, 100) << true + << QSize(100, 100) << true + << int(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 + << int(QSizePolicy::Preferred) << false + << false << true; + QTest::newRow("Change sizePolicy") + << QSize() << false + << QSize() << false + << int(QSizePolicy::Minimum) << true + << true << false; + +} + +void tst_QWidget::updateGeometry() +{ + QFETCH(QSize, minSize); + QFETCH(bool, shouldInvalidate); + QFETCH(QSize, maxSize); + QFETCH(bool, shouldInvalidate2); + QFETCH(int, verticalSizePolicy); + QFETCH(bool, shouldInvalidate3); + QFETCH(bool, setVisible); + QFETCH(bool, shouldInvalidate4); + QWidget parent; + 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, (QSizePolicy::Policy)verticalSizePolicy)); + if (shouldInvalidate3) + QCOMPARE(lout->invalidated, true); + + lout->invalidated = false; + if (!setVisible) + child->setVisible(false); + QCOMPARE(lout->invalidated, shouldInvalidate4); +} + +void tst_QWidget::sendUpdateRequestImmediately() +{ +#ifdef Q_WS_MAC + if (!QApplicationPrivate::graphicsSystem()) + QSKIP("We only send update requests on the Mac when passing -graphicssystem", SkipAll); +#endif + + UpdateWidget updateWidget; + updateWidget.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&updateWidget); +#endif + + qApp->processEvents(); +#ifdef Q_WS_QWS + QApplication::sendPostedEvents(); //glib workaround +#endif + updateWidget.reset(); + + QCOMPARE(updateWidget.numUpdateRequestEvents, 0); + updateWidget.repaint(); + QCOMPARE(updateWidget.numUpdateRequestEvents, 1); +} + +class RedirectedWidget : public QWidget +{ +protected: + void paintEvent(QPaintEvent *) + { + // Verify that the widget has a redirection set. The widget is redirected to + // the backing store on all platforms using it; otherwise to itself if the wrect + // does not start in (0, 0) or it has a mask set. + QPaintDevice *oldRedirection = QPainter::redirected(this); +#ifndef Q_WS_MAC + QVERIFY(oldRedirection); +#endif + + QImage image(size(), QImage::Format_RGB32); + image.fill(Qt::blue); + + { + QPainter painter(this); + QCOMPARE(painter.device(), static_cast(this)); + } + + QPainter::setRedirected(this, &image); + QCOMPARE(QPainter::redirected(this), static_cast(&image)); + + QPainter painter(this); + painter.fillRect(rect(), Qt::red); + + QPainter::restoreRedirected(this); + QCOMPARE(QPainter::redirected(this), oldRedirection); + + 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()); + } + +}; + +// Test to make sure we're compatible in the particular case where QPainter::setRedirected +// actually works. It has been broken for all other cases since Qt 4.1.4 (backing store). +// QWidget::render is the modern and more powerful way of doing the same. +void tst_QWidget::painterRedirection() +{ + RedirectedWidget widget; + // Set FramelessWindowHint and mask to trigger internal painter redirection on the Mac. + widget.setWindowFlags(widget.windowFlags() | Qt::FramelessWindowHint); + widget.setMask(QRect(10, 10, 50, 50)); + widget.setFixedSize(100, 200); + widget.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); +#endif + QPixmap pixmap(widget.size()); + QPainter::setRedirected(&widget, &pixmap, QPoint()); + widget.repaint(); + QCOMPARE(QPainter::redirected(&widget), static_cast(&pixmap)); +} + + +void tst_QWidget::doubleRepaint() +{ +#ifdef Q_OS_IRIX + QSKIP("4DWM issues on IRIX makes this test fail", SkipAll); +#elif defined(Q_WS_MAC) + if (!macHasAccessToWindowsServer()) + QSKIP("Not having window server access causes the wrong number of repaints to be issues", SkipAll); +#endif + UpdateWidget 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(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(10); + QTRY_COMPARE(widget.numPaintEvents, expectedRepaints); + widget.numPaintEvents = 0; + + // Minmize: Should not trigger a repaint. + widget.showMinimized(); + QTest::qWait(10); + QCOMPARE(widget.numPaintEvents, 0); + widget.numPaintEvents = 0; + + // Restore: Should not trigger a repaint. + widget.showNormal(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(10); + QCOMPARE(widget.numPaintEvents, 0); +} + +#ifndef Q_WS_MAC +// This test only makes sense on the Mac when passing -graphicssystem. +void tst_QWidget::resizeInPaintEvent() +{ + QWidget window; + UpdateWidget widget(&window); + window.show(); + QTest::qWaitForWindowShown(&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 + // invalidateBuffer() and a new update request should be posted. + widget.repaint(); + QCOMPARE(widget.numPaintEvents, 1); + widget.numPaintEvents = 0; + + QTest::qWait(10); + // Make sure the resize triggers another update. + QTRY_COMPARE(widget.numPaintEvents, 1); +} +#endif + + +class MaskSetWidget : public QWidget +{ + Q_OBJECT +public: + MaskSetWidget(QWidget* p =0) + : QWidget(p) {} + + void paintEvent(QPaintEvent* event) { + QPainter p(this); + + paintedRegion += event->region(); + foreach(QRect r, event->region().rects()) + p.fillRect(r, Qt::red); + } + + void resizeEvent(QResizeEvent*) { + 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.reset(); + w.resize(200, 200); + 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(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(30); + 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. + QCOMPARE(w.paintedRegion, expectedParentUpdate); + QCOMPARE(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: + MoveInResizeWidget(QWidget* p = 0) + : QWidget(p) + { + setWindowFlags(Qt::FramelessWindowHint); + } + + void resizeEvent(QResizeEvent*) { + + move(QPoint(100,100)); + + static bool firstTime = true; + if (firstTime) + QTimer::singleShot(250, this, SLOT(resizeMe())); + + firstTime = false; + } + +public slots: + void resizeMe() { + resize(100, 100); + } +}; + +void tst_QWidget::moveInResizeEvent() +{ + MoveInResizeWidget testWidget; + testWidget.setGeometry(50, 50, 200, 200); + testWidget.show(); + QTest::qWaitForWindowShown(&testWidget); + QTest::qWait(300); + + QRect expectedGeometry(100,100, 100, 100); + QTRY_COMPARE(testWidget.geometry(), expectedGeometry); +} + + +#if defined(Q_WS_WIN) || defined(Q_WS_X11) +void tst_QWidget::immediateRepaintAfterShow() +{ + UpdateWidget widget; + widget.show(); + qApp->processEvents(); + // On X11 in particular, we are now waiting for a MapNotify event before + // syncing the backing store. However, if someone request a repaint() + // we must repaint immediately regardless of the current state. + widget.numPaintEvents = 0; + widget.repaint(); + QCOMPARE(widget.numPaintEvents, 1); +} + +void tst_QWidget::immediateRepaintAfterInvalidateBuffer() +{ + QWidget *widget = new UpdateWidget; + widget->show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(widget); +#endif + QTest::qWait(200); + + static_cast(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)->invalidateBuffer(widget->rect()); + QCOMPARE(static_cast(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(static_cast(widget)->numPaintEvents, 1); + + delete widget; +} +#endif + +void tst_QWidget::effectiveWinId() +{ + QWidget parent; + QWidget child(&parent); + + // Shouldn't crash. + QVERIFY(!parent.effectiveWinId()); + QVERIFY(!child.effectiveWinId()); + + parent.show(); + + QVERIFY(parent.effectiveWinId()); + QVERIFY(child.effectiveWinId()); +} + +class CustomWidget : public QWidget +{ +public: + mutable int metricCallCount; + + CustomWidget(QWidget *parent = 0) : QWidget(parent), metricCallCount(0) {} + + virtual int metric(PaintDeviceMetric metric) const { + ++metricCallCount; + return QWidget::metric(metric); + } +}; + +void tst_QWidget::customDpi() +{ + QWidget *topLevel = new QWidget; + CustomWidget *custom = new CustomWidget(topLevel); + QWidget *child = new QWidget(custom); + + custom->metricCallCount = 0; + topLevel->logicalDpiX(); + QCOMPARE(custom->metricCallCount, 0); + custom->logicalDpiX(); + QCOMPARE(custom->metricCallCount, 1); + child->logicalDpiX(); +#ifdef Q_WS_S60 + // QWidget::metric is not recursive on Symbian + QCOMPARE(custom->metricCallCount, 1); +#else + QCOMPARE(custom->metricCallCount, 2); +#endif + + delete topLevel; +} + +void tst_QWidget::customDpiProperty() +{ + QWidget *topLevel = new QWidget; + QWidget *middle = new CustomWidget(topLevel); + 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); + + delete topLevel; +} + +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.setUpdatesEnabled(false); + QWidget child(&widget); + child.setUpdatesEnabled(false); + child.setAttribute(Qt::WA_OpaquePaintEvent); + widget.show(); + QTest::qWait(200); + child.move(10, 10); // Don't crash. +} + +#ifdef Q_WS_WIN +class GDIWidget : public QDialog +{ +public: + GDIWidget() { setAttribute(Qt::WA_PaintOnScreen); } + QPaintEngine *paintEngine() const { return 0; } + + + void paintEvent(QPaintEvent *) { + HDC hdc = getDC(); + SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0))); + Rectangle(hdc, 0, 0, 10, 10); + + releaseDC(hdc); + + QImage im = QPixmap::grabWindow(winId(), 0, 0, width(), height()).toImage(); + color = im.pixel(1, 1); + + accept(); + } + + QSize sizeHint() const { + return QSize(400, 300); + } + + QColor color; +}; + +void tst_QWidget::gdiPainting() +{ + GDIWidget w; + w.exec(); + + QCOMPARE(w.color, QColor(255, 0, 0)); + +} + +void tst_QWidget::paintOnScreenPossible() +{ + QWidget w1; + 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 + +void tst_QWidget::reparentStaticWidget() +{ + QWidget window1; + + QWidget *child = new QWidget(&window1); + child->setPalette(Qt::red); + child->setAutoFillBackground(true); + child->setAttribute(Qt::WA_StaticContents); + child->resize(100, 100); + + QWidget *grandChild = new QWidget(child); + grandChild->setPalette(Qt::blue); + grandChild->setAutoFillBackground(true); + grandChild->resize(50, 50); + grandChild->setAttribute(Qt::WA_StaticContents); + window1.show(); + QTest::qWaitForWindowShown(&window1); + + QWidget window2; + window2.show(); + QTest::qWaitForWindowShown(&window2); + QTest::qWait(20); + + // 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(0); + 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.setAttribute(Qt::WA_PaintOnScreen); + paintOnScreen.show(); + QTest::qWaitForWindowShown(&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); +} + +#ifdef Q_WS_QWS +void tst_QWidget::updateOutsideSurfaceClip() +{ + UpdateWidget widget; + widget.setWindowFlags(Qt::FramelessWindowHint); + widget.resize(100, 100); + widget.raise(); + widget.show(); + QTest::qWait(200); + widget.reset(); + + // Move widget partially outside buffer and change the surface clip. + widget.move(-50, 0); + QTest::qWait(100); + + // Update region is outside the surface clip and should not trigger a repaint. + widget.update(0, 0, 20, 20); + QTest::qWait(100); + QCOMPARE(widget.numPaintEvents, 0); + + // Now, move the widget back so that the update region is inside the clip + // and make sure we get a repaint of the dirty area. + widget.move(0, 0); + QTest::qWait(100); + QCOMPARE(widget.numPaintEvents, 1); + QCOMPARE(widget.paintedRegion, QRegion(0, 0, 20, 20)); +} +#endif +class ColorRedWidget : public QWidget +{ +public: + ColorRedWidget(QWidget *parent = 0) + : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip) + { + } + + void paintEvent(QPaintEvent *) { + QPainter p(this); + p.fillRect(rect(),Qt::red); + } +}; + +void tst_QWidget::translucentWidget() +{ + QPixmap pm(16,16); + pm.fill(Qt::red); + ColorRedWidget label; + label.setFixedSize(16,16); + label.setAttribute(Qt::WA_TranslucentBackground); + label.move(qApp->desktop()->availableGeometry().topLeft()); + label.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&label); +#endif + QTest::qWait(200); + + QPixmap widgetSnapshot = QPixmap::grabWindow(label.winId()); + QImage actual = widgetSnapshot.toImage().convertToFormat(QImage::Format_RGB32); + QImage expected = pm.toImage().convertToFormat(QImage::Format_RGB32); + QCOMPARE(actual.size(),expected.size()); + QCOMPARE(actual,expected); +} + +class MaskResizeTestWidget : public QWidget +{ + Q_OBJECT +public: + MaskResizeTestWidget(QWidget* p =0) + : QWidget(p) { + setMask(QRegion(QRect(0, 0, 100, 100).normalized())); + } + + void paintEvent(QPaintEvent* event) { + QPainter p(this); + + paintedRegion += event->region(); + foreach(QRect r, event->region().rects()) + 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.resize(150, 150); + topLevel.show(); + QTest::qWaitForWindowShown(&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); +#if defined(Q_WS_WIN) || defined(Q_WS_X11) // We don't control what's happening on other platforms. + // and ensure that the top-level doesn't get any update. + QCOMPARE(topLevel.numPaintEvents, 0); +#endif + + topLevel.reset(); + + // Clear top-level mask + topLevel.clearMask(); + QCOMPARE(topLevel.mask(), QRegion()); + QTest::qWait(10); + QRegion outsideOldMask(topLevel.rect()); + outsideOldMask -= topLevelMask; +#if defined(Q_WS_WIN) || defined(Q_WS_X11) // We don't control what's happening on other platforms. + // and ensure that the top-level gets an update for the area outside the old mask. + QTRY_VERIFY(topLevel.numPaintEvents > 0); + QTRY_COMPARE(topLevel.paintedRegion, outsideOldMask); +#endif + + UpdateWidget child(&topLevel); + child.setAutoFillBackground(true); // NB! Opaque child. + 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); + QTest::qWait(50); + // and ensure that the child widget doesn't get any update. +#ifdef Q_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + QCOMPARE(child.numPaintEvents, 1); +#else + QCOMPARE(child.numPaintEvents, 0); +#endif + // 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()); + QTest::qWait(10); + // 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(); +#ifndef Q_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + outsideOldMask -= childMask; +#endif + 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)); + QTest::qWait(100); +#ifdef Q_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + QTRY_COMPARE(child.numPaintEvents, 1); +#else + // and ensure that we don't get any updates at all. + QTRY_COMPARE(child.numPaintEvents, 0); +#endif + QCOMPARE(topLevel.numPaintEvents, 0); + + // ...and the same applies when clearing the mask. + child.clearMask(); + QTest::qWait(100); +#ifdef Q_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + QTRY_VERIFY(child.numPaintEvents > 0); +#else + QCOMPARE(child.numPaintEvents, 0); +#endif + 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(); + QTest::qWaitForWindowShown(&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_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask()); +#else + QTRY_COMPARE(resizeChild.paintedRegion, QRegion()); +#endif + + resizeChild.paintedRegion = QRegion(); + const QRegion oldMask = resizeChild.mask(); + QTimer::singleShot(0, &resizeChild, SLOT(enlargeMask())); + QTest::qWait(100); +#ifdef Q_WS_MAC + // Mac always issues a full update when calling setMask, and we cannot force it to not do so. + QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask()); +#else + QTRY_COMPARE(resizeChild.paintedRegion, resizeChild.mask() - oldMask); +#endif +} + +void tst_QWidget::maskedUpdate() +{ + UpdateWidget topLevel; + topLevel.resize(200, 200); + 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(); + QTest::qWaitForWindowShown(&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. +} + +#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_QWS) +void tst_QWidget::syntheticEnterLeave() +{ + class MyWidget : public QWidget + { + public: + MyWidget(QWidget *parent = 0) : QWidget(parent), numEnterEvents(0), numLeaveEvents(0) {} + void enterEvent(QEvent *) { ++numEnterEvents; } + void leaveEvent(QEvent *) { ++numLeaveEvents; } + int numEnterEvents; + int numLeaveEvents; + }; + + QCursor::setPos(QPoint(0,0)); + + MyWidget window; + window.setWindowFlags(Qt::WindowStaysOnTopHint); + 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(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QTest::qWait(300); + +#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); + +#ifdef Q_OS_WINCE_WM + QSKIP("Windows Mobile has no proper cursor support", SkipAll); +#endif + + QCOMPARE(window.numLeaveEvents, 0); + QCOMPARE(child2->numLeaveEvents, 0); + QCOMPARE(grandChild->numLeaveEvents, 0); + QCOMPARE(child1->numLeaveEvents, 0); + + QCOMPARE(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); +} + +void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() +{ +#ifdef Q_OS_WINCE_WM + QSKIP("Windows Mobile has no proper cursor support", SkipAll); +#endif + class SELParent : public QWidget + { + public: + SELParent(QWidget *parent = 0): QWidget(parent) { } + + void mousePressEvent(QMouseEvent *) { child->show(); } + QWidget *child; + }; + + class SELChild : public QWidget + { + public: + SELChild(QWidget *parent = 0) : QWidget(parent), numEnterEvents(0), numMouseMoveEvents(0) {} + void enterEvent(QEvent *) { ++numEnterEvents; } + void mouseMoveEvent(QMouseEvent *event) + { + QCOMPARE(event->button(), Qt::NoButton); + QCOMPARE(event->buttons(), Qt::MouseButtons(Qt::NoButton)); + ++numMouseMoveEvents; + } + void reset() { numEnterEvents = numMouseMoveEvents = 0; } + int numEnterEvents, numMouseMoveEvents; + }; + + QCursor::setPos(QPoint(0,0)); + + SELParent parent; + parent.resize(200, 200); + SELChild child(&parent); + child.resize(200, 200); + parent.show(); + #ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&parent); + #endif + QTest::qWait(150); + + 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. + QCOMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + + child.hide(); + child.reset(); + child.setMouseTracking(true); + child.show(); + + // Make sure the child gets enter event and mouse move event. + // Note that we verify event->button() and event->buttons() + // in SELChild::mouseMoveEvent(). + QCOMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 1); + + // 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 and one mouse move event. + QCOMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 1); + + child.hide(); + child.reset(); + child.setMouseTracking(false); + QTest::mouseClick(&parent, Qt::LeftButton); + + // Make sure the child gets enter event and no mouse move event. + QCOMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + } +#endif + +void tst_QWidget::windowFlags() +{ + QWidget w; + w.setWindowFlags(w.windowFlags() | Qt::FramelessWindowHint); + QVERIFY(w.windowFlags() & Qt::FramelessWindowHint); +} + +void tst_QWidget::initialPosForDontShowOnScreenWidgets() +{ + { // Check default position. + const QPoint expectedPos(0, 0); + QWidget widget; + widget.setAttribute(Qt::WA_DontShowOnScreen); + widget.winId(); // Make sure create_sys 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 create_sys is called. + QCOMPARE(widget.pos(), expectedPos); + QCOMPARE(widget.geometry().topLeft(), expectedPos); + } +} + +#ifdef Q_WS_X11 +void tst_QWidget::paintOutsidePaintEvent() +{ + QWidget widget; + widget.resize(200, 200); + + QWidget child1(&widget); + child1.resize(100, 100); + child1.setPalette(Qt::red); + child1.setAutoFillBackground(true); + + QWidget child2(&widget); + child2.setGeometry(50, 50, 100, 100); + child2.setPalette(Qt::blue); + child2.setAutoFillBackground(true); + + widget.show(); + QTest::qWaitForWindowShown(&widget); + QTest::qWait(60); + + const QPixmap before = QPixmap::grabWindow(widget.winId()); + + // Child 1 should be clipped by child 2, so nothing should change. + child1.setAttribute(Qt::WA_PaintOutsidePaintEvent); + QPainter painter(&child1); + painter.fillRect(child1.rect(), Qt::red); + painter.end(); + XSync(QX11Info::display(), false); // Flush output buffer. + QTest::qWait(60); + + const QPixmap after = QPixmap::grabWindow(widget.winId()); + + QCOMPARE(before, after); +} +#endif + +class MyEvilObject : public QObject +{ + Q_OBJECT +public: + MyEvilObject(QWidget *widgetToCrash) : QObject(), widget(widgetToCrash) + { + connect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(beEvil(QObject *))); + delete widget; + } + QWidget *widget; + +private slots: + void beEvil(QObject *) { widget->update(0, 0, 150, 150); } +}; + +void tst_QWidget::updateOnDestroyedSignal() +{ + QWidget widget; + + QWidget *child = new QWidget(&widget); + child->resize(100, 100); + child->setAutoFillBackground(true); + child->setPalette(Qt::red); + + widget.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); +#endif + QTest::qWait(200); + + // Please do not crash. + MyEvilObject evil(child); + QTest::qWait(200); +} + +void tst_QWidget::toplevelLineEditFocus() +{ + testWidget->hide(); + + QLineEdit w; + w.show(); + QTest::qWaitForWindowShown(&w); + QTest::qWait(20); + + QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&w); + QTRY_COMPARE(QApplication::focusWidget(), (QWidget*)&w); +} + +void tst_QWidget::focusWidget_task254563() +{ + //having different visibility for widget is important + QWidget top; + 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 +} + +void tst_QWidget::destroyBackingStore() +{ +#ifdef QT_BUILD_INTERNAL + UpdateWidget w; + w.reset(); + w.show(); + + QTest::qWaitForWindowShown(&w); + QApplication::processEvents(); + QTRY_VERIFY(w.numPaintEvents > 0); + w.reset(); + w.update(); + delete qt_widget_private(&w)->topData()->backingStore; + qt_widget_private(&w)->topData()->backingStore = 0; + qt_widget_private(&w)->topData()->backingStore = new QWidgetBackingStore(&w); + + w.update(); + QApplication::processEvents(); +#ifdef Q_WS_QWS + QApplication::processEvents(); +#endif + 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 +} + +void tst_QWidget::rectOutsideCoordinatesLimit_task144779() +{ +#ifdef Q_OS_WINCE_WM + QSKIP( "Tables of 5000 elements do not make sense on Windows Mobile.", SkipAll); +#endif + QApplication::setOverrideCursor(Qt::BlankCursor); //keep the cursor out of screen grabs + QWidget main(0,Qt::FramelessWindowHint); //don't get confused by the size of the window frame + QPalette palette; + palette.setColor(QPalette::Window, Qt::red); + main.setPalette(palette); + + QDesktopWidget desktop; + QRect desktopDimensions = desktop.availableGeometry(&main); + 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.show(); + QTest::qWaitForWindowShown(&main); + + QPixmap correct(main.size()); + correct.fill(Qt::green); + + QTRY_COMPARE(QPixmap::grabWindow(main.winId()).toImage().convertToFormat(QImage::Format_RGB32), + correct.toImage().convertToFormat(QImage::Format_RGB32)); + QApplication::restoreOverrideCursor(); +} + +void tst_QWidget::inputFocus_task257832() +{ + QLineEdit *widget = new QLineEdit; + QInputContext *context = widget->inputContext(); + if (!context) + QSKIP("No input context", SkipSingle); + widget->setFocus(); + widget->winId(); // make sure, widget has been created + context->setFocusWidget(widget); + QCOMPARE(context->focusWidget(), static_cast(widget)); + widget->setReadOnly(true); + QVERIFY(!context->focusWidget()); + delete widget; +} + +void tst_QWidget::setGraphicsEffect() +{ + // Check that we don't have any effect by default. + QWidget *widget = new QWidget; + QVERIFY(!widget->graphicsEffect()); + + // SetGet check. + QPointer blurEffect = new QGraphicsBlurEffect; + widget->setGraphicsEffect(blurEffect); + QCOMPARE(widget->graphicsEffect(), static_cast(blurEffect)); + + // Ensure the existing effect is deleted when setting a new one. + QPointer shadowEffect = new QGraphicsDropShadowEffect; + widget->setGraphicsEffect(shadowEffect); + QVERIFY(!blurEffect); + QCOMPARE(widget->graphicsEffect(), static_cast(shadowEffect)); + blurEffect = new QGraphicsBlurEffect; + + // Ensure the effect is uninstalled when setting it on a new target. + 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. + delete widget; + QVERIFY(!blurEffect); + delete anotherWidget; +} + +void tst_QWidget::activateWindow() +{ + // Test case for task 260685 + + // Create first mainwindow and set it active + QMainWindow* mainwindow = new QMainWindow(); + QLabel* label = new QLabel(mainwindow); + mainwindow->setCentralWidget(label); + mainwindow->setVisible(true); + mainwindow->activateWindow(); + QTest::qWaitForWindowShown(mainwindow); + qApp->processEvents(); + + QTRY_VERIFY(mainwindow->isActiveWindow()); + + // Create second mainwindow and set it active + QMainWindow* mainwindow2 = new QMainWindow(); + QLabel* label2 = new QLabel(mainwindow2); + mainwindow2->setCentralWidget(label2); + mainwindow2->setVisible(true); + mainwindow2->activateWindow(); + qApp->processEvents(); + + QTRY_VERIFY(!mainwindow->isActiveWindow()); + QTRY_VERIFY(mainwindow2->isActiveWindow()); + + // Revert first mainwindow back to visible active + mainwindow->setVisible(true); + mainwindow->activateWindow(); + qApp->processEvents(); + + QTRY_VERIFY(mainwindow->isActiveWindow()); + QTRY_VERIFY(!mainwindow2->isActiveWindow()); +} + +#ifdef Q_OS_SYMBIAN +void tst_QWidget::cbaVisibility() +{ + // Test case for task 261048 + + // Create first mainwindow in fullsreen and activate it + QMainWindow* mainwindow = new QMainWindow(); + QLabel* label = new QLabel(mainwindow); + mainwindow->setCentralWidget(label); + mainwindow->setWindowState(Qt::WindowFullScreen); + mainwindow->setVisible(true); + mainwindow->activateWindow(); + qApp->processEvents(); + + QVERIFY(mainwindow->isActiveWindow()); + QVERIFY(QDesktopWidget().availableGeometry().size() == mainwindow->size()); + + // Create second mainwindow in maximized and activate it + QMainWindow* mainwindow2 = new QMainWindow(); + QLabel* label2 = new QLabel(mainwindow2); + mainwindow2->setCentralWidget(label2); + mainwindow2->setWindowState(Qt::WindowMaximized); + mainwindow2->setVisible(true); + mainwindow2->activateWindow(); + qApp->processEvents(); + + QVERIFY(!mainwindow->isActiveWindow()); + QVERIFY(mainwindow2->isActiveWindow()); + QVERIFY(QDesktopWidget().availableGeometry().size() == mainwindow2->size()); + + // Verify window decorations i.e. status pane and CBA are visible. + CEikStatusPane* statusPane = CEikonEnv::Static()->AppUiFactory()->StatusPane(); + QVERIFY(statusPane->IsVisible()); + CEikButtonGroupContainer* buttonGroup = CEikonEnv::Static()->AppUiFactory()->Cba(); + QVERIFY(buttonGroup->IsVisible()); +} +#endif + +QTEST_MAIN(tst_QWidget) +#include "tst_qwidget.moc"