diff options
Diffstat (limited to 'tests/auto/widgets/graphicsview/qgraphicsproxywidget')
3 files changed, 669 insertions, 519 deletions
diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST new file mode 100644 index 0000000000..731b3ae253 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST @@ -0,0 +1,11 @@ +[scrollUpdate] +android +b2qt +[windowOpacity] +macos ci +[wheelEventPropagation] +android +[forwardTouchEvent] +android +[touchEventPropagation] +android diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt index a1bf1270c2..f28ff74cfa 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qgraphicsproxywidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsproxywidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsproxywidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsproxywidget SOURCES tst_qgraphicsproxywidget.cpp DEFINES QTEST_QPA_MOUSE_HANDLING - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index e9ba24f4fc..c3f77957f8 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -1,38 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtGui> #include <QtWidgets> +#include <QSignalSpy> #include <private/qgraphicsproxywidget_p.h> #include <private/qlayoutengine_p.h> // qSmartMin functions... +#include <QtWidgets/private/qapplication_p.h> + +Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests") + /* Notes: @@ -60,7 +40,7 @@ public: QMap<QEvent::Type, int> counts; protected: - bool eventFilter(QObject *, QEvent *event) + bool eventFilter(QObject *, QEvent *event) override { ++counts[event->type()]; return false; @@ -74,7 +54,6 @@ class tst_QGraphicsProxyWidget : public QObject private slots: void initTestCase(); void cleanup(); - void qgraphicsproxywidget_data(); void qgraphicsproxywidget(); void paint(); void paint_2(); @@ -92,13 +71,10 @@ private slots: void focusProxy_QTBUG_51856(); 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(); @@ -108,8 +84,6 @@ private slots: #if QT_CONFIG(wheelevent) void wheelEvent(); #endif - void sizeHint_data(); - void sizeHint(); void sizePolicy(); void minimumSize(); void maximumSize(); @@ -162,88 +136,39 @@ private slots: void mapToGlobal(); void mapToGlobalWithoutScene(); void QTBUG_43780_visibility(); +#if QT_CONFIG(wheelevent) + void wheelEventPropagation(); +#endif void forwardTouchEvent(); + void touchEventPropagation(); }; // Subclass that exposes the protected functions. class SubQGraphicsProxyWidget : public QGraphicsProxyWidget { - + friend tst_QGraphicsProxyWidget; public: - SubQGraphicsProxyWidget(QGraphicsItem *parent = nullptr) : 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); } + using QGraphicsProxyWidget::QGraphicsProxyWidget; - 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 = nullptr) { + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override { paintCount++; QGraphicsProxyWidget::paint(painter, option, widget); } - void focusOutEvent(QFocusEvent *event) + void focusOutEvent(QFocusEvent *event) override { focusOut++; QGraphicsProxyWidget::focusOutEvent(event); } - bool eventFilter(QObject *object, QEvent *event) { + bool eventFilter(QObject *object, QEvent *event) override { if (event->type() == QEvent::KeyPress && object == widget()) keyPress++; return QGraphicsProxyWidget::eventFilter(object, event); } - int paintCount; - int keyPress; - int focusOut; + int paintCount = 0; + int keyPress = 0; + int focusOut = 0; }; #if QT_CONFIG(wheelevent) @@ -252,7 +177,7 @@ class WheelWidget : public QWidget public: WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); } - virtual void wheelEvent(QWheelEvent *event) { event->accept(); wheelEventCalled = true; } + virtual void wheelEvent(QWheelEvent *event) override { event->accept(); wheelEventCalled = true; } bool wheelEventCalled; }; @@ -276,10 +201,6 @@ void tst_QGraphicsProxyWidget::cleanup() QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); } -void tst_QGraphicsProxyWidget::qgraphicsproxywidget_data() -{ -} - void tst_QGraphicsProxyWidget::qgraphicsproxywidget() { SubQGraphicsProxyWidget proxy; @@ -288,46 +209,47 @@ void tst_QGraphicsProxyWidget::qgraphicsproxywidget() QCOMPARE(proxy.type(), int(QGraphicsProxyWidget::Type)); QVERIFY(!proxy.widget()); QEvent event(QEvent::None); - proxy.call_eventFilter(0, &event); + proxy.eventFilter(0, &event); QFocusEvent focusEvent(QEvent::FocusIn); focusEvent.ignore(); - proxy.call_focusInEvent(&focusEvent); + proxy.focusInEvent(&focusEvent); QCOMPARE(focusEvent.isAccepted(), false); - QCOMPARE(proxy.call_focusNextPrevChild(false), false); - QCOMPARE(proxy.call_focusNextPrevChild(true), false); - proxy.call_focusOutEvent(&focusEvent); + QCOMPARE(proxy.focusNextPrevChild(false), false); + QCOMPARE(proxy.focusNextPrevChild(true), false); + proxy.focusOutEvent(&focusEvent); QHideEvent hideEvent; - proxy.call_hideEvent(&hideEvent); + proxy.hideEvent(&hideEvent); QGraphicsSceneHoverEvent hoverEvent; - proxy.call_hoverEnterEvent(&hoverEvent); - proxy.call_hoverLeaveEvent(&hoverEvent); - proxy.call_hoverMoveEvent(&hoverEvent); + proxy.hoverEnterEvent(&hoverEvent); + proxy.hoverLeaveEvent(&hoverEvent); + proxy.hoverMoveEvent(&hoverEvent); QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier); - proxy.call_keyPressEvent(&keyEvent); - proxy.call_keyReleaseEvent(&keyEvent); + proxy.keyPressEvent(&keyEvent); + proxy.keyReleaseEvent(&keyEvent); QGraphicsSceneMouseEvent mouseEvent; - proxy.call_mouseDoubleClickEvent(&mouseEvent); - proxy.call_mouseMoveEvent(&mouseEvent); - proxy.call_mousePressEvent(&mouseEvent); - proxy.call_mouseReleaseEvent(&mouseEvent); + proxy.mouseDoubleClickEvent(&mouseEvent); + proxy.mouseMoveEvent(&mouseEvent); + proxy.mousePressEvent(&mouseEvent); + proxy.mouseReleaseEvent(&mouseEvent); QGraphicsSceneResizeEvent resizeEvent; - proxy.call_resizeEvent(&resizeEvent); + proxy.resizeEvent(&resizeEvent); QShowEvent showEvent; - proxy.call_showEvent(&showEvent); - proxy.call_sizeHint(Qt::PreferredSize, QSizeF()); + proxy.showEvent(&showEvent); + proxy.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) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter. painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform @@ -386,6 +308,8 @@ void tst_QGraphicsProxyWidget::setWidget() QGraphicsView view(&scene); view.show(); QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Fusion"))); + if (style.isNull()) + QSKIP("This test requires the Fusion style"); QVERIFY(QTest::qWaitForWindowExposed(&view)); QPointer<SubQGraphicsProxyWidget> proxy = new SubQGraphicsProxyWidget; SubQGraphicsProxyWidget parentProxy; @@ -411,7 +335,7 @@ void tst_QGraphicsProxyWidget::setWidget() widget->setStyle(style.data()); widget->setFont(QFont("Times")); widget->setVisible(true); - QApplication::setActiveWindow(widget); + QApplicationPrivate::setActiveWindow(widget); widget->activateWindow(); widget->setEnabled(true); widget->resize(325, 241); @@ -433,6 +357,10 @@ void tst_QGraphicsProxyWidget::setWidget() if (shouldBeInsertable) subWidget->setAttribute(Qt::WA_QuitOnClose, true); + if (hasParent) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "QGraphicsProxyWidget::setWidget: cannot embed widget .* which is not a toplevel widget, and is not a child of an embedded widget")); + } proxy->setWidget(subWidget); if (shouldBeInsertable) { @@ -548,7 +476,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() switch (eventType) { case QEvent::None: { QEvent event(QEvent::None); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); break; } case QEvent::Resize: { @@ -580,7 +508,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() widget->hide(); } else { QHideEvent event; - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->isVisible(), false); break; @@ -592,7 +520,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() widget->show(); } else { QShowEvent event; - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->isVisible(), true); break; @@ -607,7 +535,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() QCOMPARE(proxy->isEnabled(), false); } else { QEvent event(QEvent::EnabledChange); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); // match the widget not the event QCOMPARE(proxy->isEnabled(), false); } @@ -635,7 +563,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier); } else { QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->keyPress, 1); break; @@ -695,7 +623,7 @@ void tst_QGraphicsProxyWidget::focusInEvent() QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); event.ignore(); - proxy->call_focusInEvent(&event); + proxy->focusInEvent(&event); QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus); } @@ -712,7 +640,7 @@ void tst_QGraphicsProxyWidget::focusInEventNoWidget() QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); event.ignore(); //should not crash - proxy->call_focusInEvent(&event); + proxy->focusInEvent(&event); } void tst_QGraphicsProxyWidget::focusNextPrevChild_data() @@ -750,23 +678,40 @@ void tst_QGraphicsProxyWidget::focusNextPrevChild() // 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("<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\"> p, li { white-space: pre-wrap; } </style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\"> <p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.slashdot.org\"><span style=\" text-decoration: underline; color:#0000ff;\">old</span></a> foo <a href=\"http://www.reddit.org\"><span style=\" text-decoration: underline; color:#0000ff;\">new</span></a></p></body></html>"); - widget->setTextInteractionFlags(Qt::TextBrowserInteraction); + std::unique_ptr<SubQGraphicsProxyWidget> proxyGuard(new SubQGraphicsProxyWidget); + auto *proxy = proxyGuard.get(); - if (hasWidget) + if (hasWidget) { + QLabel *widget = new QLabel; + widget->setText(R"( + <html> + <head> + <meta name=\"qrichtext\" content=\"1\" /> + <style type=\"text/css\"> + p, li { white-space: pre-wrap; } + </style> + </head> + <body> + <p> + <a href=\"http://www.slashdot.org\"> + <span style=\" text-decoration: underline; color:#0000ff;\">old</span> + </a> foo + <a href=\"http://www.reddit.org\"> + <span style=\" text-decoration: underline; color:#0000ff;\">new</span> + </a> + </p> + </body> + </html>)"); + widget->setTextInteractionFlags(Qt::TextBrowserInteraction); proxy->setWidget(widget); + } QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); if (hasScene) { - scene.addItem(proxy); + scene.addItem(proxyGuard.release()); proxy->show(); // widget should take precedence over scene so make scene.focusNextPrevChild return false @@ -781,12 +726,7 @@ void tst_QGraphicsProxyWidget::focusNextPrevChild() QVERIFY(proxy->hasFocus()); } - QCOMPARE(proxy->call_focusNextPrevChild(next), focusNextPrevChild); - - if (!hasScene) - delete proxy; - if (!hasWidget) - delete widget; + QCOMPARE(proxy->focusNextPrevChild(next), focusNextPrevChild); } void tst_QGraphicsProxyWidget::focusOutEvent_data() @@ -810,18 +750,17 @@ void tst_QGraphicsProxyWidget::focusOutEvent() SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; scene.addItem(proxy); view.show(); - QApplication::setActiveWindow(&view); view.activateWindow(); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.isVisible()); QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); - QScopedPointer<QWidget> widgetGuard(new QWidget); - QWidget *widget = widgetGuard.data(); - widget->setFocusPolicy(Qt::WheelFocus); + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); + widgetGuard->setFocusPolicy(Qt::WheelFocus); if (hasWidget) - proxy->setWidget(widgetGuard.take()); + proxy->setWidget(widgetGuard.release()); proxy->show(); proxy->setFocus(); QVERIFY(proxy->hasFocus()); @@ -832,41 +771,20 @@ void tst_QGraphicsProxyWidget::focusOutEvent() if (!call) { QWidget *other = new QLineEdit(&view); other->show(); - QApplication::processEvents(); QTRY_VERIFY(other->isVisible()); other->setFocus(); QTRY_VERIFY(other->hasFocus()); - 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); - */ - } } } void tst_QGraphicsProxyWidget::focusProxy_QTBUG_51856() { +#ifdef ANDROID + QSKIP("This test leads to failures on subsequent test cases, QTBUG-119574"); +#endif // QSpinBox has an internal QLineEdit; this QLineEdit has the spinbox // as its focus proxy. struct FocusedSpinBox : QSpinBox @@ -889,6 +807,9 @@ void tst_QGraphicsProxyWidget::focusProxy_QTBUG_51856() } }; + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QGraphicsScene scene; QGraphicsView view(&scene); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; @@ -943,19 +864,19 @@ public: installEventFilter(this); } - void enterEvent(QEnterEvent *event) + void enterEvent(QEnterEvent *event) override { enterCount++; QWidget::enterEvent(event); } - void leaveEvent(QEvent *event ) + void leaveEvent(QEvent *event ) override { leaveCount++; QWidget::leaveEvent(event); } - void mouseMoveEvent(QMouseEvent *event) + void mouseMoveEvent(QMouseEvent *event) override { event->setAccepted(true); moveCount++; @@ -970,7 +891,7 @@ public: int hoverLeave; int hoverMove; protected: - bool eventFilter(QObject *object, QEvent *event) + bool eventFilter(QObject *object, QEvent *event) override { switch (event->type()) { case QEvent::HoverEnter: @@ -1010,22 +931,21 @@ void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; - QScopedPointer<EventLogger> widgetGuard(new EventLogger); - EventLogger *widget = widgetGuard.data(); + std::unique_ptr<EventLogger> widgetGuard(new EventLogger); + EventLogger *widget = widgetGuard.get(); widget->resize(50, 50); widget->setAttribute(Qt::WA_Hover, hoverEnabled); widget->setMouseTracking(true); view.resize(100, 100); if (hasWidget) - proxy->setWidget(widgetGuard.take()); + proxy->setWidget(widgetGuard.release()); proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); // outside graphics item QTest::mouseMove(&view, QPoint(10, 10)); @@ -1048,75 +968,6 @@ void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() QTRY_COMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0); } -void tst_QGraphicsProxyWidget::hoverMoveEvent_data() -{ - QTest::addColumn<bool>("hasWidget"); - QTest::addColumn<bool>("hoverEnabled"); - QTest::addColumn<bool>("mouseTracking"); - QTest::addColumn<bool>("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); - QByteArray name = QByteArrayLiteral("hasWidget:") + (hasWidget ? '1' : '0') + ", hover:" - + (hoverEnabled ? '1' : '0') + ", mouseTracking:" - + (mouseTracking ? '1' : '0') + ", mouseDown: " + (mouseDown ? '1' : '0'); - QTest::newRow(name.constData()) << 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..."); - - // 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(12); - - if (mouseDown) - QTest::mousePress(view.viewport(), Qt::LeftButton); - - // move a little bit - QTest::mouseMove(&view, QPoint(60, 60)); - 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<bool>("hasWidget"); @@ -1133,30 +984,27 @@ void tst_QGraphicsProxyWidget::keyPressEvent() QGraphicsView view(&scene); view.show(); view.viewport()->setFocus(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QLineEdit *widget = new QLineEdit; + std::unique_ptr<QLineEdit> widgetGuard(new QLineEdit); + QLineEdit *widget = widgetGuard.get(); widget->resize(50, 50); view.resize(100, 100); - if (hasWidget) { - proxy->setWidget(widget); - proxy->show(); - } - proxy->setPos(50, 0); + if (hasWidget) + proxy->setWidget(widgetGuard.release()); + scene.addItem(proxy); + proxy->show(); + proxy->setPos(50, 0); proxy->setFocus(); QTest::keyPress(view.viewport(), 'x'); QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString()); - - if (!hasWidget) - delete widget; } void tst_QGraphicsProxyWidget::keyReleaseEvent_data() @@ -1174,19 +1022,18 @@ void tst_QGraphicsProxyWidget::keyReleaseEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QPushButton *widget = new QPushButton; - QSignalSpy spy(widget, SIGNAL(clicked())); - widget->resize(50, 50); + std::unique_ptr<QPushButton> widgetGuard(new QPushButton); + QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked())); + widgetGuard->resize(50, 50); view.resize(100, 100); if (hasWidget) { - proxy->setWidget(widget); + proxy->setWidget(widgetGuard.release()); proxy->show(); } proxy->setPos(50, 0); @@ -1194,59 +1041,43 @@ void tst_QGraphicsProxyWidget::keyReleaseEvent() proxy->setFocus(); QTest::keyPress(view.viewport(), Qt::Key_Space); - QTRY_COMPARE(spy.count(), 0); + QTRY_COMPARE(spy.size(), 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<bool>("hasWidget"); - QTest::newRow("widget") << true; - QTest::newRow("no widget") << false; + QTRY_COMPARE(spy.size(), hasWidget ? 1 : 0); } // protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) void tst_QGraphicsProxyWidget::mouseDoubleClickEvent() { - QFETCH(bool, hasWidget); - QGraphicsScene scene; QGraphicsView view(&scene); - view.show(); - - QApplication::setActiveWindow(&view); - QVERIFY(QTest::qWaitForWindowActive(&view)); - QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); 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->setWidget(widget); + proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); proxy->setFocus(); + view.resize(100, 100); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); // wait for scene to be updated before doing any coordinate mappings on it - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); QPoint pointInLineEdit = view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); - QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); - QTRY_COMPARE(widget->selectedText(), hasWidget ? QString("foo") : QString()); - - if (!hasWidget) - delete widget; + QTRY_COMPARE(widget->selectedText(), QString("foo")); } void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data() @@ -1269,27 +1100,24 @@ void tst_QGraphicsProxyWidget::mousePressReleaseEvent() SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QPushButton *widget = new QPushButton; - QSignalSpy spy(widget, SIGNAL(clicked())); - widget->resize(50, 50); + std::unique_ptr<QPushButton> widgetGuard(new QPushButton); + QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked())); + widgetGuard->resize(50, 50); if (hasWidget) - proxy->setWidget(widget); + proxy->setWidget(widgetGuard.release()); proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); proxy->setFocus(); // wait for scene to be updated before doing any coordinate mappings on it - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); QPoint buttonCenter = view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, buttonCenter); - QTRY_COMPARE(spy.count(), 0); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, buttonCenter); - QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); - - if (!hasWidget) - delete widget; + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, buttonCenter); + QTRY_COMPARE(spy.size(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, buttonCenter); + QTRY_COMPARE(spy.size(), hasWidget ? 1 : 0); } void tst_QGraphicsProxyWidget::resizeEvent_data() @@ -1305,19 +1133,17 @@ void tst_QGraphicsProxyWidget::resizeEvent() QFETCH(bool, hasWidget); SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + if (hasWidget) - proxy.setWidget(widget); + proxy.setWidget(new QWidget); QSize newSize(100, 100); QGraphicsSceneResizeEvent event; event.setOldSize(QSize(10, 10)); event.setNewSize(newSize); - proxy.call_resizeEvent(&event); + proxy.resizeEvent(&event); if (hasWidget) - QCOMPARE(widget->size(), newSize); - if (!hasWidget) - delete widget; + QCOMPARE(proxy.widget()->size(), newSize); } void tst_QGraphicsProxyWidget::paintEvent() @@ -1326,7 +1152,6 @@ void tst_QGraphicsProxyWidget::paintEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -1342,7 +1167,7 @@ void tst_QGraphicsProxyWidget::paintEvent() QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(&proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready + QTRY_VERIFY(sceneChangedSpy.size() > 0); // make sure the scene is ready proxy.paintCount = 0; w->update(); @@ -1376,48 +1201,13 @@ void tst_QGraphicsProxyWidget::wheelEvent() } #endif // QT_CONFIG(wheelevent) -Q_DECLARE_METATYPE(Qt::SizeHint) -void tst_QGraphicsProxyWidget::sizeHint_data() -{ - QTest::addColumn<Qt::SizeHint>("which"); - QTest::addColumn<QSizeF>("constraint"); - QTest::addColumn<QSizeF>("sizeHint"); - QTest::addColumn<bool>("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"); - 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); - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); QSizePolicy proxyPol(QSizePolicy::Maximum, QSizePolicy::Expanding); proxy.setSizePolicy(proxyPol); QSizePolicy widgetPol(QSizePolicy::Fixed, QSizePolicy::Minimum); @@ -1426,7 +1216,7 @@ void tst_QGraphicsProxyWidget::sizePolicy() QCOMPARE(proxy.sizePolicy(), proxyPol); QCOMPARE(widget->sizePolicy(), widgetPol); if (hasWidget) { - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.sizePolicy(), widgetPol); } else { QCOMPARE(proxy.sizePolicy(), proxyPol); @@ -1439,42 +1229,39 @@ void tst_QGraphicsProxyWidget::sizePolicy() QCOMPARE(proxy.sizePolicy(), proxyPol); else QCOMPARE(proxy.sizePolicy(), widgetPol); - - if (!hasWidget) - delete widget; } } void tst_QGraphicsProxyWidget::minimumSize() { - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); QSize minSize(50, 50); - widget->setMinimumSize(minSize); + widgetGuard->setMinimumSize(minSize); proxy.resize(30, 30); - widget->resize(30,30); + widgetGuard->resize(30,30); QCOMPARE(proxy.size(), QSizeF(30, 30)); - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.size().toSize(), minSize); QCOMPARE(proxy.minimumSize().toSize(), minSize); - widget->setMinimumSize(70, 70); + proxy.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; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); QSize maxSize(150, 150); - widget->setMaximumSize(maxSize); + widgetGuard->setMaximumSize(maxSize); proxy.resize(200, 200); - widget->resize(200,200); + widgetGuard->resize(200,200); QCOMPARE(proxy.size(), QSizeF(200, 200)); - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.size().toSize(), maxSize); QCOMPARE(proxy.maximumSize().toSize(), maxSize); - widget->setMaximumSize(70, 70); + proxy.widget()->setMaximumSize(70, 70); QCOMPARE(proxy.maximumSize(), QSizeF(70, 70)); QCOMPARE(proxy.size(), QSizeF(70, 70)); } @@ -1488,7 +1275,7 @@ public: QRegion paintEventRegion; int npaints; protected: - void paintEvent(QPaintEvent *event) + void paintEvent(QPaintEvent *event) override { ++npaints; paintEventRegion += event->region(); @@ -1515,7 +1302,7 @@ public slots: } protected: - void paintEvent(QPaintEvent *event) + void paintEvent(QPaintEvent *event) override { ++npaints; paintEventRegion += event->region(); @@ -1535,6 +1322,9 @@ static QList<QRect> rects(const QRegion ®ion) void tst_QGraphicsProxyWidget::scrollUpdate() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + ScrollWidget *widget = new ScrollWidget; QGraphicsScene scene; @@ -1544,7 +1334,7 @@ void tst_QGraphicsProxyWidget::scrollUpdate() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.npaints >= 1); - QTest::qWait(20); + QTest::qWait(150); widget->paintEventRegion = QRegion(); widget->npaints = 0; view.paintEventRegion = QRegion(); @@ -1748,7 +1538,6 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -1832,7 +1621,6 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -1965,7 +1753,6 @@ void tst_QGraphicsProxyWidget::tabFocus_complexWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -2102,7 +1889,6 @@ void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -2276,7 +2062,6 @@ void tst_QGraphicsProxyWidget::setFocus_simpleWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2348,7 +2133,6 @@ void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2427,7 +2211,6 @@ void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2500,28 +2283,29 @@ void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() void tst_QGraphicsProxyWidget::popup_basic() { - QScopedPointer<QComboBox> box(new QComboBox); + std::unique_ptr<QComboBox> boxGuard(new QComboBox); QStyleOptionComboBox opt; - opt.initFrom(box.data()); - opt.editable = box->isEditable(); - if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + opt.initFrom(boxGuard.get()); + opt.editable = boxGuard->isEditable(); + if (boxGuard->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) QSKIP("Does not work due to SH_Combobox_Popup"); // ProxyWidget should automatically create proxy's when the widget creates a child - QGraphicsScene *scene = new QGraphicsScene; - QGraphicsView view(scene); + QGraphicsScene scene; + QGraphicsView view(&scene); view.setAlignment(Qt::AlignLeft | Qt::AlignTop); view.setGeometry(0, 100, 480, 500); view.show(); - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QComboBox *box = boxGuard.get(); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; box->setGeometry(0, 0, 320, 40); box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" << "thursday" << "saturday" << "sunday"); - QCOMPARE(proxy->childItems().count(), 0); - proxy->setWidget(box.data()); + QCOMPARE(proxy->childItems().size(), 0); + proxy->setWidget(boxGuard.release()); proxy->show(); - scene->addItem(proxy); + scene.addItem(proxy); QCOMPARE(box->pos(), QPoint()); QCOMPARE(proxy->pos(), QPointF()); @@ -2530,16 +2314,16 @@ void tst_QGraphicsProxyWidget::popup_basic() QTest::qWait(125); QApplication::processEvents(); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); QTRY_COMPARE(box->pos(), QPoint()); - QCOMPARE(proxy->childItems().count(), 1); + QCOMPARE(proxy->childItems().size(), 1); QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0]; QVERIFY(child->isWidget()); QVERIFY(child->widget()); - QCOMPARE(child->widget()->parent(), static_cast<QObject*>(box.data())); + QCOMPARE(child->widget()->parent(), box); QTRY_COMPARE(proxy->pos(), QPointF(box->pos())); QCOMPARE(child->x(), qreal(box->x())); @@ -2610,12 +2394,12 @@ void tst_QGraphicsProxyWidget::changingCursor_basic() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; QLineEdit *widget = new QLineEdit; proxy->setWidget(widget); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready + QTRY_VERIFY(sceneChangedSpy.size() > 0); // make sure the scene is ready // in QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); @@ -2671,7 +2455,6 @@ void tst_QGraphicsProxyWidget::tooltip_basic() QGraphicsView view(&scene); view.setFixedSize(200, 200); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); { QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), @@ -2681,7 +2464,8 @@ void tst_QGraphicsProxyWidget::tooltip_basic() bool foundView = false; bool foundTipLabel = false; - foreach (QWidget *widget, QApplication::topLevelWidgets()) { + const auto widgets = QApplication::topLevelWidgets(); + for (QWidget *widget : widgets) { if (widget == &view) foundView = true; if (widget->inherits("QTipLabel")) @@ -2733,56 +2517,46 @@ void tst_QGraphicsProxyWidget::childPos() QGraphicsScene scene; - QComboBox *box = new QComboBox; - box->addItem("Item 1"); - box->addItem("Item 2"); - box->addItem("Item 3"); - box->addItem("Item 4"); + QComboBox box; + box.addItem("Item 1"); + box.addItem("Item 2"); + box.addItem("Item 3"); + box.addItem("Item 4"); if (moveCombo) - box->move(comboPos); + box.move(comboPos); - QGraphicsProxyWidget *proxy = scene.addWidget(box); + QGraphicsProxyWidget *proxy = scene.addWidget(&box); proxy->show(); QVERIFY(proxy->isVisible()); - QVERIFY(box->isVisible()); + QVERIFY(box.isVisible()); if (!moveCombo) proxy->setPos(proxyPos); QCOMPARE(proxy->pos(), proxyPos); - QCOMPARE(box->pos(), comboPos); + QCOMPARE(box.pos(), comboPos); for (int i = 0; i < 2; ++i) { - box->showPopup(); - QApplication::processEvents(); - QApplication::processEvents(); - - QWidget *menu = nullptr; - foreach (QObject *child, box->children()) { - if ((menu = qobject_cast<QWidget *>(child))) - break; - } + box.showPopup(); + QWidget *menu = box.findChild<QWidget *>(); QVERIFY(menu); - QVERIFY(menu->isVisible()); + QTRY_VERIFY(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<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child)))) - break; - } + QGraphicsProxyWidget *proxyChild = qobject_cast<QGraphicsProxyWidget *>( + static_cast<QGraphicsWidget *>(proxy->childItems().first())); QVERIFY(proxyChild); QVERIFY(proxyChild->isVisible()); qreal expectedXPosition = 0.0; -#if defined(Q_OS_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 (QApplication::style()->inherits("QMacStyle")) expectedXPosition = qreal(4 - 11); -#endif - QCOMPARE(proxyChild->pos().x(), expectedXPosition); + + QTRY_COMPARE(proxyChild->pos().x(), expectedXPosition); menu->hide(); } } @@ -2818,7 +2592,7 @@ void tst_QGraphicsProxyWidget::windowOpacity() QGraphicsProxyWidget *proxy = scene.addWidget(widget); proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); - QApplication::setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -2830,18 +2604,20 @@ void tst_QGraphicsProxyWidget::windowOpacity() QVERIFY(widget->isVisible()); widget->setWindowOpacity(0.5); - QApplication::processEvents(); // 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). + // X11 without XRender). On macOS, we always get a paint event. int paints = 0; +#ifdef Q_OS_MACOS + paints = 1; +#endif QTRY_COMPARE(eventSpy.counts[QEvent::UpdateRequest], 0); QTRY_COMPARE(eventSpy.counts[QEvent::Paint], paints); - QCOMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); const QList<QVariant> arguments = signalSpy.takeFirst(); const QList<QRectF> updateRects = qvariant_cast<QList<QRectF> >(arguments.at(0)); QCOMPARE(updateRects.size(), 1); @@ -2922,6 +2698,8 @@ void tst_QGraphicsProxyWidget::palettePropagation() QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3); QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1); QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + if (edit->palette() != palette) + QEXPECT_FAIL("", "Test case fails unless run alone", Abort); QCOMPARE(edit->palette(), palette); QCOMPARE(edit->palette(), proxy.palette()); QCOMPARE(edit->palette().color(QPalette::Text), QColor(Qt::red)); @@ -3112,12 +2890,12 @@ void tst_QGraphicsProxyWidget::createProxyForChildWidget() QSignalSpy spy(checkbox, SIGNAL(clicked())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); - QTRY_COMPARE(spy.count(), 0); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + QTRY_COMPARE(spy.size(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); @@ -3145,7 +2923,7 @@ public: bool embeddedPopup; bool gotContextMenuEvent; protected: - bool event(QEvent *event) + bool event(QEvent *event) override { if (event->type() == QEvent::ContextMenu) { if (!m_timer) { @@ -3157,7 +2935,7 @@ protected: } return QWidget::event(event); } - void contextMenuEvent(QContextMenuEvent *) + void contextMenuEvent(QContextMenuEvent *) override { gotContextMenuEvent = true; } @@ -3212,7 +2990,6 @@ void tst_QGraphicsProxyWidget::actionsContextMenu() view.resize(200, 200); view.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(100, 100)); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); view.setFocus(); QTRY_VERIFY(view.hasFocus()); @@ -3282,9 +3059,13 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data() void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QFETCH(bool, bypass); - QWidget *widget = new QWidget; + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); widget->resize(100, 100); QGraphicsScene scene; @@ -3292,7 +3073,7 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QGraphicsProxyWidget *proxy = scene.addWidget(widget); + QGraphicsProxyWidget *proxy = scene.addWidget(widgetGuard.release()); QCOMPARE(proxy->widget(), widget); QVERIFY(proxy->childItems().isEmpty()); @@ -3312,8 +3093,6 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() dialog->hide(); QApplication::processEvents(); - delete dialog; - delete widget; } static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos) @@ -3321,7 +3100,7 @@ static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view event->setScenePos(pos); event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos))); event->setButtons(Qt::LeftButton); - event->setModifiers(0); + event->setModifiers({}); event->setPossibleActions(Qt::CopyAction); event->setProposedAction(Qt::CopyAction); event->setDropAction(Qt::CopyAction); @@ -3483,7 +3262,7 @@ void tst_QGraphicsProxyWidget::updateAndDelete() class InputMethod_LineEdit : public QLineEdit { - bool event(QEvent *e) + bool event(QEvent *e) override { if (e->type() == QEvent::InputMethod) ++inputMethodEvents; @@ -3495,6 +3274,9 @@ public: void tst_QGraphicsProxyWidget::inputMethod() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QGraphicsScene scene; // check that the proxy is initialized with the correct input method sensitivity @@ -3554,7 +3336,6 @@ void tst_QGraphicsProxyWidget::clickFocus() view.setFrameStyle(0); view.resize(300, 300); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(!proxy->hasFocus()); @@ -3568,7 +3349,7 @@ void tst_QGraphicsProxyWidget::clickFocus() QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center()); // Spontaneous mouse click sets focus on a clickable widget. for (int retry = 0; retry < 50 && !proxy->hasFocus(); retry++) - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(proxy->hasFocus()); QVERIFY(proxy->widget()->hasFocus()); QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1); @@ -3626,7 +3407,7 @@ void tst_QGraphicsProxyWidget::clickFocus() // Spontaneous click on non-clickable widget does not give focus. proxy->widget()->setFocusPolicy(Qt::NoFocus); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(!proxy->hasFocus()); QVERIFY(!proxy->widget()->hasFocus()); @@ -3635,8 +3416,8 @@ void tst_QGraphicsProxyWidget::clickFocus() scene.setFocusItem(0); QVERIFY(!proxy->hasFocus()); QVERIFY(!proxy->widget()->hasFocus()); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(proxy->hasFocus()); QVERIFY(proxy->widget()->hasFocus()); QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1); @@ -3647,7 +3428,7 @@ void tst_QGraphicsProxyWidget::clickFocus() void tst_QGraphicsProxyWidget::windowFrameMargins() { // Make sure the top margin is non-zero when passing Qt::Window. - QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Window); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(nullptr, Qt::Window); qreal left, top, right, bottom; proxy->getWindowFrameMargins(&left, &top, &right, &bottom); @@ -3667,67 +3448,50 @@ void tst_QGraphicsProxyWidget::windowFrameMargins() QVERIFY(top > 0); } -class HoverButton : public QPushButton +void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() { -public: - HoverButton(QWidget *parent = nullptr) : QPushButton(parent), hoverLeaveReceived(false) - {} - - bool hoverLeaveReceived; - - bool event(QEvent* e) + struct HoverButton : public QPushButton { - if(QEvent::HoverLeave == e->type()) - hoverLeaveReceived = true; - return QPushButton::event(e); - } -}; + using QPushButton::QPushButton; + bool hoverLeaveReceived = false; -class Scene : public QGraphicsScene -{ -Q_OBJECT -public: - Scene() { - QWidget *background = new QWidget; - background->setGeometry(0, 0, 500, 500); - hoverButton = new HoverButton; - hoverButton->setParent(background); - hoverButton->setText("Second button"); - hoverButton->setGeometry(10, 10, 200, 50); - addWidget(background); - - QPushButton *hideButton = new QPushButton("I'm a button with a very very long text"); - hideButton->setGeometry(10, 10, 400, 50); - topButton = addWidget(hideButton); - connect(hideButton, &QPushButton::clicked, this, [&]() { topButton->hide(); }); - topButton->setFocus(); - } - - QGraphicsProxyWidget *topButton; - HoverButton *hoverButton; -}; + bool event(QEvent* e) override + { + if (QEvent::HoverLeave == e->type()) + hoverLeaveReceived = true; + return QPushButton::event(e); + } + }; -void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() -{ - if (QGuiApplication::platformName() == QLatin1String("cocoa")) { - // The "Second button" does not receive QEvent::HoverLeave - QSKIP("This test fails only on Cocoa. Investigate why. See QTBUG-69219"); - } + QGraphicsScene scene; + QWidget *background = new QWidget; + background->setGeometry(0, 0, 500, 500); + HoverButton *hoverButton = new HoverButton(background); + hoverButton->setText("Second button"); + hoverButton->setGeometry(10, 10, 200, 50); + scene.addWidget(background); + + QPushButton *hideButton = new QPushButton("I'm a button with a very very long text"); + hideButton->setGeometry(10, 10, 400, 50); + QGraphicsProxyWidget *topButton = scene.addWidget(hideButton); + connect(hideButton, &QPushButton::clicked, &scene, [&]() { topButton->hide(); }); + topButton->setFocus(); - QGraphicsView view; - Scene scene; - view.setScene(&scene); + QGraphicsView view(&scene); view.resize(600, 600); - QApplication::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QPoint topButtonTopLeftCorner = view.mapFromScene(scene.topButton->scenePos()); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, topButtonTopLeftCorner); + QPoint topButtonTopLeftCorner = view.mapFromScene(topButton->scenePos()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, topButtonTopLeftCorner); // move to the bottom right corner (buttons are placed in the top left corner) - QCOMPARE(scene.hoverButton->hoverLeaveReceived, false); + QCOMPARE(hoverButton->hoverLeaveReceived, false); QTest::mouseMove(view.viewport(), view.viewport()->rect().bottomRight()); - QTRY_COMPARE(scene.hoverButton->hoverLeaveReceived, true); + if (QGuiApplication::platformName() == QLatin1String("cocoa")) { + // The "Second button" does not receive QEvent::HoverLeave + QEXPECT_FAIL("", "This test fails only on Cocoa. Investigate why. See QTBUG-69219", Continue); + } + QTRY_COMPARE(hoverButton->hoverLeaveReceived, true); } static QByteArray msgPointMismatch(const QPoint &actual, const QPoint &expected) @@ -3758,7 +3522,6 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 childWidget->resize(embeddedWidget->size() / 2); childWidget->move(embeddedWidget->width() / 4, embeddedWidget->height() / 4); // center in embeddedWidget scene.addWidget(embeddedWidget); - QApplication::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); const QPoint embeddedCenter = embeddedWidget->rect().center(); @@ -3766,8 +3529,9 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 QCOMPARE(embeddedWidget->mapFromGlobal(embeddedCenterGlobal), embeddedCenter); // This should be equivalent to the view center give or take rounding // errors due to odd window margins + const int Tolerance = qCeil(4 * view.devicePixelRatio()); const QPoint viewCenter = view.geometry().center(); - QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 3, + QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= Tolerance, msgPointMismatch(embeddedCenterGlobal, viewCenter).constData()); // Same test with child centered on embeddedWidget. Also make sure @@ -3777,7 +3541,7 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 const QPoint childCenter = childWidget->rect().center(); const QPoint childCenterGlobal = childWidget->mapToGlobal(childCenter); QCOMPARE(childWidget->mapFromGlobal(childCenterGlobal), childCenter); - QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= 4, + QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= Tolerance, msgPointMismatch(childCenterGlobal, viewCenter).constData()); } @@ -3832,7 +3596,7 @@ class TouchWidget : public QWidget public: TouchWidget(QWidget *parent = nullptr) : QWidget(parent) {} - bool event(QEvent *event) + bool event(QEvent *event) override { switch (event->type()) { case QEvent::TouchBegin: @@ -3840,7 +3604,6 @@ public: case QEvent::TouchEnd: event->accept(); return true; - break; default: break; } @@ -3849,48 +3612,417 @@ public: } }; +#if QT_CONFIG(wheelevent) +/*! + QGraphicsProxyWidget receives wheel events from QGraphicsScene, and then + generates a new event that is sent spontaneously in order to enable event + propagation. This requires extra handling of the wheel grabbing we do for + high-precision wheel event streams. + + Test that this doesn't trigger infinite recursion, while still resulting in + event propagation within the embedded widget hierarchy, and back to the + QGraphicsView if the event is not accepted. + + See tst_QApplication::wheelEventPropagation for a similar test. +*/ +void tst_QGraphicsProxyWidget::wheelEventPropagation() +{ + QGraphicsScene scene(0, 0, 600, 600); + + QWidget *label = new QLabel("Direct"); + label->setFixedSize(300, 30); + QGraphicsProxyWidget *labelProxy = scene.addWidget(label); + labelProxy->setPos(0, 50); + labelProxy->show(); + + class NestedWidget : public QWidget + { + public: + NestedWidget(const QString &text) + { + setObjectName("Nested Label"); + QLabel *nested = new QLabel(text); + QHBoxLayout *hbox = new QHBoxLayout; + hbox->addWidget(nested); + setLayout(hbox); + } + + int wheelEventCount = 0; + protected: + void wheelEvent(QWheelEvent *) override + { + ++wheelEventCount; + } + }; + NestedWidget *nestedWidget = new NestedWidget("Nested"); + nestedWidget->setFixedSize(300, 60); + QGraphicsProxyWidget *nestedProxy = scene.addWidget(nestedWidget); + nestedProxy->setPos(0, 120); + nestedProxy->show(); + + QGraphicsView view(&scene); + view.setFixedHeight(200); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(view.verticalScrollBar()->isVisible()); + + view.verticalScrollBar()->setValue(0); + QSignalSpy scrollSpy(view.verticalScrollBar(), &QScrollBar::valueChanged); + + const QPoint wheelPosition(50, 25); + auto wheelUp = [&view, wheelPosition](Qt::ScrollPhase phase) { + const QPoint global = view.mapToGlobal(wheelPosition); + const QPoint pixelDelta(0, -25); + const QPoint angleDelta(0, -120); + QWindowSystemInterface::handleWheelEvent(view.windowHandle(), wheelPosition, global, + pixelDelta, angleDelta, Qt::NoModifier, + phase); + QCoreApplication::processEvents(); + }; + + int scrollCount = 0; + // test non-kinetic events; they are not grabbed, and should scroll the view unless + // accepted by the embedded widget + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // wheeling on the label, which ignores the event, should scroll the view + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // left the widget + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // reached the nested widget, which accepts the wheel event, so no more scrolling + QCOMPARE(view.itemAt(wheelPosition), nestedProxy); + // remember this position for later + const int scrollBarValueOnNestedProxy = view.verticalScrollBar()->value(); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), scrollCount); + QCOMPARE(nestedWidget->wheelEventCount, 1); + + // reset, try with kinetic events + view.verticalScrollBar()->setValue(0); + ++scrollCount; + + // starting a scroll outside any widget and scrolling through the widgets should work, + // no matter if the widget accepts wheel events - the view has the grab + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), ++scrollCount); + for (int i = 0; i < 5; ++i) { + wheelUp(Qt::ScrollUpdate); + QCOMPARE(scrollSpy.size(), ++scrollCount); + } + wheelUp(Qt::ScrollEnd); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // reset + view.verticalScrollBar()->setValue(0); + scrollCount = scrollSpy.size(); + + // starting a scroll on a widget that doesn't accept wheel events + // should also scroll the view, which still gets the grab + wheelUp(Qt::NoScrollPhase); + scrollCount = scrollSpy.size(); + + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), ++scrollCount); + for (int i = 0; i < 5; ++i) { + wheelUp(Qt::ScrollUpdate); + QCOMPARE(scrollSpy.size(), ++scrollCount); + } + wheelUp(Qt::ScrollEnd); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // starting a scroll on a widget that does accept wheel events + // should not scroll the view + view.verticalScrollBar()->setValue(scrollBarValueOnNestedProxy); + scrollCount = scrollSpy.size(); + + QCOMPARE(view.itemAt(wheelPosition), nestedProxy); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), scrollCount); +} +#endif // QT_CONFIG(wheelevent) + // QTBUG_45737 void tst_QGraphicsProxyWidget::forwardTouchEvent() { - QGraphicsScene *scene = new QGraphicsScene; + QGraphicsScene scene; TouchWidget *widget = new TouchWidget; - widget->setAttribute(Qt::WA_AcceptTouchEvents); - QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; - + QGraphicsProxyWidget *proxy = scene.addWidget(widget); proxy->setAcceptTouchEvents(true); - proxy->setWidget(widget); - - scene->addItem(proxy); - QGraphicsView *view = new QGraphicsView(scene); - - view->show(); + QGraphicsView view(&scene); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); EventSpy eventSpy(widget); QPointingDevice *device = QTest::createTouchDevice(); + QVERIFY(device); QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 0); QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 0); QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 0); - QTest::touchEvent(view, device).press(0, QPoint(10, 10), view); - QTest::touchEvent(view, device).move(0, QPoint(15, 15), view); - QTest::touchEvent(view, device).move(0, QPoint(16, 16), view); - QTest::touchEvent(view, device).release(0, QPoint(15, 15), view); + QTest::touchEvent(&view, device).press(0, QPoint(10, 10), &view); + QTest::touchEvent(&view, device).move(0, QPoint(15, 15), &view); + QTest::touchEvent(&view, device).move(0, QPoint(16, 16), &view); + QTest::touchEvent(&view, device).release(0, QPoint(15, 15), &view); QApplication::processEvents(); QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 1); QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 1); +} - delete view; - delete proxy; - delete scene; +void tst_QGraphicsProxyWidget::touchEventPropagation() +{ + QGraphicsScene scene(0, 0, 300, 200); + QWidget *simpleWidget = new QWidget; + simpleWidget->setObjectName("simpleWidget"); + simpleWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); + QGraphicsProxyWidget *simpleProxy = scene.addWidget(simpleWidget); + simpleProxy->setAcceptTouchEvents(true); + simpleProxy->setGeometry(QRectF(0, 0, 30, 30)); + + QWidget *formWidget = new QWidget; + formWidget->setObjectName("formWidget"); + formWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); + QPushButton *pushButton1 = new QPushButton("One"); + pushButton1->setObjectName("pushButton1"); + pushButton1->setAttribute(Qt::WA_AcceptTouchEvents, true); + QPushButton *pushButton2 = new QPushButton("Two"); + pushButton2->setObjectName("pushButton2"); + pushButton2->setAttribute(Qt::WA_AcceptTouchEvents, true); + TouchWidget *touchWidget1 = new TouchWidget; + touchWidget1->setObjectName("touchWidget1"); + touchWidget1->setAttribute(Qt::WA_AcceptTouchEvents, true); + touchWidget1->setFixedSize(pushButton1->sizeHint()); + TouchWidget *touchWidget2 = new TouchWidget; + touchWidget2->setObjectName("touchWidget2"); + touchWidget2->setAttribute(Qt::WA_AcceptTouchEvents, true); + touchWidget2->setFixedSize(pushButton2->sizeHint()); + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(pushButton1); + vbox->addWidget(pushButton2); + vbox->addWidget(touchWidget1); + vbox->addWidget(touchWidget2); + formWidget->setLayout(vbox); + QGraphicsProxyWidget *formProxy = scene.addWidget(formWidget); + formProxy->setAcceptTouchEvents(true); + formProxy->setGeometry(QRectF(50, 50, 200, 160)); + + QGraphicsView view(&scene); + view.setFixedSize(scene.width(), scene.height()); + view.verticalScrollBar()->setValue(0); + view.horizontalScrollBar()->setValue(0); + view.viewport()->setObjectName("GraphicsView's Viewport"); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + class TouchEventSpy : public QObject + { + public: + using QObject::QObject; + + struct TouchRecord { + QObject *receiver; + QEvent::Type eventType; + QPointF position; + }; + QHash<int, QList<TouchRecord>> records; + QWidget *mousePressReceiver = nullptr; + + int count(int id = 0) const { return records.value(id).size(); } + TouchRecord at(int i, int id = 0) const { return records.value(id).at(i); } + void clear() + { + records.clear(); + mousePressReceiver = nullptr; + } + protected: + bool eventFilter(QObject *receiver, QEvent *event) override + { + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchCancel: + case QEvent::TouchEnd: { + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event); + // instead of detaching each QEventPoint, just store the relative positions + for (const auto &touchPoint : touchEvent->points()) + records[touchPoint.id()] << TouchRecord{receiver, event->type(), touchPoint.position()}; + qCDebug(lcTests) << "Recording" << event << receiver; + break; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + mousePressReceiver = qobject_cast<QWidget*>(receiver); + break; + default: + break; + } + return QObject::eventFilter(receiver, event); + } + } eventSpy; + qApp->installEventFilter(&eventSpy); + + auto touchDevice = QTest::createTouchDevice(); + const QPointF simpleCenter = simpleProxy->geometry().center(); + + // On systems without double conversion we might get different rounding behavior. + // One pixel off in any direction is acceptable for this test. + constexpr auto closeEnough = [](QPointF exp, QPointF act) -> bool { + const QRectF expArea(exp - QPointF(1., 1.), exp + QPointF(1., 1.)); + const bool contains = expArea.contains(act); + if (!contains) + qWarning() << act << "not in" << exp; + return contains; + }; + + // verify that the embedded widget gets the correctly translated event + QTest::touchEvent(&view, touchDevice).press(0, simpleCenter.toPoint()); + // window, viewport, scene, simpleProxy, simpleWidget + QCOMPARE(eventSpy.count(), 5); + QCOMPARE(eventSpy.at(0).receiver, view.windowHandle()); + QCOMPARE(eventSpy.at(1).receiver, view.viewport()); + QCOMPARE(eventSpy.at(2).receiver, &scene); + QCOMPARE(eventSpy.at(3).receiver, simpleProxy); + auto record = eventSpy.at(4); + QCOMPARE(record.receiver, simpleWidget); + QCOMPARE(record.eventType, QEvent::TouchBegin); + QVERIFY(closeEnough(record.position, simpleCenter)); + eventSpy.clear(); + + // verify that the layout of formWidget is how we expect it to be + QCOMPARE(formWidget->childAt(QPoint(5, 5)), nullptr); + const QPoint pb1Center = pushButton1->rect().center(); + QCOMPARE(formWidget->childAt(pushButton1->pos() + pb1Center), pushButton1); + const QPoint pb2Center = pushButton2->rect().center(); + QCOMPARE(formWidget->childAt(pushButton2->pos() + pb2Center), pushButton2); + const QPoint tw1Center = touchWidget1->rect().center(); + QCOMPARE(formWidget->childAt(touchWidget1->pos() + tw1Center), touchWidget1); + const QPoint tw2Center = touchWidget2->rect().center(); + QCOMPARE(formWidget->childAt(touchWidget2->pos() + tw2Center), touchWidget2); + + // touch events are sent to the view, in view coordinates + const QPoint formProxyPox = view.mapFromScene(formProxy->pos().toPoint()); + const QPoint pb1TouchPos = pushButton1->pos() + pb1Center + formProxyPox; + const QPoint pb2TouchPos = pushButton2->pos() + pb2Center + formProxyPox; + const QPoint tw1TouchPos = touchWidget1->pos() + tw1Center + formProxyPox; + const QPoint tw2TouchPos = touchWidget2->pos() + tw2Center + formProxyPox; + + QSignalSpy clickedSpy(pushButton1, &QPushButton::clicked); + // Single touch point to nested widget not accepting event. + // Event should bubble up and translate correctly, TouchUpdate and TouchEnd events + // stop at the window since nobody accepted the TouchBegin. A mouse event will be generated. + QTest::touchEvent(&view, touchDevice).press(0, pb1TouchPos); + QTest::touchEvent(&view, touchDevice).move(0, pb1TouchPos + QPoint(1, 1)); + QTest::touchEvent(&view, touchDevice).release(0, pb1TouchPos + QPoint(1, 1)); + // ..., formProxy, pushButton1, formWidget, window, window + QCOMPARE(eventSpy.count(), 8); + QCOMPARE(eventSpy.at(3).receiver, formProxy); // formProxy dispatches to the right subwidget + record = eventSpy.at(4); + QCOMPARE(record.receiver, pushButton1); + QVERIFY(closeEnough(record.position, pb1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + // pushButton doesn't accept the point, so the TouchBegin propagates to parent + record = eventSpy.at(5); + QCOMPARE(record.receiver, formWidget); + QVERIFY(closeEnough(record.position, pushButton1->pos() + pb1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + record = eventSpy.at(6); + QCOMPARE(record.receiver, view.windowHandle()); + QCOMPARE(record.eventType, QEvent::TouchUpdate); + record = eventSpy.at(7); + QCOMPARE(record.receiver, view.windowHandle()); + QCOMPARE(record.eventType, QEvent::TouchEnd); + QCOMPARE(eventSpy.mousePressReceiver, pushButton1); + QCOMPARE(clickedSpy.size(), 1); + eventSpy.clear(); + clickedSpy.clear(); + + // Single touch point to nested widget accepting event. + QTest::touchEvent(&view, touchDevice).press(0, tw1TouchPos); + QTest::touchEvent(&view, touchDevice).move(0, tw1TouchPos + QPoint(5, 5)); + QTest::touchEvent(&view, touchDevice).release(0, tw1TouchPos + QPoint(5, 5)); + // Press: ..., formProxy, touchWidget1 (5) + // Move: window, touchWidget1 (2) + // Release: window, touchWidget1 (2) + QCOMPARE(eventSpy.count(), 9); + QCOMPARE(eventSpy.at(3).receiver, formProxy); // form proxy dispatches TouchBegin to the right widget + record = eventSpy.at(4); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + QCOMPARE(eventSpy.at(5).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchUpdate + record = eventSpy.at(6); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5))); + QCOMPARE(record.eventType, QEvent::TouchUpdate); + QCOMPARE(eventSpy.at(7).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchEnd + record = eventSpy.at(8); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5))); + QCOMPARE(record.eventType, QEvent::TouchEnd); + eventSpy.clear(); + + // to simplify the remaining test, install the event spy explicitly on the target widgets + qApp->removeEventFilter(&eventSpy); + formWidget->installEventFilter(&eventSpy); + pushButton1->installEventFilter(&eventSpy); + pushButton2->installEventFilter(&eventSpy); + touchWidget1->installEventFilter(&eventSpy); + touchWidget2->installEventFilter(&eventSpy); + + // multi-touch to different widgets, some do and some don't accept the event + QTest::touchEvent(&view, touchDevice) + .press(0, pb1TouchPos) + .press(1, tw1TouchPos) + .press(2, pb2TouchPos) + .press(3, tw2TouchPos); + QTest::touchEvent(&view, touchDevice) + .move(0, pb1TouchPos + QPoint(1, 1)) + .move(1, tw1TouchPos + QPoint(1, 1)) + .move(2, pb2TouchPos + QPoint(1, 1)) + .move(3, tw2TouchPos + QPoint(1, 1)); + QTest::touchEvent(&view, touchDevice) + .release(0, pb1TouchPos + QPoint(1, 1)) + .release(1, tw1TouchPos + QPoint(1, 1)) + .release(2, pb2TouchPos + QPoint(1, 1)) + .release(3, tw2TouchPos + QPoint(1, 1)); + + QCOMPARE(eventSpy.count(0), 2); // Begin never accepted, so move up and then stop + QCOMPARE(eventSpy.count(1), 3); // Begin accepted, so not propagated and update/end received + QCOMPARE(eventSpy.count(2), 2); // Begin never accepted + QCOMPARE(eventSpy.count(3), 3); // Begin accepted + QCOMPARE(eventSpy.at(0, 0).receiver, pushButton1); + QCOMPARE(eventSpy.at(1, 0).receiver, formWidget); + QCOMPARE(eventSpy.at(0, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(1, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(2, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(0, 2).receiver, pushButton2); + QCOMPARE(eventSpy.at(1, 2).receiver, formWidget); + QCOMPARE(eventSpy.at(0, 3).receiver, touchWidget2); + QCOMPARE(eventSpy.at(1, 3).receiver, touchWidget2); + QCOMPARE(eventSpy.at(2, 3).receiver, touchWidget2); + QCOMPARE(clickedSpy.size(), 0); // multi-touch event does not synthesize a mouse event } QTEST_MAIN(tst_QGraphicsProxyWidget) |