From e5fcad302d86d316390c6b0f62759a067313e8a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Mar 2009 10:18:55 +0100 Subject: Long live Qt 4.5! --- tests/auto/qgraphicsproxywidget/.gitignore | 1 + .../qgraphicsproxywidget/qgraphicsproxywidget.pro | 4 + .../tst_qgraphicsproxywidget.cpp | 3159 ++++++++++++++++++++ 3 files changed, 3164 insertions(+) create mode 100644 tests/auto/qgraphicsproxywidget/.gitignore create mode 100644 tests/auto/qgraphicsproxywidget/qgraphicsproxywidget.pro create mode 100644 tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp (limited to 'tests/auto/qgraphicsproxywidget') diff --git a/tests/auto/qgraphicsproxywidget/.gitignore b/tests/auto/qgraphicsproxywidget/.gitignore new file mode 100644 index 0000000000..530452478b --- /dev/null +++ b/tests/auto/qgraphicsproxywidget/.gitignore @@ -0,0 +1 @@ +tst_qgraphicsproxywidget diff --git a/tests/auto/qgraphicsproxywidget/qgraphicsproxywidget.pro b/tests/auto/qgraphicsproxywidget/qgraphicsproxywidget.pro new file mode 100644 index 0000000000..08d7b1ab91 --- /dev/null +++ b/tests/auto/qgraphicsproxywidget/qgraphicsproxywidget.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qgraphicsproxywidget.cpp + + diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp new file mode 100644 index 0000000000..d1d18571ee --- /dev/null +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -0,0 +1,3159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include "../../shared/util.h" +#include // qSmartMin functions... +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) +#include +#endif +#ifdef Q_WS_X11 +#include +#endif + + +static void sendMouseMove(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::NoButton) +{ + QMouseEvent event(QEvent::MouseMove, point, widget->mapToGlobal(point), button, button, 0); + QApplication::sendEvent(widget, &event); +} + + +#ifndef QT_NO_STYLE_CLEANLOOKS +/* + Notes: + + 1) The proxy and the widget geometries are linked. + proxy resize => widget resize => stop (no livelock) + widget resize => proxy resize => stop (no livelock) + + 2) As far as possible, the properties are linked. + proxy enable => widget enable => stop + widget disabled => proxy disabled => stop + + 3) Windowed state is linked + Windowed proxy state => windowed widget state => stop + Windowed widget state => windowed proxy state => stop +*/ + +class EventSpy : public QObject +{ +public: + EventSpy(QObject *receiver) + { + receiver->installEventFilter(this); + } + + QMap counts; + +protected: + bool eventFilter(QObject *, QEvent *event) + { + ++counts[event->type()]; + return false; + } +}; + +class tst_QGraphicsProxyWidget : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qgraphicsproxywidget_data(); + void qgraphicsproxywidget(); + void paint(); + void paint_2(); + void setWidget_data(); + void setWidget(); + void eventFilter_data(); + void eventFilter(); + void focusInEvent_data(); + void focusInEvent(); + void focusInEventNoWidget(); + void focusNextPrevChild_data(); + void focusNextPrevChild(); + void focusOutEvent_data(); + void focusOutEvent(); + void hoverEnterLeaveEvent_data(); + void hoverEnterLeaveEvent(); + void hoverMoveEvent_data(); + void hoverMoveEvent(); + void keyPressEvent_data(); + void keyPressEvent(); + void keyReleaseEvent_data(); + void keyReleaseEvent(); + void mouseDoubleClickEvent_data(); + void mouseDoubleClickEvent(); + void mousePressReleaseEvent_data(); + void mousePressReleaseEvent(); + void resizeEvent_data(); + void resizeEvent(); + void paintEvent(); + void wheelEvent(); + void sizeHint_data(); + void sizeHint(); + void sizePolicy(); + void minimumSize(); + void maximumSize(); + void scrollUpdate(); + void setWidget_simple(); + void setWidget_ownership(); + void resize_simple_data(); + void resize_simple(); + void symmetricMove(); + void symmetricResize(); + void symmetricEnabled(); + void symmetricVisible(); + void tabFocus_simpleWidget(); + void tabFocus_simpleTwoWidgets(); + void tabFocus_complexWidget(); + void tabFocus_complexTwoWidgets(); + void setFocus_simpleWidget(); + void setFocus_simpleTwoWidgets(); + void setFocus_complexTwoWidgets(); + void popup_basic(); + void popup_subwidget(); + void changingCursor_basic(); + void tooltip_basic(); + void childPos_data(); + void childPos(); + void autoShow(); + void windowOpacity(); + void stylePropagation(); + void palettePropagation(); + void fontPropagation(); + void dontCrashWhenDie(); + void createProxyForChildWidget(); + void actionsContextMenu(); + void deleteProxyForChildWidget(); + void bypassGraphicsProxyWidget_data(); + void bypassGraphicsProxyWidget(); + void dragDrop(); +}; + +// Subclass that exposes the protected functions. +class SubQGraphicsProxyWidget : public QGraphicsProxyWidget +{ + +public: + SubQGraphicsProxyWidget(QGraphicsItem *parent = 0) : QGraphicsProxyWidget(parent), + paintCount(0), keyPress(0), focusOut(0) + {} + + bool call_eventFilter(QObject* object, QEvent* event) + { return SubQGraphicsProxyWidget::eventFilter(object, event); } + + void call_focusInEvent(QFocusEvent* event) + { return SubQGraphicsProxyWidget::focusInEvent(event); } + + bool call_focusNextPrevChild(bool next) + { return SubQGraphicsProxyWidget::focusNextPrevChild(next); } + + void call_focusOutEvent(QFocusEvent* event) + { return SubQGraphicsProxyWidget::focusOutEvent(event); } + + void call_hideEvent(QHideEvent* event) + { return SubQGraphicsProxyWidget::hideEvent(event); } + + void call_hoverEnterEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverEnterEvent(event); } + + void call_hoverLeaveEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverLeaveEvent(event); } + + void call_hoverMoveEvent(QGraphicsSceneHoverEvent* event) + { return SubQGraphicsProxyWidget::hoverMoveEvent(event); } + + void call_keyPressEvent(QKeyEvent* event) + { return SubQGraphicsProxyWidget::keyPressEvent(event); } + + void call_keyReleaseEvent(QKeyEvent* event) + { return SubQGraphicsProxyWidget::keyReleaseEvent(event); } + + void call_mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseDoubleClickEvent(event); } + + void call_mouseMoveEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseMoveEvent(event); } + + void call_mousePressEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mousePressEvent(event); } + + void call_mouseReleaseEvent(QGraphicsSceneMouseEvent* event) + { return SubQGraphicsProxyWidget::mouseReleaseEvent(event); } + + void call_resizeEvent(QGraphicsSceneResizeEvent* event) + { return SubQGraphicsProxyWidget::resizeEvent(event); } + + QSizeF call_sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const + { return SubQGraphicsProxyWidget::sizeHint(which, constraint); } + + void call_showEvent(QShowEvent* event) + { return SubQGraphicsProxyWidget::showEvent(event); } + + void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) { + paintCount++; + QGraphicsProxyWidget::paint(painter, option, widget); + } + + void focusOutEvent(QFocusEvent *event) + { + focusOut++; + QGraphicsProxyWidget::focusOutEvent(event); + } + + bool eventFilter(QObject *object, QEvent *event) { + if (event->type() == QEvent::KeyPress && object == widget()) + keyPress++; + return QGraphicsProxyWidget::eventFilter(object, event); + } + int paintCount; + int keyPress; + int focusOut; +}; + +class WheelWidget : public QWidget +{ +public: + WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); } + + virtual void wheelEvent(QWheelEvent *event) { event->accept(); wheelEventCalled = true; } + + bool wheelEventCalled; +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QGraphicsProxyWidget::initTestCase() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QGraphicsProxyWidget::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QGraphicsProxyWidget::init() +{ +} + +// This will be called after every test function. +void tst_QGraphicsProxyWidget::cleanup() +{ +} + +void tst_QGraphicsProxyWidget::qgraphicsproxywidget_data() +{ +} + +void tst_QGraphicsProxyWidget::qgraphicsproxywidget() +{ + SubQGraphicsProxyWidget proxy; + proxy.paint(0, 0, 0); + proxy.setWidget(0); + QVERIFY(proxy.type() == QGraphicsProxyWidget::Type); + QVERIFY(!proxy.widget()); + QEvent event(QEvent::None); + proxy.call_eventFilter(0, &event); + QFocusEvent focusEvent(QEvent::FocusIn); + focusEvent.ignore(); + proxy.call_focusInEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), false); + QCOMPARE(proxy.call_focusNextPrevChild(false), false); + QCOMPARE(proxy.call_focusNextPrevChild(true), false); + proxy.call_focusOutEvent(&focusEvent); + QHideEvent hideEvent; + proxy.call_hideEvent(&hideEvent); + QGraphicsSceneHoverEvent hoverEvent; + proxy.call_hoverEnterEvent(&hoverEvent); + proxy.call_hoverLeaveEvent(&hoverEvent); + proxy.call_hoverMoveEvent(&hoverEvent); + QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier); + proxy.call_keyPressEvent(&keyEvent); + proxy.call_keyReleaseEvent(&keyEvent); + QGraphicsSceneMouseEvent mouseEvent; + proxy.call_mouseDoubleClickEvent(&mouseEvent); + proxy.call_mouseMoveEvent(&mouseEvent); + proxy.call_mousePressEvent(&mouseEvent); + proxy.call_mouseReleaseEvent(&mouseEvent); + QGraphicsSceneResizeEvent resizeEvent; + proxy.call_resizeEvent(&resizeEvent); + QShowEvent showEvent; + proxy.call_showEvent(&showEvent); + proxy.call_sizeHint(Qt::PreferredSize, QSizeF()); +} + +// public void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget) +void tst_QGraphicsProxyWidget::paint() +{ + SubQGraphicsProxyWidget proxy; + proxy.paint(0, 0, 0); +} + +class MyProxyWidget : public QGraphicsProxyWidget +{ +public: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter. + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform + | QPainter::NonCosmeticDefaultPen | QPainter::TextAntialiasing); + const QPainter::RenderHints oldRenderHints = painter->renderHints(); + QGraphicsProxyWidget::paint(painter, option, widget); + QCOMPARE(painter->renderHints(), oldRenderHints); + } +}; + +void tst_QGraphicsProxyWidget::paint_2() +{ + MyProxyWidget *proxyWidget = new MyProxyWidget; + proxyWidget->setWidget(new QLineEdit); + + QGraphicsScene scene; + scene.addItem(proxyWidget); + scene.setSceneRect(scene.itemsBoundingRect()); + + // Trigger repaint. + QPixmap pixmap(scene.sceneRect().toRect().size()); + QPainter painter(&pixmap); + scene.render(&painter); +} + +void tst_QGraphicsProxyWidget::setWidget_data() +{ + QTest::addColumn("widgetExists"); + QTest::addColumn("insertWidget"); + QTest::addColumn("hasParent"); + QTest::addColumn("proxyHasParent"); + + QTest::newRow("setWidget(0)") << false << false << false << false; + QTest::newRow("setWidget(widget)") << false << true << false << false; + QTest::newRow("setWidget(widgetWParent)") << false << true << true << false; + QTest::newRow("setWidget(1), setWidget(0)") << true << false << false << false; + QTest::newRow("setWidget(1), setWidget(widget)") << true << true << false << false; + QTest::newRow("setWidget(1), setWidget(widgetWParent)") << true << true << true << false; + QTest::newRow("p setWidget(0)") << false << false << false << true; + QTest::newRow("p setWidget(widget)") << false << true << false << true; + QTest::newRow("p setWidget(widgetWParent)") << false << true << true << true; + QTest::newRow("p setWidget(1), setWidget(0)") << true << false << false << true; + QTest::newRow("p setWidget(1), setWidget(widget)") << true << true << false << true; + QTest::newRow("p setWidget(1), setWidget(widgetWParent)") << true << true << true << true; +} + +// public void setWidget(QWidget* widget) +void tst_QGraphicsProxyWidget::setWidget() +{ + QFETCH(bool, widgetExists); + QFETCH(bool, insertWidget); + QFETCH(bool, hasParent); + QFETCH(bool, proxyHasParent); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QPointer proxy = new SubQGraphicsProxyWidget; + SubQGraphicsProxyWidget parentProxy; + scene.addItem(proxy); + scene.addItem(&parentProxy); + if (proxyHasParent) + proxy->setParent(&parentProxy); + QPointer existingSubWidget = new QWidget; + proxy->setVisible(false); + proxy->setEnabled(false); + + if (widgetExists) { + existingSubWidget->setAttribute(Qt::WA_QuitOnClose, true); + proxy->setWidget(existingSubWidget); + } + + QWidget *widget = new QWidget; +#ifndef QT_NO_CURSOR + widget->setCursor(Qt::IBeamCursor); +#endif + widget->setPalette(QPalette(Qt::magenta)); + widget->setLayoutDirection(Qt::RightToLeft); + QCleanlooksStyle cleanlooksStyle; + widget->setStyle(&cleanlooksStyle); + widget->setFont(QFont("Times")); + widget->setVisible(true); + QApplication::setActiveWindow(widget); + widget->activateWindow(); + widget->setEnabled(true); + widget->resize(325, 241); + widget->setMinimumSize(100, 200); + widget->setMaximumSize(1000, 2000); + widget->setFocusPolicy(Qt::TabFocus); + widget->setContentsMargins(10, 29, 19, 81); + widget->setFocus(Qt::TabFocusReason); + widget->setAcceptDrops(true); + QTRY_VERIFY(widget->hasFocus()); + QVERIFY(widget->isActiveWindow()); + + QWidget parentWidget; + if (hasParent) + widget->setParent(&parentWidget); + + QWidget *subWidget = insertWidget ? widget : 0; + bool shouldBeInsertable = !hasParent && subWidget; + if (shouldBeInsertable) + subWidget->setAttribute(Qt::WA_QuitOnClose, true); + + proxy->setWidget(subWidget); + + if (shouldBeInsertable) { + QCOMPARE(proxy->widget(), subWidget); + QVERIFY(subWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(!subWidget->testAttribute(Qt::WA_QuitOnClose)); + QCOMPARE(proxy->acceptHoverEvents(), true); +#ifndef QT_NO_CURSOR + QVERIFY(proxy->hasCursor()); + + // These should match + QCOMPARE(proxy->cursor().shape(), widget->cursor().shape()); +#endif + //###QCOMPARE(proxy->palette(), widget->palette()); + QCOMPARE(proxy->layoutDirection(), widget->layoutDirection()); + QCOMPARE(proxy->style(), widget->style()); + QCOMPARE(proxy->isVisible(), widget->isVisible()); + QCOMPARE(proxy->isEnabled(), widget->isEnabled()); + QVERIFY(proxy->flags() & QGraphicsItem::ItemIsFocusable); + QCOMPARE(proxy->effectiveSizeHint(Qt::MinimumSize).toSize(), + qSmartMinSize(widget)); + QCOMPARE(proxy->minimumSize().toSize(), + qSmartMinSize(widget) ); + QCOMPARE(proxy->maximumSize().toSize(), QSize(1000, 2000)); + QCOMPARE(proxy->effectiveSizeHint(Qt::PreferredSize).toSize(), + qSmartMinSize(widget)); + QCOMPARE(proxy->size().toSize(), widget->size()); + QCOMPARE(proxy->rect().toRect(), widget->rect()); + QCOMPARE(proxy->focusPolicy(), Qt::WheelFocus); + QVERIFY(proxy->acceptDrops()); + QCOMPARE(proxy->acceptsHoverEvents(), true); // to get widget enter events + int left, top, right, bottom; + widget->getContentsMargins(&left, &top, &right, &bottom); + qreal rleft, rtop, rright, rbottom; + proxy->getContentsMargins(&rleft, &rtop, &rright, &rbottom); + QCOMPARE((qreal)left, rleft); + QCOMPARE((qreal)top, rtop); + QCOMPARE((qreal)right, rright); + QCOMPARE((qreal)bottom, rbottom); + } else { + // proxy shouldn't mess with the widget if it can't insert it. + QCOMPARE(proxy->widget(), (QWidget*)0); + QCOMPARE(proxy->acceptHoverEvents(), false); + if (subWidget) { + QVERIFY(!subWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(subWidget->testAttribute(Qt::WA_QuitOnClose)); + // reset + subWidget->setAttribute(Qt::WA_QuitOnClose, false); + } + } + + if (widgetExists) { + QCOMPARE(existingSubWidget->parent(), static_cast(0)); + QVERIFY(!existingSubWidget->testAttribute(Qt::WA_DontShowOnScreen)); + QVERIFY(!existingSubWidget->testAttribute(Qt::WA_QuitOnClose)); + } + + if (hasParent) + widget->setParent(0); + + delete widget; + if (shouldBeInsertable) + QVERIFY(!proxy); + delete existingSubWidget; + if (!shouldBeInsertable) { + QVERIFY(proxy); + delete proxy; + } + QVERIFY(!proxy); +} + +Q_DECLARE_METATYPE(QEvent::Type) +void tst_QGraphicsProxyWidget::eventFilter_data() +{ + QTest::addColumn("eventType"); + QTest::addColumn("fromObject"); // big grin evil + QTest::newRow("none") << QEvent::None << false; + for (int i = 0; i < 2; ++i) { + bool fromObject = (i == 0); + QTest::newRow(QString("resize %1").arg(fromObject).toLatin1()) << QEvent::Resize << fromObject; + QTest::newRow(QString("move %1").arg(fromObject).toLatin1()) << QEvent::Move << fromObject; + QTest::newRow(QString("hide %1").arg(fromObject).toLatin1()) << QEvent::Hide << fromObject; + QTest::newRow(QString("show %1").arg(fromObject).toLatin1()) << QEvent::Show << fromObject; + QTest::newRow(QString("enabled %1").arg(fromObject).toLatin1()) << QEvent::EnabledChange << fromObject; + QTest::newRow(QString("focusIn %1").arg(fromObject).toLatin1()) << QEvent::FocusIn << fromObject; + QTest::newRow(QString("focusOut %1").arg(fromObject).toLatin1()) << QEvent::FocusOut << fromObject; + QTest::newRow(QString("keyPress %1").arg(fromObject).toLatin1()) << QEvent::KeyPress << fromObject; + } +} + +// protected bool eventFilter(QObject* object, QEvent* event) +void tst_QGraphicsProxyWidget::eventFilter() +{ + QFETCH(QEvent::Type, eventType); + QFETCH(bool, fromObject); + + QGraphicsScene scene; + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + scene.addItem(proxy); + + QWidget *widget = new QWidget(0, Qt::FramelessWindowHint); + widget->setFocusPolicy(Qt::TabFocus); + widget->resize(10, 10); + widget->show(); + proxy->setWidget(widget); + proxy->show(); + + // mirror whatever is happening to the widget + // don't get in a loop + switch (eventType) { + case QEvent::None: { + QEvent event(QEvent::None); + proxy->call_eventFilter(widget, &event); + break; + } + case QEvent::Resize: { + QSize oldSize = widget->size(); + QSize newSize = QSize(100, 100); + if (fromObject) { + widget->resize(newSize); + } else { + proxy->resize(newSize); + } + QCOMPARE(proxy->size().toSize(), newSize); + QCOMPARE(widget->size(), newSize); + break; + } + case QEvent::Move: { + QPoint oldPoint = widget->pos(); + QPoint newPoint = QPoint(100, 100); + if (fromObject) { + widget->move(newPoint); + } else { + proxy->setPos(newPoint); + } + QCOMPARE(proxy->pos().toPoint(), newPoint); + QCOMPARE(widget->pos(), newPoint); + break; + } + case QEvent::Hide: { + // A hide event can only come from a widget + if (fromObject) { + widget->setFocus(Qt::TabFocusReason); + widget->hide(); + } else { + QHideEvent event; + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->isVisible(), false); + break; + } + case QEvent::Show: { + // A show event can either come from a widget or somewhere else + widget->hide(); + if (fromObject) { + widget->show(); + } else { + QShowEvent event; + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->isVisible(), true); + break; + } + case QEvent::EnabledChange: { + widget->setEnabled(false); + proxy->setEnabled(false); + if (fromObject) { + widget->setEnabled(true); + QCOMPARE(proxy->isEnabled(), true); + widget->setEnabled(false); + QCOMPARE(proxy->isEnabled(), false); + } else { + QEvent event(QEvent::EnabledChange); + proxy->call_eventFilter(widget, &event); + // match the widget not the event + QCOMPARE(proxy->isEnabled(), false); + } + break; + } + case QEvent::FocusIn: { + if (fromObject) { + widget->setFocus(Qt::TabFocusReason); + QVERIFY(proxy->hasFocus()); + } + break; + } + case QEvent::FocusOut: { + widget->setFocus(Qt::TabFocusReason); + QVERIFY(proxy->hasFocus()); + QVERIFY(widget->hasFocus()); + if (fromObject) { + widget->clearFocus(); + QVERIFY(!proxy->hasFocus()); + } + break; + } + case QEvent::KeyPress: { + if (fromObject) { + QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier); + } else { + QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); + proxy->call_eventFilter(widget, &event); + } + QCOMPARE(proxy->keyPress, 1); + break; + } + default: + break; + } +} + +void tst_QGraphicsProxyWidget::focusInEvent_data() +{ + QTest::addColumn("widgetHasFocus"); + QTest::addColumn("widgetCanHaveFocus"); + QTest::newRow("no focus, can't get") << false << false; + QTest::newRow("no focus, can get") << false << true; + QTest::newRow("has focus, can't get") << true << false; + QTest::newRow("has focus, can get") << true << true; + // ### add test for widget having a focusNextPrevChild +} + +// protected void focusInEvent(QFocusEvent* event) +void tst_QGraphicsProxyWidget::focusInEvent() +{ + QFETCH(bool, widgetHasFocus); + QFETCH(bool, widgetCanHaveFocus); + + QGraphicsView view; + QGraphicsScene scene(&view); + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setEnabled(true); + scene.addItem(proxy); + proxy->setVisible(true); + view.show(); + QApplication::setActiveWindow(&view); + view.activateWindow(); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + if (widgetCanHaveFocus) + widget->setFocusPolicy(Qt::WheelFocus); + widget->show(); + + if (widgetHasFocus) + widget->setFocus(Qt::TabFocusReason); + + proxy->setWidget(widget); + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this + QTRY_VERIFY(widget->isVisible() && view.isVisible()); + QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); + event.ignore(); + proxy->call_focusInEvent(&event); + QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus); +} + +void tst_QGraphicsProxyWidget::focusInEventNoWidget() +{ + QGraphicsView view; + QGraphicsScene scene(&view); + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setEnabled(true); + scene.addItem(proxy); + proxy->setVisible(true); + view.show(); + + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this + QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); + event.ignore(); + //should not crash + proxy->call_focusInEvent(&event); +} + +void tst_QGraphicsProxyWidget::focusNextPrevChild_data() +{ + QTest::addColumn("hasWidget"); + QTest::addColumn("hasScene"); + QTest::addColumn("next"); + QTest::addColumn("focusNextPrevChild"); + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + bool next = (i == 0); + bool hasWidget = (j == 0); + bool hasScene = (k == 0); + bool result = hasScene && hasWidget; + QString name = QString("Forward: %1, hasWidget: %2, hasScene: %3, result: %4").arg(next).arg(hasWidget).arg(hasScene).arg(result); + QTest::newRow(name.toLatin1()) << hasWidget << hasScene << next << result; + } + } + } +} + +// protected bool focusNextPrevChild(bool next) +void tst_QGraphicsProxyWidget::focusNextPrevChild() +{ + QFETCH(bool, next); + QFETCH(bool, focusNextPrevChild); + QFETCH(bool, hasWidget); + QFETCH(bool, hasScene); + + // If a widget has its own focusNextPrevChild we need to respect it + // otherwise respect the scene + // Respect the widget over the scene! + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + + QLabel *widget = new QLabel; + // I can't believe designer adds this much junk! + widget->setText("

old foo new

"); + widget->setTextInteractionFlags(Qt::TextBrowserInteraction); + + if (hasWidget) + proxy->setWidget(widget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + if (hasScene) { + scene.addItem(proxy); + proxy->show(); + + // widget should take precedence over scene so make scene.focusNextPrevChild return false + // so we know that a true can only come from the widget + if (!(hasWidget && hasScene)) { + QGraphicsTextItem *item = new QGraphicsTextItem("Foo"); + item->setTextInteractionFlags(Qt::TextBrowserInteraction); + scene.addItem(item); + item->setPos(50, 40); + } + scene.setFocusItem(proxy); + QVERIFY(proxy->hasFocus()); + } + + QCOMPARE(proxy->call_focusNextPrevChild(next), focusNextPrevChild); + + if (!hasScene) + delete proxy; +} + +void tst_QGraphicsProxyWidget::focusOutEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::addColumn("call"); + QTest::newRow("no widget, focus to other widget") << false << false; + QTest::newRow("no widget, focusOutCalled") << false << true; + QTest::newRow("widget, focus to other widget") << true << false; + QTest::newRow("widget, focusOutCalled") << true << true; +} + +// protected void focusOutEvent(QFocusEvent* event) +void tst_QGraphicsProxyWidget::focusOutEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, call); + + QGraphicsScene scene; + QGraphicsView view(&scene); + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + scene.addItem(proxy); + view.show(); + QApplication::setActiveWindow(&view); + view.activateWindow(); + view.setFocus(); + QTRY_VERIFY(view.isVisible()); + QTest::qWait(125); + + QWidget *widget = new QWidget; + widget->setFocusPolicy(Qt::WheelFocus); + if (hasWidget) + proxy->setWidget(widget); + proxy->show(); + proxy->setFocus(); + QVERIFY(proxy->hasFocus()); + QEXPECT_FAIL("widget, focus to other widget", "Widget should have focus but doesn't", Continue); + QEXPECT_FAIL("widget, focusOutCalled", "Widget should have focus but doesn't", Continue); + QCOMPARE(widget->hasFocus(), hasWidget); + + if (!call) { + QWidget *other = new QLineEdit(&view); + other->show(); + QTest::qWait(125); + QTRY_VERIFY(other->isVisible()); + other->setFocus(); + QTRY_VERIFY(other->hasFocus()); + QTest::qWait(125); + qApp->processEvents(); + QTRY_COMPARE(proxy->hasFocus(), false); + QVERIFY(proxy->focusOut); + QCOMPARE(widget->hasFocus(), false); + } else { + { + /* + ### Test doesn't make sense + QFocusEvent focusEvent(QEvent::FocusOut); + proxy->call_focusOutEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), hasWidget); + qApp->processEvents(); + QCOMPARE(proxy->paintCount, hasWidget ? 1 : 0); + */ + } + { + /* + ### Test doesn't make sense + proxy->setFlag(QGraphicsItem::ItemIsFocusable, false); + QFocusEvent focusEvent(QEvent::FocusOut); + proxy->call_focusOutEvent(&focusEvent); + QCOMPARE(focusEvent.isAccepted(), hasWidget); + qApp->processEvents(); + QCOMPARE(proxy->paintCount, 0); + */ + } + } +} + +class EventLogger : public QWidget +{ +public: + EventLogger() : QWidget(), enterCount(0), leaveCount(0), moveCount(0), + hoverEnter(0), hoverLeave(0), hoverMove(0) + { + installEventFilter(this); + } + + void enterEvent(QEvent *event) + { + enterCount++; + QWidget::enterEvent(event); + } + + void leaveEvent(QEvent *event ) + { + leaveCount++; + QWidget::leaveEvent(event); + } + + void mouseMoveEvent(QMouseEvent *event) + { + event->setAccepted(true); + moveCount++; + QWidget::mouseMoveEvent(event); + } + + int enterCount; + int leaveCount; + int moveCount; + + int hoverEnter; + int hoverLeave; + int hoverMove; +protected: + bool eventFilter(QObject *object, QEvent *event) + { + switch (event->type()) { + case QEvent::HoverEnter: + hoverEnter++; + break; + case QEvent::HoverLeave: + hoverLeave++; + break; + case QEvent::HoverMove: + hoverMove++; + break; + default: + break; + } + return QWidget::eventFilter(object, event); + } +}; + +void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::addColumn("hoverEnabled"); + QTest::newRow("widget, no hover") << true << false; + QTest::newRow("no widget, no hover") << false << false; + QTest::newRow("widget, hover") << true << true; + QTest::newRow("no widget, hover") << false << true; +} + +// protected void hoverEnterEvent(QGraphicsSceneHoverEvent* event) +void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, hoverEnabled); + +#if defined(Q_OS_WINCE) && (!defined(GWES_ICONCURS) || defined(QT_NO_CURSOR)) + QSKIP("hover events not supported on this platform", SkipAll); +#endif + + // proxy should translate this into events that the widget would expect + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + EventLogger *widget = new EventLogger; + widget->resize(50, 50); + widget->setAttribute(Qt::WA_Hover, hoverEnabled); + widget->setMouseTracking(true); + view.resize(100, 100); + if (hasWidget) + proxy->setWidget(widget); + proxy->setPos(50, 0); + scene.addItem(proxy); + QTest::qWait(30); + QTest::mouseMove(&view, QPoint(10, 10)); + QTest::qWait(30); + + // in + QTest::mouseMove(&view, QPoint(50, 50)); + QTest::qWait(250); + // QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), hasWidget ? true : false); + // ### this attribute isn't supported + QCOMPARE(widget->enterCount, hasWidget ? 1 : 0); + QCOMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0); + // does not work on all platforms + //QCOMPARE(widget->moveCount, 0); + + // out + QTest::mouseMove(&view, QPoint(10, 10)); + QTest::qWait(250); + // QTRY_COMPARE(widget->testAttribute(Qt::WA_UnderMouse), false); + // ### this attribute isn't supported + QCOMPARE(widget->leaveCount, hasWidget ? 1 : 0); + QCOMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0); + // does not work on all platforms + //QCOMPARE(widget->moveCount, 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::hoverMoveEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::addColumn("hoverEnabled"); + QTest::addColumn("mouseTracking"); + QTest::addColumn("mouseDown"); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + for (int l = 0; l < 2; ++l) { + bool hasWidget = (i == 0); + bool hoverEnabled = (j == 0); + bool mouseTracking = (k == 0); + bool mouseDown = (l == 0); + QString name = QString("hasWidget:%1, hover:%2, mouseTracking:%3, mouseDown: %4").arg(hasWidget).arg(hoverEnabled).arg(mouseTracking).arg(mouseDown); + QTest::newRow(name.toLatin1()) << hasWidget << hoverEnabled << mouseTracking << mouseDown; + } + } + } + } +} + +// protected void hoverMoveEvent(QGraphicsSceneHoverEvent* event) +void tst_QGraphicsProxyWidget::hoverMoveEvent() +{ + QFETCH(bool, hasWidget); + QFETCH(bool, hoverEnabled); + QFETCH(bool, mouseTracking); + QFETCH(bool, mouseDown); + + QSKIP("Ambiguous test...", SkipAll); + + // proxy should translate the move events to what the widget would expect + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + EventLogger *widget = new EventLogger; + widget->resize(50, 50); + widget->setAttribute(Qt::WA_Hover, hoverEnabled); + if (mouseTracking) + widget->setMouseTracking(true); + view.resize(100, 100); + if (hasWidget) + proxy->setWidget(widget); + proxy->setPos(50, 0); + scene.addItem(proxy); + + // in + QTest::mouseMove(&view, QPoint(50, 50)); + QTest::qWait(125); + + if (mouseDown) + QTest::mousePress(view.viewport(), Qt::LeftButton); + + // move a little bit + QTest::mouseMove(&view, QPoint(60, 60)); + QTest::qWait(125); + QTRY_COMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0); + QCOMPARE(widget->moveCount, (hasWidget && mouseTracking) || (hasWidget && mouseDown) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::keyPressEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void keyPressEvent(QKeyEvent* event) +void tst_QGraphicsProxyWidget::keyPressEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + view.viewport()->setFocus(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + + QLineEdit *widget = new QLineEdit; + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::keyPress(view.viewport(), 'x'); + + QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString()); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::keyReleaseEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void keyReleaseEvent(QKeyEvent* event) +void tst_QGraphicsProxyWidget::keyReleaseEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QPushButton *widget = new QPushButton; + QSignalSpy spy(widget, SIGNAL(clicked())); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::keyPress(view.viewport(), Qt::Key_Space); + QTRY_COMPARE(spy.count(), 0); + QTest::keyRelease(view.viewport(), Qt::Key_Space); + QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::mouseDoubleClickEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +void tst_QGraphicsProxyWidget::mouseDoubleClickEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QLineEdit *widget = new QLineEdit; + widget->setText("foo"); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()))); + + QTRY_COMPARE(widget->selectedText(), hasWidget ? QString("foo") : QString()); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void mousePressEvent(QGraphicsSceneMouseEvent* event) +void tst_QGraphicsProxyWidget::mousePressReleaseEvent() +{ + QFETCH(bool, hasWidget); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWait(125); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! + QPushButton *widget = new QPushButton; + QSignalSpy spy(widget, SIGNAL(clicked())); + widget->resize(50, 50); + view.resize(100, 100); + if (hasWidget) { + proxy->setWidget(widget); + proxy->show(); + } + proxy->setPos(50, 0); + scene.addItem(proxy); + proxy->setFocus(); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTRY_COMPARE(spy.count(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); + + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::resizeEvent_data() +{ + QTest::addColumn("hasWidget"); + QTest::newRow("widget") << true; + QTest::newRow("no widget") << false; +} + +// protected void resizeEvent(QGraphicsSceneResizeEvent* event) +void tst_QGraphicsProxyWidget::resizeEvent() +{ + QFETCH(bool, hasWidget); + + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + if (hasWidget) + proxy.setWidget(widget); + + QSize newSize(100, 100); + QGraphicsSceneResizeEvent event; + event.setOldSize(QSize(10, 10)); + event.setNewSize(newSize); + proxy.call_resizeEvent(&event); + if (hasWidget) + QCOMPARE(widget->size(), newSize); + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::paintEvent() +{ + //we test that calling update on a widget inside a QGraphicsView is triggering a repaint + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + SubQGraphicsProxyWidget proxy; + + QWidget *w = new QWidget; + //showing the widget here seems to create a bug in Graphics View + //this bug prevents the widget from being updated + + w->show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(w); +#endif + QTest::qWait(100); + + proxy.setWidget(w); + scene.addItem(&proxy); + + //make sure we flush all the paint events + QTest::qWait(250); + proxy.paintCount = 0; + + w->update(); + QTest::qWait(100); + QCOMPARE(proxy.paintCount, 1); //the widget should have been painted now +} + + +void tst_QGraphicsProxyWidget::wheelEvent() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + WheelWidget *wheelWidget = new WheelWidget(); + wheelWidget->setFixedSize(400, 400); + + QGraphicsProxyWidget *proxy = scene.addWidget(wheelWidget); + proxy->setVisible(true); + + QGraphicsSceneWheelEvent event(QEvent::GraphicsSceneWheel); + event.setScenePos(QPoint(50, 50)); + event.setAccepted(false); + wheelWidget->wheelEventCalled = false; + + QApplication::sendEvent(&scene, &event); + + QVERIFY(event.isAccepted()); + QVERIFY(wheelWidget->wheelEventCalled); +} + +Q_DECLARE_METATYPE(Qt::SizeHint) +void tst_QGraphicsProxyWidget::sizeHint_data() +{ + QTest::addColumn("which"); + QTest::addColumn("constraint"); + QTest::addColumn("sizeHint"); + QTest::addColumn("hasWidget"); + + for (int i = 0; i < 2; ++i) { + bool hasWidget = (i == 0); + // ### What should these do? + QTest::newRow("min") << Qt::MinimumSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("pre") << Qt::PreferredSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("max") << Qt::MaximumSize << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("mindes") << Qt::MinimumDescent << QSizeF() << QSizeF() << hasWidget; + QTest::newRow("nsize") << Qt::NSizeHints << QSizeF() << QSizeF() << hasWidget; + } +} + +// protected QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const +void tst_QGraphicsProxyWidget::sizeHint() +{ + QFETCH(Qt::SizeHint, which); + QFETCH(QSizeF, constraint); + QFETCH(QSizeF, sizeHint); + QFETCH(bool, hasWidget); + QSKIP("Broken test", SkipAll); + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + if (hasWidget) + proxy.setWidget(widget); + QCOMPARE(proxy.call_sizeHint(which, constraint), sizeHint); + if (!hasWidget) + delete widget; +} + +void tst_QGraphicsProxyWidget::sizePolicy() +{ + for (int p = 0; p < 2; ++p) { + bool hasWidget = (p == 0 ? true : false); + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSizePolicy proxyPol(QSizePolicy::Maximum, QSizePolicy::Expanding); + proxy.setSizePolicy(proxyPol); + QSizePolicy widgetPol(QSizePolicy::Fixed, QSizePolicy::Minimum); + widget->setSizePolicy(widgetPol); + + QCOMPARE(proxy.sizePolicy(), proxyPol); + QCOMPARE(widget->sizePolicy(), widgetPol); + if (hasWidget) { + proxy.setWidget(widget); + QCOMPARE(proxy.sizePolicy(), widgetPol); + } else { + QCOMPARE(proxy.sizePolicy(), proxyPol); + } + QCOMPARE(widget->sizePolicy(), widgetPol); + + proxy.setSizePolicy(widgetPol); + widget->setSizePolicy(proxyPol); + if (hasWidget) + QCOMPARE(proxy.sizePolicy(), proxyPol); + else + QCOMPARE(proxy.sizePolicy(), widgetPol); + } +} + +void tst_QGraphicsProxyWidget::minimumSize() +{ + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSize minSize(50, 50); + widget->setMinimumSize(minSize); + proxy.resize(30, 30); + widget->resize(30,30); + QCOMPARE(proxy.size(), QSizeF(30, 30)); + proxy.setWidget(widget); + QCOMPARE(proxy.size().toSize(), minSize); + QCOMPARE(proxy.minimumSize().toSize(), minSize); + widget->setMinimumSize(70, 70); + QCOMPARE(proxy.minimumSize(), QSizeF(70, 70)); + QCOMPARE(proxy.size(), QSizeF(70, 70)); +} + +void tst_QGraphicsProxyWidget::maximumSize() +{ + SubQGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + QSize maxSize(150, 150); + widget->setMaximumSize(maxSize); + proxy.resize(200, 200); + widget->resize(200,200); + QCOMPARE(proxy.size(), QSizeF(200, 200)); + proxy.setWidget(widget); + QCOMPARE(proxy.size().toSize(), maxSize); + QCOMPARE(proxy.maximumSize().toSize(), maxSize); + widget->setMaximumSize(70, 70); + QCOMPARE(proxy.maximumSize(), QSizeF(70, 70)); + QCOMPARE(proxy.size(), QSizeF(70, 70)); +} + +class View : public QGraphicsView +{ +public: + View(QGraphicsScene *scene, QWidget *parent = 0) + : QGraphicsView(scene, parent), npaints(0) + { } + QRegion paintEventRegion; + int npaints; +protected: + void paintEvent(QPaintEvent *event) + { + ++npaints; + paintEventRegion += event->region(); + QGraphicsView::paintEvent(event); + } +}; + +class ScrollWidget : public QWidget +{ + Q_OBJECT +public: + ScrollWidget() : npaints(0) + { + resize(200, 200); + } + QRegion paintEventRegion; + int npaints; + +public slots: + void updateScroll() + { + update(0, 0, 200, 10); + scroll(0, 10, QRect(0, 0, 100, 20)); + } + +protected: + void paintEvent(QPaintEvent *event) + { + ++npaints; + paintEventRegion += event->region(); + QPainter painter(this); + painter.fillRect(event->rect(), Qt::blue); + } +}; + +void tst_QGraphicsProxyWidget::scrollUpdate() +{ + ScrollWidget *widget = new ScrollWidget; + + QGraphicsScene scene; + scene.addWidget(widget); + + View view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(200); + widget->paintEventRegion = QRegion(); + widget->npaints = 0; + view.paintEventRegion = QRegion(); + view.npaints = 0; + QTimer::singleShot(0, widget, SLOT(updateScroll())); + QTest::qWait(500); + QCOMPARE(view.npaints, 2); + // QRect(0, 0, 200, 12) is the first update, expanded (-2, -2, 2, 2) + // QRect(0, 12, 102, 10) is the scroll update, expanded (-2, -2, 2, 2), + // intersected with the above update. + QCOMPARE(view.paintEventRegion.rects(), + QVector() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10)); + QCOMPARE(widget->npaints, 2); + QCOMPARE(widget->paintEventRegion.rects(), + QVector() << QRect(0, 0, 200, 13) << QRect(0, 13, 103, 10)); +} + +void tst_QGraphicsProxyWidget::setWidget_simple() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + + QVERIFY(lineEdit->testAttribute(Qt::WA_DontShowOnScreen)); + // Size hints + // ### size hints are tested in a different test + // QCOMPARE(proxy.effectiveSizeHint(Qt::MinimumSize).toSize(), lineEdit->minimumSizeHint()); + // QCOMPARE(proxy.effectiveSizeHint(Qt::MaximumSize).toSize(), lineEdit->maximumSize()); + // QCOMPARE(proxy.effectiveSizeHint(Qt::PreferredSize).toSize(), lineEdit->sizeHint()); + QCOMPARE(proxy.size().toSize(), lineEdit->minimumSizeHint().expandedTo(lineEdit->size())); + QRect rect = lineEdit->rect(); + rect.setSize(rect.size().expandedTo(lineEdit->minimumSizeHint())); + QCOMPARE(proxy.rect().toRect(), rect); + + // Properties + // QCOMPARE(proxy.focusPolicy(), lineEdit->focusPolicy()); + QCOMPARE(proxy.palette(), lineEdit->palette()); +#ifndef QT_NO_CURSOR + QCOMPARE(proxy.cursor().shape(), lineEdit->cursor().shape()); +#endif + QCOMPARE(proxy.layoutDirection(), lineEdit->layoutDirection()); + QCOMPARE(proxy.style(), lineEdit->style()); + QCOMPARE(proxy.font(), lineEdit->font()); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); +} + +void tst_QGraphicsProxyWidget::setWidget_ownership() +{ + QPointer lineEdit = new QLineEdit; + QPointer lineEdit2 = new QLineEdit; + QVERIFY(lineEdit); + { + // Create a proxy and transfer ownership to it + QGraphicsProxyWidget proxy; + proxy.setWidget(lineEdit); + QCOMPARE(proxy.widget(), (QWidget *)lineEdit); + + // Remove the widget without destroying it. + proxy.setWidget(0); + QVERIFY(!proxy.widget()); + QVERIFY(lineEdit); + + // Assign the widget again and switch to another widget. + proxy.setWidget(lineEdit); + proxy.setWidget(lineEdit2); + QCOMPARE(proxy.widget(), (QWidget *)lineEdit2); + + // Assign the first widget, and destroy the proxy. + proxy.setWidget(lineEdit); + } + QVERIFY(!lineEdit); + QVERIFY(lineEdit2); + + QGraphicsScene scene; + QPointer proxy = scene.addWidget(lineEdit2); + + delete lineEdit2; + QVERIFY(!proxy); +} + +void tst_QGraphicsProxyWidget::resize_simple_data() +{ + QTest::addColumn("size"); + + QTest::newRow("200, 200") << QSizeF(200, 200); +#if !defined(QT_ARCH_ARM) && !defined(Q_OS_WINCE) + QTest::newRow("1000, 1000") << QSizeF(1000, 1000); + // Since 4.5, 10000x10000 runs out of memory. + // QTest::newRow("10000, 10000") << QSizeF(10000, 10000); +#endif +} + +void tst_QGraphicsProxyWidget::resize_simple() +{ + QFETCH(QSizeF, size); + + QGraphicsProxyWidget proxy; + QWidget *widget = new QWidget; + widget->setGeometry(0, 0, size.width(), size.height()); + proxy.setWidget(widget); + widget->show(); + QCOMPARE(widget->pos(), QPoint()); + + // The proxy resizes itself, the line edit follows + proxy.resize(size); + QCOMPARE(proxy.size(), size); + QCOMPARE(proxy.size().toSize(), widget->size()); + + // The line edit resizes itself, the proxy follows (no loopback/live lock) + QSize doubleSize = size.toSize() * 2; + widget->resize(doubleSize); + QCOMPARE(widget->size(), doubleSize); + QCOMPARE(widget->size(), proxy.size().toSize()); +} + +void tst_QGraphicsProxyWidget::symmetricMove() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + proxy.setPos(10, 10); + QCOMPARE(proxy.pos(), QPointF(10, 10)); + QCOMPARE(lineEdit->pos(), QPoint(10, 10)); + + lineEdit->move(5, 5); + QCOMPARE(proxy.pos(), QPointF(5, 5)); + QCOMPARE(lineEdit->pos(), QPoint(5, 5)); +} + +void tst_QGraphicsProxyWidget::symmetricResize() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + proxy.resize(256, 256); + QCOMPARE(proxy.size(), QSizeF(256, 256)); + QCOMPARE(lineEdit->size(), QSize(256, 256)); + + lineEdit->resize(512, 512); + QCOMPARE(proxy.size(), QSizeF(512, 512)); + QCOMPARE(lineEdit->size(), QSize(512, 512)); +} + +void tst_QGraphicsProxyWidget::symmetricVisible() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + + proxy.hide(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + proxy.show(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + lineEdit->hide(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + lineEdit->show(); + QCOMPARE(proxy.isVisible(), lineEdit->isVisible()); + } + +void tst_QGraphicsProxyWidget::symmetricEnabled() +{ + QGraphicsProxyWidget proxy; + QLineEdit *lineEdit = new QLineEdit; + proxy.setWidget(lineEdit); + lineEdit->show(); + + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + proxy.setEnabled(false); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + proxy.setEnabled(true); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + lineEdit->setEnabled(false); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); + lineEdit->setEnabled(true); + QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled()); +} + +void tst_QGraphicsProxyWidget::tabFocus_simpleWidget() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Backtab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(leftDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); +} + +void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QLineEdit *edit2 = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + QGraphicsProxyWidget *editProxy2 = scene.addWidget(edit2); + editProxy2->show(); + editProxy2->setPos(0, editProxy->rect().height() * 1.1); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + EventSpy eventSpy2(edit2); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into second line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(editProxy2->hasFocus()); + QVERIFY(edit2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!editProxy2->hasFocus()); + QVERIFY(!edit2->hasFocus()); + QVERIFY(rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(editProxy2->hasFocus()); + QVERIFY(edit2->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(editProxy->hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!editProxy2->hasFocus()); + QVERIFY(!edit2->hasFocus()); + QVERIFY(!rightDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(!scene.hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(leftDial->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); +} + +void tst_QGraphicsProxyWidget::tabFocus_complexWidget() +{ + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpyBox(box); + + // Tab into group box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(proxy->hasFocus()); + QVERIFY(box->hasFocus()); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + edit1->hasFocus(); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + edit2->hasFocus(); + QVERIFY(!edit1->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!edit2->hasFocus()); + rightDial->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!rightDial->hasFocus()); + edit2->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit2->hasFocus()); + edit1->hasFocus(); + QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit1->hasFocus()); + box->hasFocus(); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!box->hasFocus()); + leftDial->hasFocus(); +} + +void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() +{ + // ### add event spies to this test. + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QLineEdit *edit1_2 = new QLineEdit; + edit1_2->setText("QLineEdit 1_2"); + QLineEdit *edit2_2 = new QLineEdit; + edit2_2->setText("QLineEdit 2_2"); + vlayout = new QVBoxLayout; + vlayout->addWidget(edit1_2); + vlayout->addWidget(edit2_2); + + QGroupBox *box_2 = new QGroupBox("QGroupBox 2"); + box_2->setCheckable(true); + box_2->setChecked(true); + box_2->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2); + proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0); + proxy_2->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + view->setRenderHint(QPainter::Antialiasing); + view->rotate(45); + view->scale(0.5, 0.5); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpy1_2(edit1_2); + EventSpy eventSpy2_2(edit2_2); + EventSpy eventSpyBox(box); + + // Tab into group box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(view->viewport()->hasFocus()); + QVERIFY(scene.hasFocus()); + QVERIFY(proxy->hasFocus()); + QVERIFY(box->hasFocus()); + + // Tab into line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + edit1->hasFocus(); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0); + + // Tab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + edit2->hasFocus(); + QVERIFY(!edit1->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + + // Tab into right box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!edit2->hasFocus()); + box_2->hasFocus(); + + // Tab into right top line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!box_2->hasFocus()); + edit1_2->hasFocus(); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 0); + + // Tab into right bottom line edit + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!edit1_2->hasFocus()); + edit2_2->hasFocus(); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 0); + + // Tab into right dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab); + QTest::qWait(125); + QVERIFY(!edit2->hasFocus()); + rightDial->hasFocus(); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!rightDial->hasFocus()); + edit2_2->hasFocus(); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit2_2->hasFocus()); + edit1_2->hasFocus(); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit1_2->hasFocus()); + box_2->hasFocus(); + + // Backtab into line edit 2 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!rightDial->hasFocus()); + edit2->hasFocus(); + + // Backtab into line edit 1 + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit2->hasFocus()); + edit1->hasFocus(); + + // Backtab into line box + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!edit1->hasFocus()); + box->hasFocus(); + + // Backtab into left dial + QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab); + QTest::qWait(125); + QVERIFY(!box->hasFocus()); + leftDial->hasFocus(); +} + +void tst_QGraphicsProxyWidget::setFocus_simpleWidget() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + // Calling setFocus for the line edit when the view doesn't have input + // focus does not steal focus from the dial. The edit, proxy and scene + // have focus but only in their own little space. + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(!view->hasFocus()); + QVERIFY(!view->viewport()->hasFocus()); + QVERIFY(leftDial->hasFocus()); + + // Clearing focus is a local operation. + edit->clearFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!view->hasFocus()); + QVERIFY(leftDial->hasFocus()); + + view->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(view->hasFocus()); + QVERIFY(!leftDial->hasFocus()); + QVERIFY(!edit->hasFocus()); + + scene.clearFocus(); + QVERIFY(!scene.hasFocus()); + + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(editProxy->hasFocus()); + + // Symmetry + editProxy->clearFocus(); + QVERIFY(!edit->hasFocus()); +} + +void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() +{ + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + editProxy->show(); + QLineEdit *edit2 = new QLineEdit; + QGraphicsProxyWidget *edit2Proxy = scene.addWidget(edit2); + edit2Proxy->show(); + edit2Proxy->setPos(editProxy->boundingRect().width() * 1.01, 0); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit); + + view->setFocus(); + QVERIFY(!edit->hasFocus()); + + edit->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(edit->hasFocus()); + QVERIFY(editProxy->hasFocus()); + + edit2->setFocus(); + QVERIFY(scene.hasFocus()); + QVERIFY(!edit->hasFocus()); + QVERIFY(!editProxy->hasFocus()); + QVERIFY(edit2->hasFocus()); + QVERIFY(edit2Proxy->hasFocus()); +} + +void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() +{ + // ### add event spies to this test. + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QLineEdit *edit1_2 = new QLineEdit; + edit1_2->setText("QLineEdit 1_2"); + QLineEdit *edit2_2 = new QLineEdit; + edit2_2->setText("QLineEdit 2_2"); + vlayout = new QVBoxLayout; + vlayout->addWidget(edit1_2); + vlayout->addWidget(edit2_2); + + QGroupBox *box_2 = new QGroupBox("QGroupBox 2"); + box_2->setCheckable(true); + box_2->setChecked(true); + box_2->setLayout(vlayout); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + + QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2); + proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0); + proxy_2->show(); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + QGraphicsView *view = new QGraphicsView(&scene); + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(view); + layout->addWidget(rightDial); + window.setLayout(layout); + + window.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&window); +#endif + QApplication::setActiveWindow(&window); + window.activateWindow(); + QTest::qWait(125); + + leftDial->setFocus(); + QTest::qWait(125); + QVERIFY(leftDial->hasFocus()); + + EventSpy eventSpy(edit1); + EventSpy eventSpy2(edit2); + EventSpy eventSpyBox(box); + EventSpy eventSpy_2(edit1_2); + EventSpy eventSpy2_2(edit2_2); + EventSpy eventSpyBox_2(box_2); + + view->setFocus(); + + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 0); + + edit1->setFocus(); + QTest::qWait(125); + QVERIFY(scene.hasFocus()); + QVERIFY(edit1->hasFocus()); + QVERIFY(!box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0); + + edit2_2->setFocus(); + QTest::qWait(125); + QVERIFY(!edit1->hasFocus()); + QVERIFY(!box_2->hasFocus()); + QVERIFY(edit2_2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + + box->setFocus(); + QTest::qWait(125); + QVERIFY(!edit2_2->hasFocus()); + QVERIFY(!edit1->hasFocus()); + QVERIFY(box->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0); + + edit2_2->setFocus(); + QTest::qWait(125); + QVERIFY(edit2_2->hasFocus()); + QVERIFY(!edit1->hasFocus()); + QVERIFY(!box->hasFocus()); + QVERIFY(!box_2->hasFocus()); + QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 2); + QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1); + QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 1); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); + QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0); +} + +void tst_QGraphicsProxyWidget::popup_basic() +{ + // ProxyWidget should automatically create proxy's when the widget creates a child + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsView view(scene); + view.setAlignment(Qt::AlignLeft | Qt::AlignTop); + view.setGeometry(0, 100, 480, 500); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QComboBox *box = new QComboBox; + box->setGeometry(0, 0, 320, 40); + box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" + << "thursday" << "saturday" << "sunday"); + QCOMPARE(proxy->childItems().count(), 0); + proxy->setWidget(box); + proxy->show(); + scene->addItem(proxy); + + QCOMPARE(box->pos(), QPoint()); + QCOMPARE(proxy->pos(), QPointF()); + + QTest::qWait(125); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + + QTest::qWait(125); + + QCOMPARE(box->pos(), QPoint()); + + QCOMPARE(proxy->childItems().count(), 1); + QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0]; + QVERIFY(child->isWidget()); + QVERIFY(child->widget()); + QStyleOptionComboBox opt; + opt.initFrom(box); + opt.editable = box->isEditable(); + if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + QSKIP("Does not work due to SH_Combobox_Popup", SkipAll); + QCOMPARE(child->widget()->parent(), static_cast(box)); + + QTest::qWait(125); + QCOMPARE(proxy->pos(), QPointF(box->pos())); + QCOMPARE(child->x(), qreal(box->x())); + QCOMPARE(child->y(), qreal(box->rect().bottom())); +#ifndef Q_OS_WIN + // The popup's coordinates on Windows are in global space, + // regardless. + QCOMPARE(child->widget()->x(), box->x()); + QCOMPARE(child->widget()->y(), box->rect().bottom()); + QCOMPARE(child->geometry().toRect(), child->widget()->geometry()); +#endif + QTest::qWait(125); +} + +void tst_QGraphicsProxyWidget::popup_subwidget() +{ + QGroupBox *groupBox = new QGroupBox; + groupBox->setTitle("GroupBox"); + groupBox->setCheckable(true); + + QComboBox *box = new QComboBox; + box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" + << "thursday" << "saturday" << "sunday"); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(new QLineEdit("QLineEdit")); + layout->addWidget(box); + layout->addWidget(new QLineEdit("QLineEdit")); + groupBox->setLayout(layout); + + QGraphicsScene scene; + QGraphicsProxyWidget *groupBoxProxy = scene.addWidget(groupBox); + groupBox->show(); + groupBox->move(10000, 10000); + QCOMPARE(groupBox->pos(), QPoint(10000, 10000)); + QCOMPARE(groupBoxProxy->pos(), QPointF(10000, 10000)); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + box->showPopup(); + + QVERIFY(!groupBoxProxy->childItems().isEmpty()); + + QStyleOptionComboBox opt; + opt.initFrom(box); + opt.editable = box->isEditable(); + if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + QSKIP("Does not work due to SH_Combobox_Popup", SkipAll); + QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(groupBoxProxy->childItems())[0]; + QWidget *popup = child->widget(); + QCOMPARE(popup->parent(), static_cast(box)); + QCOMPARE(popup->x(), box->mapToGlobal(QPoint()).x()); + QCOMPARE(popup->y(), QRect(box->mapToGlobal(QPoint()), box->size()).bottom()); + QCOMPARE(popup->size(), child->size().toSize()); +} + +void tst_QGraphicsProxyWidget::changingCursor_basic() +{ +#if defined(Q_OS_WINCE) && (!defined(GWES_ICONCURS) || defined(QT_NO_CURSOR)) + QSKIP("hover events not supported on this platform", SkipAll); +#endif +#ifndef QT_NO_CURSOR + // Confirm that mouse events are working properly by checking that + // when moving the mouse over a line edit it will change the cursor into the I + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QLineEdit *widget = new QLineEdit; + proxy->setWidget(widget); + proxy->show(); + scene.addItem(proxy); + QTest::qWait(125); + + // in + QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + sendMouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTest::qWait(125); + QCOMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor); + + // out + QTest::mouseMove(view.viewport(), QPoint(1, 1)); + sendMouseMove(view.viewport(), QPoint(1, 1)); + QTest::qWait(125); + QCOMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor); +#endif +} + +void tst_QGraphicsProxyWidget::tooltip_basic() +{ + // Confirm that mouse events are working properly by checking that + // when moving the mouse over a label with a tooptip the tooltip appears + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + QSKIP("Tooltips don't work yet", SkipAll); + + SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QLabel *widget = new QLabel; + widget->setText("If it ain't tested it's broken"); + widget->setToolTip("When in doubt, test"); + proxy->setWidget(widget); + widget->show(); + scene.addItem(proxy); + QTest::qWait(125); + + // in + QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); + QTest::qWait(3000); + + QCOMPARE(proxy->childItems().count(), 1); + QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0]; + QVERIFY(child->isWidget()); + QVERIFY(child->widget()); + QCOMPARE(child->widget()->parent(), static_cast(widget)); + QCOMPARE(child->widget()->x(), widget->x()); // ### ??? + QCOMPARE(child->widget()->y(), widget->y() + widget->height()); // ### ??? +} + +void tst_QGraphicsProxyWidget::childPos_data() +{ + QTest::addColumn("moveCombo"); + QTest::addColumn("comboPos"); + QTest::addColumn("proxyPos"); + QTest::addColumn("menuPos"); + + QTest::newRow("0") << true << QPoint() << QPointF() << QPointF(); + QTest::newRow("1") << true << QPoint(10, 0) << QPointF(10, 0) << QPointF(); + QTest::newRow("2") << true << QPoint(100, 0) << QPointF(100, 0) << QPointF(); + QTest::newRow("3") << true << QPoint(1000, 0) << QPointF(1000, 0) << QPointF(); + QTest::newRow("4") << true << QPoint(10000, 0) << QPointF(10000, 0) << QPointF(); + QTest::newRow("5") << true << QPoint(-10000, 0) << QPointF(-10000, 0) << QPointF(); + QTest::newRow("6") << true << QPoint(-1000, 0) << QPointF(-1000, 0) << QPointF(); + QTest::newRow("7") << true << QPoint(-100, 0) << QPointF(-100, 0) << QPointF(); + QTest::newRow("8") << true << QPoint(-10, 0) << QPointF(-10, 0) << QPointF(); + QTest::newRow("0-") << false << QPoint() << QPointF() << QPointF(); + QTest::newRow("1-") << false << QPoint(10, 0) << QPointF(10, 0) << QPointF(); + QTest::newRow("2-") << false << QPoint(100, 0) << QPointF(100, 0) << QPointF(); + QTest::newRow("3-") << false << QPoint(1000, 0) << QPointF(1000, 0) << QPointF(); + QTest::newRow("4-") << false << QPoint(10000, 0) << QPointF(10000, 0) << QPointF(); + QTest::newRow("5-") << false << QPoint(-10000, 0) << QPointF(-10000, 0) << QPointF(); + QTest::newRow("6-") << false << QPoint(-1000, 0) << QPointF(-1000, 0) << QPointF(); + QTest::newRow("7-") << false << QPoint(-100, 0) << QPointF(-100, 0) << QPointF(); + QTest::newRow("8-") << false << QPoint(-10, 0) << QPointF(-10, 0) << QPointF(); +} + +void tst_QGraphicsProxyWidget::childPos() +{ +#ifdef Q_OS_IRIX + QSKIP("This test is not reliable on IRIX.", SkipAll); +#endif + QFETCH(bool, moveCombo); + QFETCH(QPoint, comboPos); + QFETCH(QPointF, proxyPos); + QFETCH(QPointF, menuPos); + + QGraphicsScene scene; + + QComboBox *box = new QComboBox; + box->addItem("Item 1"); + box->addItem("Item 2"); + box->addItem("Item 3"); + box->addItem("Item 4"); + + if (moveCombo) + box->move(comboPos); + + QGraphicsProxyWidget *proxy = scene.addWidget(box); + proxy->show(); + QVERIFY(proxy->isVisible()); + QVERIFY(box->isVisible()); + + if (!moveCombo) + proxy->setPos(proxyPos); + + QCOMPARE(proxy->pos(), proxyPos); + QCOMPARE(box->pos(), comboPos); + + for (int i = 0; i < 2; ++i) { + box->showPopup(); + QTest::qWait(50); + + QWidget *menu = 0; + foreach (QObject *child, box->children()) { + if ((menu = qobject_cast(child))) + break; + } + QVERIFY(menu); + QVERIFY(menu->isVisible()); + QVERIFY(menu->testAttribute(Qt::WA_DontShowOnScreen)); + + QCOMPARE(proxy->childItems().size(), 1); + QGraphicsProxyWidget *proxyChild = 0; + foreach (QGraphicsItem *child, proxy->childItems()) { + if (child->isWidget() && (proxyChild = qobject_cast(static_cast(child)))) + break; + } + QVERIFY(proxyChild); + QVERIFY(proxyChild->isVisible()); + qreal expectedXPosition = 0.0; +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + // The Mac style wants the popup to show up at QPoint(4 - 11, 1). + // See QMacStyle::subControlRect SC_ComboBoxListBoxPopup. + if (qobject_cast(QApplication::style())) + expectedXPosition = qreal(4 - 11); +#endif + QCOMPARE(proxyChild->pos().x(), expectedXPosition); + menu->hide(); + } +} + +void tst_QGraphicsProxyWidget::autoShow() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsProxyWidget *proxy1 = scene.addWidget(new QPushButton("Button1")); + + QPushButton *button2 = new QPushButton("Button2"); + button2->hide(); + QGraphicsProxyWidget *proxy2 = new QGraphicsProxyWidget(); + proxy2->setWidget(button2); + scene.addItem(proxy2); + + view.show(); + QApplication::processEvents(); + + QCOMPARE(proxy1->isVisible(), true); + QCOMPARE(proxy2->isVisible(), false); + +} + +Q_DECLARE_METATYPE(QList) +void tst_QGraphicsProxyWidget::windowOpacity() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + QGraphicsProxyWidget *proxy = scene.addWidget(widget); + proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QApplication::sendPostedEvents(); + QTest::qWait(100); + + qRegisterMetaType >("QList"); + QSignalSpy signalSpy(&scene, SIGNAL(changed(const QList &))); + + EventSpy eventSpy(widget); + QVERIFY(widget->isVisible()); + + widget->setWindowOpacity(0.5); + QTest::qWait(100); + + // Make sure setWindowOpacity triggers an update on the scene, + // and not on the widget or the proxy itself. The entire proxy needs an update + // in case it has a window decoration. Update: QGraphicsItem::CacheMode is + // disabled on platforms without alpha channel support in QPixmap (e.g., + // X11 without XRender). + int paints = 0; +#ifdef Q_WS_X11 + paints = !X11->use_xrender; +#endif + QCOMPARE(eventSpy.counts[QEvent::UpdateRequest], 0); + QCOMPARE(eventSpy.counts[QEvent::Paint], paints); + + QCOMPARE(signalSpy.count(), 1); + const QList arguments = signalSpy.takeFirst(); + const QList updateRects = qvariant_cast >(arguments.at(0)); + QCOMPARE(updateRects.size(), 1); + QCOMPARE(updateRects.at(0), proxy->sceneBoundingRect()); +} + +void tst_QGraphicsProxyWidget::stylePropagation() +{ + QPointer windowsStyle = new QWindowsStyle; + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy + QCOMPARE(proxy.style(), QApplication::style()); + edit->setStyle(windowsStyle); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 1); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 1); + QCOMPARE(proxy.style(), (QStyle *)windowsStyle); + edit->setStyle(0); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 2); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 2); + QCOMPARE(proxy.style(), QApplication::style()); + QCOMPARE(edit->style(), QApplication::style()); + QVERIFY(windowsStyle); // not deleted + + // Proxy to widget + proxy.setStyle(windowsStyle); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 3); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 3); + QCOMPARE(edit->style(), (QStyle *)windowsStyle); + proxy.setStyle(0); + QCOMPARE(editSpy.counts[QEvent::StyleChange], 4); + QCOMPARE(proxySpy.counts[QEvent::StyleChange], 4); + QCOMPARE(proxy.style(), QApplication::style()); + QCOMPARE(edit->style(), QApplication::style()); + QVERIFY(windowsStyle); // not deleted + + delete windowsStyle; +} + +void tst_QGraphicsProxyWidget::palettePropagation() +{ + // Construct a palette with an unlikely setup + QPalette lineEditPalette = QApplication::palette("QLineEdit"); + QPalette palette = lineEditPalette; + palette.setBrush(QPalette::Text, Qt::red); + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy (no change) + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + edit->setPalette(palette); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 1); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); + QVERIFY(edit->testAttribute(Qt::WA_SetPalette)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(proxy.palette(), lineEditPalette); + edit->setPalette(QPalette()); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 2); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(proxy.palette(), lineEditPalette); + + // Proxy to widget + proxy.setPalette(palette); + QVERIFY(proxy.testAttribute(Qt::WA_SetPalette)); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QCOMPARE(edit->palette(), palette); + QCOMPARE(edit->palette(), proxy.palette()); + QCOMPARE(edit->palette().color(QPalette::Text), QColor(Qt::red)); + proxy.setPalette(QPalette()); + QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); + QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + QCOMPARE(editSpy.counts[QEvent::PaletteChange], 4); + QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 2); + QCOMPARE(edit->palette(), lineEditPalette); +} + +void tst_QGraphicsProxyWidget::fontPropagation() +{ + // Construct a font with an unlikely setup + QFont lineEditFont = QApplication::font("QLineEdit"); + QFont font = lineEditFont; + font.setPointSize(43); + + QLineEdit *edit = new QLineEdit; + QGraphicsProxyWidget proxy; + proxy.setWidget(edit); + + EventSpy editSpy(edit); + EventSpy proxySpy(&proxy); + + // Widget to proxy (no change) + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + edit->setFont(font); + QCOMPARE(editSpy.counts[QEvent::FontChange], 1); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 0); + QVERIFY(edit->testAttribute(Qt::WA_SetFont)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(proxy.font(), lineEditFont); + edit->setFont(QFont()); + QCOMPARE(editSpy.counts[QEvent::FontChange], 2); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 0); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(proxy.font(), lineEditFont); + + // Proxy to widget + proxy.setFont(font); + QVERIFY(proxy.testAttribute(Qt::WA_SetFont)); + QCOMPARE(editSpy.counts[QEvent::FontChange], 3); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 1); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QCOMPARE(edit->font(), font); + QCOMPARE(edit->font(), proxy.font()); + QCOMPARE(edit->font().pointSize(), 43); + proxy.setFont(QFont()); + QVERIFY(!proxy.testAttribute(Qt::WA_SetFont)); + QVERIFY(!edit->testAttribute(Qt::WA_SetFont)); + QCOMPARE(editSpy.counts[QEvent::FontChange], 4); + QCOMPARE(proxySpy.counts[QEvent::FontChange], 2); + QCOMPARE(edit->font(), lineEditFont); + + proxy.setFont(font); + QLineEdit *edit2 = new QLineEdit; + proxy.setWidget(edit2); + delete edit; + QCOMPARE(edit2->font().pointSize(), 43); +} + +class MainWidget : public QMainWindow +{ +Q_OBJECT +public: + MainWidget() : QMainWindow() { + view = new QGraphicsView(this); + scene = new QGraphicsScene(this); + item = new QGraphicsWidget(); + widget = new QGraphicsProxyWidget(item); + layout = new QGraphicsLinearLayout(item); + layout->addItem(widget); + item->setLayout(layout); + button = new QPushButton("Push me"); + widget->setWidget(button); + setCentralWidget(view); + scene->addItem(item); + view->setScene(scene); + scene->setFocusItem(item); + layout->activate(); + } + QGraphicsView* view; + QGraphicsScene* scene; + QGraphicsWidget * item; + QGraphicsLinearLayout * layout; + QGraphicsProxyWidget * widget; + QPushButton * button; +}; + +void tst_QGraphicsProxyWidget::dontCrashWhenDie() +{ + MainWidget *w = new MainWidget(); + w->show(); + QTest::qWait(200); + QTest::mouseMove(w->view->viewport(), w->view->mapFromScene(w->widget->mapToScene(w->widget->boundingRect().center()))); + delete w->item; +} + +void tst_QGraphicsProxyWidget::createProxyForChildWidget() +{ + QGraphicsScene scene; + + QLineEdit *edit1 = new QLineEdit; + edit1->setText("QLineEdit 1"); + QLineEdit *edit2 = new QLineEdit; + edit2->setText("QLineEdit 2"); + QCheckBox *checkbox = new QCheckBox("QCheckBox"); + QVBoxLayout *vlayout = new QVBoxLayout; + + vlayout->addWidget(edit1); + vlayout->addWidget(edit2); + vlayout->addWidget(checkbox); + vlayout->insertStretch(-1); + + QGroupBox *box = new QGroupBox("QGroupBox"); + box->setCheckable(true); + box->setChecked(true); + box->setLayout(vlayout); + + QDial *leftDial = new QDial; + QDial *rightDial = new QDial; + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(leftDial); + layout->addWidget(box); + layout->addWidget(rightDial); + window.setLayout(layout); + + QVERIFY(window.graphicsProxyWidget() == 0); + QVERIFY(checkbox->graphicsProxyWidget() == 0); + + QGraphicsProxyWidget *windowProxy = scene.addWidget(&window); + QGraphicsView view(&scene); + view.show(); + view.resize(500,500); + + QVERIFY(window.graphicsProxyWidget() == windowProxy); + QVERIFY(box->graphicsProxyWidget() == 0); + QVERIFY(checkbox->graphicsProxyWidget() == 0); + + QPointer checkboxProxy = windowProxy->createProxyForChildWidget(checkbox); + + QGraphicsProxyWidget *boxProxy = box->graphicsProxyWidget(); + + QVERIFY(boxProxy); + QVERIFY(checkbox->graphicsProxyWidget() == checkboxProxy); + QVERIFY(checkboxProxy->parentItem() == boxProxy); + QVERIFY(boxProxy->parentItem() == windowProxy); + + QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint())); + QVERIFY(checkboxProxy->size() == checkbox->size()); + QVERIFY(boxProxy->size() == box->size()); + + window.resize(500,500); + QVERIFY(windowProxy->size() == QSize(500,500)); + QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint())); + QVERIFY(checkboxProxy->size() == checkbox->size()); + QVERIFY(boxProxy->size() == box->size()); + + QTest::qWait(10); + + + QSignalSpy spy(checkbox, SIGNAL(clicked())); + + QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); + QTRY_COMPARE(spy.count(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); + QTRY_COMPARE(spy.count(), 1); + + + + boxProxy->setWidget(0); + + QVERIFY(checkbox->graphicsProxyWidget() == 0); + QVERIFY(box->graphicsProxyWidget() == 0); + QVERIFY(checkboxProxy == 0); + + delete boxProxy; +} + +class ContextMenuWidget : public QWidget +{ + Q_OBJECT +public: + ContextMenuWidget() + : embeddedPopup(false), + gotContextMenuEvent(false) + { } + bool embeddedPopup; + bool gotContextMenuEvent; +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::ContextMenu) + QTimer::singleShot(0, this, SLOT(checkMenu())); + return QWidget::event(event); + } + void contextMenuEvent(QContextMenuEvent *) + { + gotContextMenuEvent = true; + } + +private slots: + void checkMenu() + { + if (qFindChild(this)) + embeddedPopup = true; + hide(); + } +}; + +void tst_QGraphicsProxyWidget::actionsContextMenu() +{ + ContextMenuWidget *widget = new ContextMenuWidget; + widget->addAction(new QAction("item 1", widget)); + widget->addAction(new QAction("item 2", widget)); + widget->addAction(new QAction("item 3", widget)); + widget->setContextMenuPolicy(Qt::ActionsContextMenu); + + QGraphicsScene scene; + scene.addWidget(widget); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, + view.viewport()->rect().center(), + view.viewport()->mapToGlobal(view.viewport()->rect().center())); + contextMenuEvent.accept(); + qApp->sendEvent(view.viewport(), &contextMenuEvent); + + QVERIFY(widget->embeddedPopup); + QVERIFY(!widget->gotContextMenuEvent); +} + + +void tst_QGraphicsProxyWidget::deleteProxyForChildWidget() +{ + QDialog dialog; + dialog.resize(320, 120); + dialog.move(80, 40); + + QGraphicsScene scene; + scene.setSceneRect(QRectF(0, 0, 640, 480)); + QGraphicsView view(&scene); + QComboBox *combo = new QComboBox; + combo->addItems(QStringList() << "red" << "green" << "blue" << "white" << "black" << "yellow" << "cyan" << "magenta"); + dialog.setLayout(new QVBoxLayout); + dialog.layout()->addWidget(combo); + + QGraphicsProxyWidget *proxy = scene.addWidget(&dialog); + view.show(); + + proxy->setWidget(0); + //just don't crash +} + +void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data() +{ + QTest::addColumn("bypass"); + + QTest::newRow("autoembed") << false; + QTest::newRow("bypass") << true; +} + +void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() +{ + QFETCH(bool, bypass); + + QWidget *widget = new QWidget; + widget->resize(100, 100); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + + QGraphicsProxyWidget *proxy = scene.addWidget(widget); + + QCOMPARE(proxy->widget(), widget); + QVERIFY(proxy->childItems().isEmpty()); + + Qt::WindowFlags flags; + flags |= Qt::Dialog; + if (bypass) + flags |= Qt::BypassGraphicsProxyWidget; + QFileDialog *dialog = new QFileDialog(widget, flags); + dialog->show(); + + QCOMPARE(proxy->childItems().size(), bypass ? 0 : 1); + if (!bypass) + QCOMPARE(((QGraphicsProxyWidget *)proxy->childItems().first())->widget(), (QWidget *)dialog); +} + +static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos) +{ + event->setScenePos(pos); + event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos))); + event->setButtons(Qt::LeftButton); + event->setModifiers(0); + event->setPossibleActions(Qt::CopyAction); + event->setProposedAction(Qt::CopyAction); + event->setDropAction(Qt::CopyAction); + event->setWidget(view->viewport()); + event->setSource(view->viewport()); + event->ignore(); +} + +void tst_QGraphicsProxyWidget::dragDrop() +{ + QPushButton *button = new QPushButton; // acceptDrops(false) + QLineEdit *edit = new QLineEdit; // acceptDrops(true) + + QGraphicsScene scene; + QGraphicsProxyWidget *buttonProxy = scene.addWidget(button); + QGraphicsProxyWidget *editProxy = scene.addWidget(edit); + QVERIFY(buttonProxy->acceptDrops()); + QVERIFY(editProxy->acceptDrops()); + + button->setGeometry(0, 0, 100, 50); + edit->setGeometry(0, 60, 100, 50); + + QGraphicsView view(&scene); + view.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + + QMimeData data; + data.setText("hei"); + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragEnter); + makeDndEvent(&event, &view, QPointF(50, 25)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + makeDndEvent(&event, &view, QPointF(50, 25)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(!event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDragMove); + makeDndEvent(&event, &view, QPointF(50, 75)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + { + QGraphicsSceneDragDropEvent event(QEvent::GraphicsSceneDrop); + makeDndEvent(&event, &view, QPointF(50, 75)); + event.setMimeData(&data); + qApp->sendEvent(&scene, &event); + QVERIFY(event.isAccepted()); + } + QCOMPARE(edit->text(), QString("hei")); +} + +QTEST_MAIN(tst_QGraphicsProxyWidget) +#include "tst_qgraphicsproxywidget.moc" + +#else // QT_NO_STYLE_CLEANLOOKS +QTEST_NOOP_MAIN +#endif + -- cgit v1.2.3