diff options
Diffstat (limited to 'tests/auto/quick/qquickitem2/tst_qquickitem.cpp')
-rw-r--r-- | tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 1315 |
1 files changed, 1079 insertions, 236 deletions
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 3170fb5918..267be73ec9 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -1,30 +1,6 @@ -/**************************************************************************** -** -** 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.h> #include <QtTest/QSignalSpy> #include <QtQml/qqmlengine.h> @@ -33,16 +9,29 @@ #include <QtQuick/qquickitemgrabresult.h> #include <QtQuick/qquickview.h> #include <QtGui/private/qinputmethod_p.h> +#include <QtQuick/private/qquickloader_p.h> +#include <QtQuick/private/qquickpalette_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquicktextinput_p.h> #include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuick/private/qquickanchors_p.h> #include <QtGui/qstylehints.h> #include <private/qquickitem_p.h> -#include "../../shared/util.h" -#include "../shared/visualtestutil.h" -#include "../../shared/platforminputcontext.h" +#include <QtQuickTest/QtQuickTest> +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/visualtestutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QtQuickTestUtils/private/platforminputcontext_p.h> +#include <QtTest/private/qpropertytesthelper_p.h> +#ifdef QT_WIDGETS_LIB +#include <QtWidgets/qwidget.h> +#include <QtWidgets/qboxlayout.h> +#include <QtWidgets/qlineedit.h> +#endif + +using namespace QQuickVisualTestUtils; -using namespace QQuickVisualTestUtil; +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") class tst_QQuickItem : public QQmlDataTest { @@ -66,6 +55,7 @@ private slots: void activeFocusOnTab10(); void activeFocusOnTab_infiniteLoop_data(); void activeFocusOnTab_infiniteLoop(); + void activeFocusOnTab_infiniteLoopControls(); void nextItemInFocusChain(); void nextItemInFocusChain2(); @@ -75,6 +65,7 @@ private slots: void qtbug_50516(); void qtbug_50516_2_data(); void qtbug_50516_2(); + void focusableItemReparentedToLoadedComponent(); void keys(); #if QT_CONFIG(shortcut) @@ -103,6 +94,7 @@ private slots: void mapCoordinates_data(); void mapCoordinatesRect(); void mapCoordinatesRect_data(); + void mapCoordinatesWithWindows(); void propertyChanges(); void nonexistentPropertyConnection(); void transforms(); @@ -115,6 +107,8 @@ private slots: void childrenProperty(); void resourcesProperty(); + void bindableProperties_data(); + void bindableProperties(); void changeListener(); void transformCrash(); @@ -129,6 +123,26 @@ private slots: void grab(); + void colorGroup(); + void paletteAllocated(); + + void undefinedIsInvalidForWidthAndHeight(); + + void viewport_data(); + void viewport(); + + void qobject_castOnDestruction(); + void signalsOnDestruction_data(); + void signalsOnDestruction(); + void visibleChanged(); + + void lastFocusChangeReason(); + void focusInScopeChanges(); + +#ifdef QT_WIDGETS_LIB + void embeddedInWidgetsFocus(); +#endif + private: QQmlEngine engine; bool qt_tab_all_widgets() { @@ -202,7 +216,7 @@ public: KeyTestItem(QQuickItem *parent=nullptr) : QQuickItem(parent), mKey(0) {} protected: - void keyPressEvent(QKeyEvent *e) { + void keyPressEvent(QKeyEvent *e) override { mKey = e->key(); if (e->key() == Qt::Key_A) @@ -211,7 +225,7 @@ protected: e->ignore(); } - void keyReleaseEvent(QKeyEvent *e) { + void keyReleaseEvent(QKeyEvent *e) override { if (e->key() == Qt::Key_B) e->accept(); else @@ -225,7 +239,7 @@ public: class FocusEventFilter : public QObject { protected: - bool eventFilter(QObject *watched, QEvent *event) { + bool eventFilter(QObject *watched, QEvent *event) override { if ((event->type() == QEvent::FocusIn) || (event->type() == QEvent::FocusOut)) { QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event); lastFocusReason = focusEvent->reason(); @@ -242,8 +256,8 @@ QML_DECLARE_TYPE(KeyTestItem); class HollowTestItem : public QQuickItem { Q_OBJECT - Q_PROPERTY(bool circle READ isCircle WRITE setCircle) - Q_PROPERTY(qreal holeRadius READ holeRadius WRITE setHoleRadius) + Q_PROPERTY(bool circle READ isCircle WRITE setCircle NOTIFY circleChanged) + Q_PROPERTY(qreal holeRadius READ holeRadius WRITE setHoleRadius NOTIFY holeRadiusChanged) public: HollowTestItem(QQuickItem *parent = nullptr) @@ -261,12 +275,12 @@ public: bool isHovered() const { return m_isHovered; } bool isCircle() const { return m_isCircle; } - void setCircle(bool circle) { m_isCircle = circle; } + void setCircle(bool circle) { m_isCircle = circle; emit circleChanged(); } qreal holeRadius() const { return m_holeRadius; } - void setHoleRadius(qreal radius) { m_holeRadius = radius; } + void setHoleRadius(qreal radius) { m_holeRadius = radius; emit holeRadiusChanged(); } - bool contains(const QPointF &point) const { + bool contains(const QPointF &point) const override { const qreal w = width(); const qreal h = height(); const qreal r = m_holeRadius; @@ -287,11 +301,15 @@ public: return dd > (r * r) && dd <= outerRadius * outerRadius; } +signals: + void circleChanged(); + void holeRadiusChanged(); + protected: - void hoverEnterEvent(QHoverEvent *) { m_isHovered = true; } - void hoverLeaveEvent(QHoverEvent *) { m_isHovered = false; } - void mousePressEvent(QMouseEvent *) { m_isPressed = true; } - void mouseReleaseEvent(QMouseEvent *) { m_isPressed = false; } + void hoverEnterEvent(QHoverEvent *) override { m_isHovered = true; } + void hoverLeaveEvent(QHoverEvent *) override { m_isHovered = false; } + void mousePressEvent(QMouseEvent *) override { m_isPressed = true; } + void mouseReleaseEvent(QMouseEvent *) override { m_isPressed = false; } private: bool m_isPressed; @@ -302,6 +320,22 @@ private: QML_DECLARE_TYPE(HollowTestItem); +class ViewportTestItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QRectF viewport READ viewport NOTIFY viewportChanged) + +public: + ViewportTestItem(QQuickItem *parent = nullptr) : QQuickItem(parent) { } + + QRectF viewport() const { return clipRect(); } + +signals: + void viewportChanged(); +}; + +QML_DECLARE_TYPE(ViewportTestItem); + class TabFenceItem : public QQuickItem { Q_OBJECT @@ -334,6 +368,7 @@ public: QML_DECLARE_TYPE(TabFenceItem2); tst_QQuickItem::tst_QQuickItem() + : QQmlDataTest(QT_QMLTEST_DATADIR) { } @@ -342,6 +377,7 @@ void tst_QQuickItem::initTestCase() QQmlDataTest::initTestCase(); qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem"); qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem"); + qmlRegisterType<ViewportTestItem>("Test", 1, 0, "ViewportTestItem"); qmlRegisterType<TabFenceItem>("Test", 1, 0, "TabFence"); qmlRegisterType<TabFenceItem2>("Test", 1, 0, "TabFence2"); } @@ -1143,6 +1179,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop() QCOMPARE(item, window->rootObject()); } + +void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls() +{ + auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml"); + QScopedPointer<QQuickView>window(new QQuickView()); + window->setSource(source); + window->show(); + QVERIFY(window->errors().isEmpty()); + QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang +} + void tst_QQuickItem::nextItemInFocusChain() { if (!qt_tab_all_widgets()) @@ -1284,14 +1331,13 @@ void verifyTabFocusChain(QQuickView *window, const char **focusChain, bool forwa int idx = 0; for (const char **objectName = focusChain; *objectName; ++objectName, ++idx) { const QString &descrStr = QString("idx=%1 objectName=\"%2\"").arg(idx).arg(*objectName); - const char *descr = descrStr.toLocal8Bit().data(); QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, forward ? Qt::NoModifier : Qt::ShiftModifier); QGuiApplication::sendEvent(window, &key); - QVERIFY2(key.isAccepted(), descr); + QVERIFY2(key.isAccepted(), qPrintable(descrStr)); QQuickItem *item = findItem<QQuickItem>(window->rootObject(), *objectName); - QVERIFY2(item, descr); - QVERIFY2(item->hasActiveFocus(), descr); + QVERIFY2(item, qPrintable(descrStr)); + QVERIFY2(item->hasActiveFocus(), qPrintable(descrStr)); } } @@ -1400,6 +1446,35 @@ void tst_QQuickItem::qtbug_50516_2() delete window; } +void tst_QQuickItem::focusableItemReparentedToLoadedComponent() // QTBUG-89736 +{ + QQuickView window; + window.setSource(testFileUrl("focusableItemReparentedToLoadedComponent.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QGuiApplication::focusWindow(), &window); + QQuickLoader *loader = window.rootObject()->findChild<QQuickLoader *>(); + QVERIFY(loader); + QTRY_VERIFY(loader->status() == QQuickLoader::Ready); + QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput *>(); + QVERIFY(textInput); + + // click to focus + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10}); + QTRY_VERIFY(textInput->hasActiveFocus()); + + // unload and reload + auto component = loader->sourceComponent(); + loader->resetSourceComponent(); + QTRY_VERIFY(loader->status() == QQuickLoader::Null); + loader->setSourceComponent(component); + QTRY_VERIFY(loader->status() == QQuickLoader::Ready); + + // click to focus again + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10}); + QTRY_VERIFY(textInput->hasActiveFocus()); +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(nullptr); @@ -1813,90 +1888,90 @@ void tst_QQuickItem::layoutMirroring() QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(rootItem); QVERIFY(rootPrivate); - QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true); - - QCOMPARE(anchorsMirrored(rootItem, "mirrored1"), true); - QCOMPARE(anchorsMirrored(rootItem, "mirrored2"), true); - QCOMPARE(anchorsMirrored(rootItem, "notMirrored1"), false); - QCOMPARE(anchorsMirrored(rootItem, "notMirrored2"), false); - QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror1"), true); - QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror2"), true); - - QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true); - - QCOMPARE(childPrivate(rootItem, "mirrored1")->isMirrorImplicit, false); - QCOMPARE(childPrivate(rootItem, "mirrored2")->isMirrorImplicit, false); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->isMirrorImplicit, false); - QCOMPARE(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit, true); - - QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent, true); - QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent, false); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent, true); - QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent, true); - - QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem, true); - QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem, false); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem, false); - QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem, false); + QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror); + + QVERIFY(anchorsMirrored(rootItem, "mirrored1")); + QVERIFY(anchorsMirrored(rootItem, "mirrored2")); + QVERIFY(!anchorsMirrored(rootItem, "notMirrored1")); + QVERIFY(!anchorsMirrored(rootItem, "notMirrored2")); + QVERIFY(anchorsMirrored(rootItem, "inheritedMirror1")); + QVERIFY(anchorsMirrored(rootItem, "inheritedMirror2")); + + QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror); + QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror); + + QVERIFY(!childPrivate(rootItem, "mirrored1")->isMirrorImplicit); + QVERIFY(!childPrivate(rootItem, "mirrored2")->isMirrorImplicit); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->isMirrorImplicit); + QVERIFY(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit); + + QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent); + QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent); + QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent); + QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent); + + QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem); // load dynamic content using Loader that needs to inherit mirroring rootItem->setProperty("state", "newContent"); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror, true); + QVERIFY(!childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror, true); + QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->isMirrorImplicit, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit, true); + QVERIFY(!childPrivate(rootItem, "notMirrored3")->isMirrorImplicit); + QVERIFY(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent, true); + QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent); + QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false); - QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false); + QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem); + QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem); // disable inheritance rootItem->setProperty("childrenInherit", false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); + QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror); + QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, false); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, false); + QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror); + QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror); + QVERIFY(!childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror); // re-enable inheritance rootItem->setProperty("childrenInherit", true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror); + QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror); + QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror); - QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true); - QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true); + QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror); + QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror); // // dynamic parenting @@ -1909,24 +1984,24 @@ void tst_QQuickItem::layoutMirroring() // inherit in constructor QQuickItem *childItem1 = new QQuickItem(parentItem1); - QCOMPARE(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror, true); - QCOMPARE(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent, true); + QVERIFY(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror); + QVERIFY(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent); // inherit through a parent change QQuickItem *childItem2 = new QQuickItem(); - QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false); - QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false); + QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror); + QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent); childItem2->setParentItem(parentItem1); - QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, true); - QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, true); + QVERIFY(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror); + QVERIFY(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent); // stop inherting through a parent change QQuickItem *parentItem2 = new QQuickItem(); QQuickItemPrivate::get(parentItem2)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true QQuickItemPrivate::get(parentItem2)->resolveLayoutMirror(); childItem2->setParentItem(parentItem2); - QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false); - QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false); + QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror); + QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent); delete parentItem1; delete parentItem2; @@ -1942,11 +2017,11 @@ void tst_QQuickItem::layoutMirroringWindow() window->show(); QQuickItemPrivate *content = QQuickItemPrivate::get(window->contentItem()); - QCOMPARE(content->effectiveLayoutMirror, true); - QCOMPARE(content->inheritedLayoutMirror, true); - QCOMPARE(content->isMirrorImplicit, false); - QCOMPARE(content->inheritMirrorFromParent, true); - QCOMPARE(content->inheritMirrorFromItem, true); + QVERIFY(content->effectiveLayoutMirror); + QVERIFY(content->inheritedLayoutMirror); + QVERIFY(!content->isMirrorImplicit); + QVERIFY(content->inheritMirrorFromParent); + QVERIFY(content->inheritMirrorFromItem); } void tst_QQuickItem::layoutMirroringIllegalParent() @@ -2491,19 +2566,19 @@ void tst_QQuickItem::smooth() item->setSmooth(true); QVERIFY(item->smooth()); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); QList<QVariant> arguments = spy.first(); - QCOMPARE(arguments.count(), 1); + QCOMPARE(arguments.size(), 1); QVERIFY(arguments.at(0).toBool()); item->setSmooth(true); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); item->setSmooth(false); QVERIFY(!item->smooth()); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); item->setSmooth(false); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); delete item; } @@ -2520,19 +2595,19 @@ void tst_QQuickItem::antialiasing() item->setAntialiasing(true); QVERIFY(item->antialiasing()); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); QList<QVariant> arguments = spy.first(); - QCOMPARE(arguments.count(), 1); + QCOMPARE(arguments.size(), 1); QVERIFY(arguments.at(0).toBool()); item->setAntialiasing(true); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); item->setAntialiasing(false); QVERIFY(!item->antialiasing()); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); item->setAntialiasing(false); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); delete item; } @@ -2551,18 +2626,18 @@ void tst_QQuickItem::clip() QVERIFY(item->clip()); QList<QVariant> arguments = spy.first(); - QCOMPARE(arguments.count(), 1); + QCOMPARE(arguments.size(), 1); QVERIFY(arguments.at(0).toBool()); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); item->setClip(true); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); item->setClip(false); QVERIFY(!item->clip()); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); item->setClip(false); - QCOMPARE(spy.count(),2); + QCOMPARE(spy.size(),2); delete item; } @@ -2637,15 +2712,17 @@ void tst_QQuickItem::mapCoordinates() Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y))); QCOMPARE(result.value<QPointF>(), -QPointF(150,150) + QPointF(x, y)); - QString warning1 = testFileUrl("mapCoordinates.qml").toString() + ":35:5: QML Item: mapToItem() given argument \"1122\" which is neither null nor an Item"; - QString warning2 = testFileUrl("mapCoordinates.qml").toString() + ":35:5: QML Item: mapFromItem() given argument \"1122\" which is neither null nor an Item"; + QRegularExpression warning1 = QRegularExpression(".*Could not convert argument 0 at.*"); + QRegularExpression warning2 = QRegularExpression(".*checkMapA.*Invalid@.*"); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, warning1); + QTest::ignoreMessage(QtWarningMsg, warning2); QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y))); QVERIFY(result.toBool()); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + QTest::ignoreMessage(QtWarningMsg, warning1); + QTest::ignoreMessage(QtWarningMsg, warning2); QVERIFY(QMetaObject::invokeMethod(root, "checkMapAFromInvalid", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y))); QVERIFY(result.toBool()); @@ -2669,7 +2746,7 @@ void tst_QQuickItem::mapCoordinatesRect() QFETCH(int, width); QFETCH(int, height); - QQuickView *window = new QQuickView(nullptr); + std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>(); window->setBaseSize(QSize(300, 300)); window->setSource(testFileUrl("mapCoordinatesRect.qml")); window->show(); @@ -2708,20 +2785,20 @@ void tst_QQuickItem::mapCoordinatesRect() Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height))); QCOMPARE(result.value<QRectF>(), qobject_cast<QQuickItem*>(a)->mapRectFromScene(QRectF(x, y, width, height))); - QString warning1 = testFileUrl("mapCoordinatesRect.qml").toString() + ":35:5: QML Item: mapToItem() given argument \"1122\" which is neither null nor an Item"; - QString warning2 = testFileUrl("mapCoordinatesRect.qml").toString() + ":35:5: QML Item: mapFromItem() given argument \"1122\" which is neither null nor an Item"; + QRegularExpression warning1 = QRegularExpression(".*Could not convert argument 0 at.*"); + QRegularExpression warning2 = QRegularExpression(".*checkMapA.*Invalid@.*"); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, warning1); + QTest::ignoreMessage(QtWarningMsg, warning2); QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height))); QVERIFY(result.toBool()); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + QTest::ignoreMessage(QtWarningMsg, warning1); + QTest::ignoreMessage(QtWarningMsg, warning2); QVERIFY(QMetaObject::invokeMethod(root, "checkMapAFromInvalid", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height))); QVERIFY(result.toBool()); - - delete window; } void tst_QQuickItem::mapCoordinatesRect_data() @@ -2735,6 +2812,76 @@ void tst_QQuickItem::mapCoordinatesRect_data() QTest::newRow(QTest::toString(i)) << i << i << i << i; } +void tst_QQuickItem::mapCoordinatesWithWindows() +{ + QQmlComponent component(&engine, testFileUrl("mapCoordinatesWithWindows.qml")); + std::unique_ptr<QObject> root(component.create()); + QVERIFY(root); + + auto *windowA = root->property("windowA").value<QQuickWindow*>(); + QVERIFY(windowA); + + // The window container geometry, parenting, etc, is applied + // during polish, so to test these we need to wait for one. + QVERIFY(QQuickTest::qWaitForPolish(windowA)); + + auto *childItem = windowA->findChild<QQuickItem*>("childItem"); + QVERIFY(childItem); + + QPoint itemPos = childItem->position().toPoint(); + QCOMPARE(childItem->mapToScene({0, 0}), itemPos); + QCOMPARE(childItem->mapToGlobal({0, 0}), windowA->position() + itemPos); + + auto *childItemInChildWindow = windowA->findChild<QQuickItem*>("childItemInChildWindow"); + QVERIFY(childItemInChildWindow); + + QPoint windowItemPos = childItemInChildWindow->position().toPoint(); + QCOMPARE(childItemInChildWindow->mapToScene({0, 0}), windowItemPos); + QCOMPARE(childItemInChildWindow->mapToGlobal({0, 0}), windowA->position() + + childItemInChildWindow->window()->position() + windowItemPos); + + QCOMPARE(childItemInChildWindow->mapToItem(nullptr, {0, 0}), windowItemPos); + + auto globalItemOffset = [](QQuickItem *a, QQuickItem *b) { + return a->mapToGlobal({0, 0}) - b->mapToGlobal({0, 0}); + }; + + QCOMPARE(childItemInChildWindow->mapToItem(childItem, {0, 0}), + globalItemOffset(childItemInChildWindow, childItem)); + QCOMPARE(childItemInChildWindow->mapFromItem(childItem, {0, 0}), + globalItemOffset(childItem, childItemInChildWindow)); + + QCOMPARE(childItem->mapToItem(childItemInChildWindow, {0, 0}), + globalItemOffset(childItem, childItemInChildWindow)); + QCOMPARE(childItem->mapFromItem(childItemInChildWindow, {0, 0}), + globalItemOffset(childItemInChildWindow, childItem)); + + auto *windowB = root->property("windowB").value<QQuickWindow*>(); + QVERIFY(windowA); + auto *childItemInOtherWindow = windowB->findChild<QQuickItem*>("childItem"); + QVERIFY(childItemInOtherWindow); + + QCOMPARE(childItemInOtherWindow->mapToItem(childItem, {0, 0}), + globalItemOffset(childItemInOtherWindow, childItem)); + QCOMPARE(childItemInOtherWindow->mapFromItem(childItem, {0, 0}), + globalItemOffset(childItem, childItemInOtherWindow)); + + QCOMPARE(childItem->mapToItem(childItemInOtherWindow, {0, 0}), + globalItemOffset(childItem, childItemInOtherWindow)); + QCOMPARE(childItem->mapFromItem(childItemInOtherWindow, {0, 0}), + globalItemOffset(childItemInOtherWindow, childItem)); + + QCOMPARE(childItemInOtherWindow->mapToItem(childItemInChildWindow, {0, 0}), + globalItemOffset(childItemInOtherWindow, childItemInChildWindow)); + QCOMPARE(childItemInOtherWindow->mapFromItem(childItemInChildWindow, {0, 0}), + globalItemOffset(childItemInChildWindow, childItemInOtherWindow)); + + QCOMPARE(childItemInChildWindow->mapToItem(childItemInOtherWindow, {0, 0}), + globalItemOffset(childItemInChildWindow, childItemInOtherWindow)); + QCOMPARE(childItemInChildWindow->mapFromItem(childItemInOtherWindow, {0, 0}), + globalItemOffset(childItemInOtherWindow, childItemInChildWindow)); +} + void tst_QQuickItem::transforms_data() { QTest::addColumn<QByteArray>("qml"); @@ -2812,6 +2959,30 @@ void tst_QQuickItem::resourcesProperty() delete object; } +void tst_QQuickItem::bindableProperties_data() +{ + QTest::addColumn<qreal>("initialValue"); + QTest::addColumn<qreal>("newValue"); + QTest::addColumn<QString>("property"); + + // can't simply use 3. or 3.0 for the numbers as qreal might + // be float instead of double... + QTest::addRow("x") << qreal(3) << qreal(14) << "x"; + QTest::addRow("y") << qreal(10) << qreal(20) << "y"; + QTest::addRow("width") << qreal(100) << qreal(200) << "width"; + QTest::addRow("height") << qreal(50) << qreal(40) << "height"; +} + +void tst_QQuickItem::bindableProperties() +{ + QQuickItem item; + QFETCH(qreal, initialValue); + QFETCH(qreal, newValue); + QFETCH(QString, property); + + QTestPrivate::testReadWritePropertyBasics(item, initialValue, newValue, property.toUtf8().constData()); +} + void tst_QQuickItem::propertyChanges() { QQuickView *window = new QQuickView(nullptr); @@ -2846,50 +3017,50 @@ void tst_QQuickItem::propertyChanges() item->setBaselineOffset(10.0); QCOMPARE(item->parentItem(), parentItem); - QCOMPARE(parentSpy.count(),1); + QCOMPARE(parentSpy.size(),1); QList<QVariant> parentArguments = parentSpy.first(); - QCOMPARE(parentArguments.count(), 1); + QCOMPARE(parentArguments.size(), 1); QCOMPARE(item->parentItem(), qvariant_cast<QQuickItem *>(parentArguments.at(0))); - QCOMPARE(childrenChangedSpy.count(),1); + QCOMPARE(childrenChangedSpy.size(),1); item->setParentItem(parentItem); - QCOMPARE(childrenChangedSpy.count(),1); + QCOMPARE(childrenChangedSpy.size(),1); QCOMPARE(item->width(), 100.0); - QCOMPARE(widthSpy.count(),1); + QCOMPARE(widthSpy.size(),1); QCOMPARE(item->height(), 200.0); - QCOMPARE(heightSpy.count(),1); + QCOMPARE(heightSpy.size(),1); QCOMPARE(item->baselineOffset(), 10.0); - QCOMPARE(baselineOffsetSpy.count(),1); + QCOMPARE(baselineOffsetSpy.size(),1); QList<QVariant> baselineOffsetArguments = baselineOffsetSpy.first(); - QCOMPARE(baselineOffsetArguments.count(), 1); + QCOMPARE(baselineOffsetArguments.size(), 1); QCOMPARE(item->baselineOffset(), baselineOffsetArguments.at(0).toReal()); QCOMPARE(parentItem->childrenRect(), QRectF(0.0,0.0,100.0,200.0)); - QCOMPARE(childrenRectSpy.count(),1); + QCOMPARE(childrenRectSpy.size(),1); QList<QVariant> childrenRectArguments = childrenRectSpy.at(0); - QCOMPARE(childrenRectArguments.count(), 1); + QCOMPARE(childrenRectArguments.size(), 1); QCOMPARE(parentItem->childrenRect(), childrenRectArguments.at(0).toRectF()); QCOMPARE(item->hasActiveFocus(), true); - QCOMPARE(focusSpy.count(),1); + QCOMPARE(focusSpy.size(),1); QList<QVariant> focusArguments = focusSpy.first(); - QCOMPARE(focusArguments.count(), 1); + QCOMPARE(focusArguments.size(), 1); QCOMPARE(focusArguments.at(0).toBool(), true); QCOMPARE(parentItem->hasActiveFocus(), false); QCOMPARE(parentItem->hasFocus(), false); - QCOMPARE(wantsFocusSpy.count(),0); + QCOMPARE(wantsFocusSpy.size(),0); item->setX(10.0); QCOMPARE(item->x(), 10.0); - QCOMPARE(xSpy.count(), 1); + QCOMPARE(xSpy.size(), 1); item->setY(10.0); QCOMPARE(item->y(), 10.0); - QCOMPARE(ySpy.count(), 1); + QCOMPARE(ySpy.size(), 1); delete window; } @@ -2936,7 +3107,7 @@ void tst_QQuickItem::childrenRectBug() { QQuickView *window = new QQuickView(nullptr); - QString warning = testFileUrl("childrenRectBug.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\""; + QString warning = testFileUrl("childrenRectBug.qml").toString() + ":11:9: QML Item: Binding loop detected for property \"height\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); @@ -2957,11 +3128,11 @@ void tst_QQuickItem::childrenRectBug2() { QQuickView *window = new QQuickView(nullptr); - QString warning1 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"width\""; + QString warning1 = testFileUrl("childrenRectBug2.qml").toString() + ":10:9: QML Item: Binding loop detected for property \"width\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); - QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\""; + QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":11:9: QML Item: Binding loop detected for property \"height\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); @@ -3174,7 +3345,7 @@ void tst_QQuickItem::changeListener() QCOMPARE(child2Listener.count(QQuickItemPrivate::Destroyed), 1); QQuickItemPrivate::get(parent)->removeItemChangeListener(&parentListener, QQuickItemPrivate::Children); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // QTBUG-54732: all listeners should get invoked even if they remove themselves while iterating the listeners QList<TestListener *> listeners; @@ -3182,93 +3353,93 @@ void tst_QQuickItem::changeListener() listeners << new TestListener(true); // itemVisibilityChanged x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setVisible(false); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemRotationChanged x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setRotation(90); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemOpacityChanged x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setOpacity(0.5); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemChildAdded() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); child1 = new QQuickItem(parent); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Children), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemParentChanged() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(child1)->addItemChangeListener(listener, QQuickItemPrivate::Parent); - QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), listeners.size()); child1->setParentItem(nullptr); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1); - QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), 0); // itemImplicitWidthChanged() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setImplicitWidth(parent->implicitWidth() + 1); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemImplicitHeightChanged() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setImplicitHeight(parent->implicitHeight() + 1); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemGeometryChanged() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); parent->setWidth(parent->width() + 1); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemChildRemoved() x 5 child1->setParentItem(parent); - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); delete child1; - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Children), 2); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0); // itemDestroyed() x 5 - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Destroyed); - QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count()); + QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size()); delete parent; - foreach (TestListener *listener, listeners) + for (TestListener *listener : std::as_const(listeners)) QCOMPARE(listener->count(QQuickItemPrivate::Destroyed), 1); } @@ -3512,39 +3683,35 @@ void tst_QQuickItem::contains() QFETCH(bool, insideTarget); QFETCH(QList<QPoint>, points); - QQuickView *window = new QQuickView(nullptr); - window->rootContext()->setContextProperty("circleShapeTest", circleTest); - window->setBaseSize(QSize(400, 400)); - window->setSource(testFileUrl("hollowTestItem.qml")); - window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(QGuiApplication::focusWindow(), window); - - QQuickItem *root = qobject_cast<QQuickItem *>(window->rootObject()); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hollowTestItem.qml"))); + QQuickItem *root = qobject_cast<QQuickItem *>(window.rootObject()); QVERIFY(root); + // Ensure that we don't get extra hover events delivered on the side. + QQuickWindowPrivate::get(&window)->deliveryAgentPrivate()->frameSynchronousHoverEnabled = false; + // Flush out any mouse events that might be queued up + qGuiApp->processEvents(); + HollowTestItem *hollowItem = root->findChild<HollowTestItem *>("hollowItem"); QVERIFY(hollowItem); + hollowItem->setCircle(circleTest); + + for (const QPoint &point : points) { + qCDebug(lcTests) << "hover and click @" << point; - foreach (const QPoint &point, points) { // check mouse hover - QTest::mouseMove(window, point); - QTest::qWait(10); - QCOMPARE(hollowItem->isHovered(), insideTarget); + QTest::mouseMove(&window, point); + QTRY_COMPARE(hollowItem->isHovered(), insideTarget); // check mouse press - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, point); - QTest::qWait(10); - QCOMPARE(hollowItem->isPressed(), insideTarget); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point); + QTRY_COMPARE(hollowItem->isPressed(), insideTarget); // check mouse release - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, point); - QTest::qWait(10); - QCOMPARE(hollowItem->isPressed(), false); + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point); + QTRY_COMPARE(hollowItem->isPressed(), false); } - - delete window; } void tst_QQuickItem::childAt() @@ -3586,9 +3753,8 @@ void tst_QQuickItem::childAt() void tst_QQuickItem::grab() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabToImage not functional on minimal platforms"); QQuickView view; view.setSource(testFileUrl("grabToImage.qml")); @@ -3661,6 +3827,683 @@ void tst_QQuickItem::isAncestorOf() QVERIFY(!sub2.isAncestorOf(&sub2)); } +/* + Verify that a nested item's palette responds to changes of the enabled state + and of the window's activation state by switching the current color group. +*/ +void tst_QQuickItem::colorGroup() +{ + QQuickView view; + + view.setSource(testFileUrl("colorgroup.qml")); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject()); + QQuickItem *background = root->findChild<QQuickItem *>("background"); + QVERIFY(background); + QQuickItem *foreground = root->findChild<QQuickItem *>("foreground"); + QVERIFY(foreground); + + QQuickPalette *palette = foreground->property("palette").value<QQuickPalette*>(); + QVERIFY(palette); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QCOMPARE(palette->currentColorGroup(), QPalette::Active); + QCOMPARE(foreground->property("color").value<QColor>(), palette->active()->base()); + + background->setEnabled(false); + QCOMPARE(palette->currentColorGroup(), QPalette::Disabled); + QCOMPARE(foreground->property("color").value<QColor>(), palette->disabled()->base()); + + QWindow activationThief; + activationThief.show(); + activationThief.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&activationThief)); + QCOMPARE(palette->currentColorGroup(), QPalette::Disabled); + QCOMPARE(foreground->property("color").value<QColor>(), palette->disabled()->base()); + + background->setEnabled(true); + QCOMPARE(palette->currentColorGroup(), QPalette::Inactive); + QCOMPARE(foreground->property("color").value<QColor>(), palette->inactive()->base()); + + activationThief.hide(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + QCOMPARE(palette->currentColorGroup(), QPalette::Active); + QCOMPARE(foreground->property("color").value<QColor>(), palette->active()->base()); + + activationThief.show(); + activationThief.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&activationThief)); + QCOMPARE(palette->currentColorGroup(), QPalette::Inactive); + QCOMPARE(foreground->property("color").value<QColor>(), palette->inactive()->base()); +} + +/*! + Verify that items don't allocate their own QQuickPalette instance + unnecessarily. +*/ +void tst_QQuickItem::paletteAllocated() +{ + QQuickView view; + + view.setSource(testFileUrl("paletteAllocate.qml")); + + QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject()); + QQuickItem *background = root->findChild<QQuickItem *>("background"); + QVERIFY(background); + QQuickItem *foreground = root->findChild<QQuickItem *>("foreground"); + QVERIFY(foreground); + + bool backgroundHasPalette = false; + bool foregroundHasPalette = false; + QObject::connect(background, &QQuickItem::paletteCreated, this, [&]{ backgroundHasPalette = true; }); + QObject::connect(foreground, &QQuickItem::paletteCreated, this, [&]{ foregroundHasPalette = true; }); + + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QVERIFY(!backgroundHasPalette); + QVERIFY(!foregroundHasPalette); + + view.close(); + + QVERIFY(!backgroundHasPalette); + QVERIFY(!foregroundHasPalette); + + auto quickpalette = foreground->property("palette").value<QQuickPalette*>(); + QVERIFY(!backgroundHasPalette); + QVERIFY(quickpalette); + QVERIFY(foregroundHasPalette); +} + +void tst_QQuickItem::undefinedIsInvalidForWidthAndHeight() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("undefinedInvalid.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + auto item = qobject_cast<QQuickItem *>(root.get()); + auto priv = QQuickItemPrivate::get(item); + QVERIFY(item); + QCOMPARE(item->height(), 300); + QCOMPARE(item->width(), 200); + QVERIFY(!priv->widthValid()); + QVERIFY(!priv->heightValid()); +} + +void tst_QQuickItem::viewport_data() +{ + QTest::addColumn<bool>("contentObservesViewport"); + + QTest::addColumn<bool>("innerClip"); + QTest::addColumn<bool>("innerViewport"); + QTest::addColumn<bool>("innerObservesViewport"); + + QTest::addColumn<bool>("outerClip"); + QTest::addColumn<bool>("outerViewport"); + QTest::addColumn<bool>("outerObservesViewport"); + + QTest::addColumn<QPoint>("innerViewportOffset"); + QTest::addColumn<QPoint>("outerViewportOffset"); + + QTest::addColumn<QRect>("expectedViewportTestRect"); + QTest::addColumn<QRect>("expectedContentClipRect"); + + QTest::newRow("default") << false + << false << false << false + << false << false << false + << QPoint() << QPoint() << QRect(0, 0, 290, 290) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp, clipping and observing") << true + << true << true << true + << true << true << true + << QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp, clipping and observing; content not observing") << false + << true << true << true + << true << true << true + << QPoint() << QPoint() << QRect(0, 0, 290, 290) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing") << true + << false << true << true + << false << true << true + << QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, inner pos offset") << true + << false << true << true + << false << true << true + << QPoint(120, 120) << QPoint() << QRect(55, 55, 80, 80) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, inner neg offset") << true + << false << true << true + << false << true << true + << QPoint(-70, -50) << QPoint() << QRect(105, 85, 170, 190) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, outer pos offset") << true + << false << true << true + << false << true << true + << QPoint() << QPoint(220, 220) << QRect(55, 55, 20, 20) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, outer neg offset") << true + << false << true << true + << false << true << true + << QPoint() << QPoint(-70, -50) << QRect(65, 55, 190, 200) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, pos and neg offset") << true + << false << true << true + << false << true << true + << QPoint(150, 150) << QPoint(-170, -150) << QRect(55, 55, 50, 50) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp and observing, neg and pos offset") << true + << false << true << true + << false << true << true + << QPoint(-180, -210) << QPoint(100, 115) << QRect(215, 245, 60, 30) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp not observing") << true + << false << true << false + << false << true << false + << QPoint() << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp not observing, inner pos offset") << true + << false << true << false + << false << true << false + << QPoint(120, 120) << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp not observing, inner neg offset") << true + << false << true << false + << false << true << false + << QPoint(-70, -50) << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp not observing, outer pos offset") << true + << false << true << false + << false << true << false + << QPoint() << QPoint(220, 220) << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner and outer: vp not observing, outer neg offset") << true + << false << true << false + << false << true << false + << QPoint() << QPoint(-70, -50) << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner clipping and observing") << true + << true << true << true + << false << false << false + << QPoint() << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290); + QTest::newRow("inner clipping and observing only outer") << true + << true << true << true + << false << true << false + << QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290); +} + +void tst_QQuickItem::viewport() +{ + QFETCH(bool, contentObservesViewport); + QFETCH(bool, innerClip); + QFETCH(bool, innerViewport); + QFETCH(bool, innerObservesViewport); + QFETCH(bool, outerClip); + QFETCH(bool, outerViewport); + QFETCH(bool, outerObservesViewport); + QFETCH(QPoint, innerViewportOffset); + QFETCH(QPoint, outerViewportOffset); + QFETCH(QRect, expectedViewportTestRect); + QFETCH(QRect, expectedContentClipRect); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("viewports.qml"))); + + QQuickItem *root = qobject_cast<QQuickItem *>(window.rootObject()); + QQuickItem *outer = root->findChild<QQuickItem *>("outerViewport"); + QVERIFY(outer); + QQuickItem *inner = root->findChild<QQuickItem *>("innerViewport"); + QVERIFY(inner); + QQuickItem *contentItem = root->findChild<QQuickItem *>("innerRect"); + QVERIFY(contentItem); + ViewportTestItem *viewportTestItem = root->findChild<ViewportTestItem *>(); + QVERIFY(viewportTestItem); + + inner->setPosition(inner->position() + innerViewportOffset); + outer->setPosition(outer->position() + outerViewportOffset); + outer->setClip(outerClip); + QCOMPARE(outer->flags().testFlag(QQuickItem::ItemIsViewport), outerClip); + outer->setFlag(QQuickItem::ItemIsViewport, outerViewport); + outer->setFlag(QQuickItem::ItemObservesViewport, outerObservesViewport); + inner->setClip(innerClip); + QCOMPARE(inner->flags().testFlag(QQuickItem::ItemIsViewport), innerClip); + inner->setFlag(QQuickItem::ItemIsViewport, innerViewport); + inner->setFlag(QQuickItem::ItemObservesViewport, innerObservesViewport); + viewportTestItem->setFlag(QQuickItem::ItemObservesViewport, contentObservesViewport); + emit viewportTestItem->viewportChanged(); + + if (lcTests().isDebugEnabled()) + QTest::qWait(1000); + if (contentObservesViewport) { + if (innerViewport) + QCOMPARE(viewportTestItem->viewportItem(), inner); + else if (outerViewport) + QCOMPARE(viewportTestItem->viewportItem(), outer); + else + QCOMPARE(viewportTestItem->viewportItem(), root->parentItem()); // QQuickRootItem + } else { + QCOMPARE(viewportTestItem->viewportItem(), root->parentItem()); // QQuickRootItem + } + + QCOMPARE(contentItem->clipRect().toRect(), expectedContentClipRect); + QCOMPARE(viewportTestItem->clipRect().toRect(), expectedViewportTestRect); +} + +// Test that in a slot connected to destroyed() the emitter is +// is no longer a QQuickItem. +void tst_QQuickItem::qobject_castOnDestruction() +{ + QQuickItem item; + QObject::connect(&item, &QObject::destroyed, [](QObject *object) + { + QVERIFY(!qobject_cast<QQuickItem *>(object)); + QVERIFY(!dynamic_cast<QQuickItem *>(object)); + QVERIFY(!object->isQuickItemType()); + }); +} + +/* + Items that are getting destroyed should not emit property change notifications. +*/ +void tst_QQuickItem::signalsOnDestruction_data() +{ + QTest::addColumn<bool>("childVisible"); + QTest::addColumn<bool>("grandChildVisible"); + + QTest::addRow("Both visible") << true << true; + QTest::addRow("Child visible") << true << false; + QTest::addRow("Grand child visible") << false << true; + QTest::addRow("Both hidden") << false << false; +} + +void tst_QQuickItem::signalsOnDestruction() +{ + QFETCH(bool, childVisible); + QFETCH(bool, grandChildVisible); + + QQuickWindow window; + window.show(); + + int expectedChildVisibleCount = 0; + int expectedGrandChildVisibleCount = 0; + + // Visual children, but not QObject children. + // Note: QQuickItem's visible property defaults to true after creation, as visual + // items are always expected to be added to a visual hierarchy. So for the sake + // of this test we first add, and then remove the item from a parent. This explicitly + // sets the effective visibility to false. + std::unique_ptr<QQuickItem> parent(new QQuickItem(window.contentItem())); + QVERIFY(parent->isVisible()); + std::unique_ptr<QQuickItem> child(new QQuickItem); + child->setVisible(childVisible); + child->setParentItem(parent.get()); + child->setParentItem(nullptr); + QVERIFY(!child->isVisible()); + std::unique_ptr<QQuickItem> grandChild(new QQuickItem); + grandChild->setVisible(grandChildVisible); + grandChild->setParentItem(child.get()); + grandChild->setParentItem(nullptr); + QVERIFY(!grandChild->isVisible()); + + QSignalSpy childrenSpy(parent.get(), &QQuickItem::childrenChanged); + QSignalSpy visibleChildrenSpy(parent.get(), &QQuickItem::visibleChildrenChanged); + QSignalSpy childParentSpy(child.get(), &QQuickItem::parentChanged); + QSignalSpy childVisibleSpy(child.get(), &QQuickItem::visibleChanged); + QSignalSpy childChildrenSpy(child.get(), &QQuickItem::childrenChanged); + QSignalSpy childVisibleChildrenSpy(child.get(), &QQuickItem::visibleChildrenChanged); + QSignalSpy grandChildParentSpy(grandChild.get(), &QQuickItem::parentChanged); + QSignalSpy grandChildVisibleSpy(grandChild.get(), &QQuickItem::visibleChanged); + + child->setParentItem(parent.get()); + QCOMPARE(childrenSpy.count(), 1); + if (childVisible) + ++expectedChildVisibleCount; + QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount); + QCOMPARE(childChildrenSpy.count(), 0); + QCOMPARE(childVisibleChildrenSpy.count(), 0); + + grandChild->setParentItem(child.get()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount); + QCOMPARE(childChildrenSpy.count(), 1); + if (grandChildVisible && childVisible) + ++expectedGrandChildVisibleCount; + QCOMPARE(childVisibleChildrenSpy.count(), expectedGrandChildVisibleCount); + QCOMPARE(grandChildParentSpy.count(), 1); + QCOMPARE(grandChildVisibleSpy.count(), expectedGrandChildVisibleCount); + + parent.reset(); + + QVERIFY(!child->parentItem()); + QVERIFY(grandChild->parentItem()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount); + QCOMPARE(childParentSpy.count(), 2); + QCOMPARE(childChildrenSpy.count(), 1); + if (childVisible) + ++expectedChildVisibleCount; + QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount); + if (childVisible && grandChildVisible) + ++expectedGrandChildVisibleCount; + QCOMPARE(childVisibleChildrenSpy.count(), expectedGrandChildVisibleCount); + QCOMPARE(grandChildParentSpy.count(), 1); + QCOMPARE(grandChildVisibleSpy.count(), expectedGrandChildVisibleCount); +} + +void tst_QQuickItem::visibleChanged() +{ + QQuickView window; + window.setSource(testFileUrl("visiblechanged.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject()); + QVERIFY(root); + + QPointer<QQuickItem> parentItem = root->findChild<QQuickItem *>("parentItem"); + QPointer<QQuickItem> childItem = root->findChild<QQuickItem *>("childItem"); + QPointer<QQuickItem> loader = root->findChild<QQuickItem *>("loader"); + QPointer<QQuickItem> loaderChild = root->findChild<QQuickItem *>("loaderChild"); + QVERIFY(parentItem); + QVERIFY(childItem); + QVERIFY(loader); + QVERIFY(loaderChild); + + QSignalSpy parentItemSpy(parentItem.data(), &QQuickItem::visibleChanged); + QSignalSpy childItemSpy(childItem.data(), &QQuickItem::visibleChanged); + QSignalSpy loaderChildSpy(loaderChild.data(), &QQuickItem::visibleChanged); + + loader->setProperty("active", false); + QCOMPARE(parentItemSpy.count(), 0); + QCOMPARE(childItemSpy.count(), 0); + QVERIFY(!loaderChild->parentItem()); + QCOMPARE(loaderChildSpy.count(), 1); + QCOMPARE(loaderChild->isVisible(), false); + + delete parentItem.data(); + QVERIFY(!parentItem); + QVERIFY(childItem); + QVERIFY(!childItem->parentItem()); + + QCOMPARE(parentItemSpy.count(), 0); + QCOMPARE(childItemSpy.count(), 1); +} + +void tst_QQuickItem::lastFocusChangeReason() +{ + std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>(); + window->setSource(testFileUrl("focusReason.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.get())); + + QQuickItem *item = window->findChild<QQuickItem *>("item"); + QQuickItem *customText = window->findChild<QQuickItem *>("customText"); + QQuickItem *customItem = window->findChild<QQuickItem *>("customItem"); + QQuickItem *hyperlink = window->findChild<QQuickItem *>("hyperlink"); + QQuickItem *textInputChild = window->findChild<QQuickItem *>("textInputChild"); + + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + QQuickItemPrivate *customTextPrivate = QQuickItemPrivate::get(customText); + QQuickItemPrivate *customItemPrivate = QQuickItemPrivate::get(customItem); + QQuickItemPrivate *hyperlinkPrivate = QQuickItemPrivate::get(hyperlink); + QQuickItemPrivate *textInputChildPrivate = QQuickItemPrivate::get(textInputChild); + + QVERIFY(item); + QVERIFY(customText); + QVERIFY(customItem); + QVERIFY(hyperlink); + QVERIFY(textInputChild); + + QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls); + auto resetTabFocusBehavior = qScopeGuard([]{ + QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1)); + }); + + // helper for clicking into an item + const auto itemCenter = [](const QQuickItem *item) -> QPoint { + return item->mapToScene(item->clipRect().center()).toPoint(); + }; + + // setting focusPolicy to Strong/WheelFocus doesn't implicitly turn on event delivery + customText->setAcceptedMouseButtons(Qt::LeftButton); + customItem->setAcceptedMouseButtons(Qt::LeftButton); + customItem->setAcceptTouchEvents(true); + customText->setAcceptTouchEvents(true); + hyperlink->setAcceptTouchEvents(true); + + // window activation -> ActiveWindowFocusReason + QVERIFY(item->hasFocus()); + QVERIFY(item->hasActiveFocus()); + if (itemPrivate->lastFocusChangeReason() != Qt::ActiveWindowFocusReason + && QStringList{"windows", "offscreen"}.contains(QGuiApplication::platformName())) { + QEXPECT_FAIL("", "On Windows and offscreen platforms, window activation does not set focus reason", Continue); + } + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ActiveWindowFocusReason); + + // test setter/getter + item->setFocus(false, Qt::MouseFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + item->setFocus(true, Qt::TabFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + item->setFocus(false, Qt::BacktabFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + item->forceActiveFocus(Qt::ShortcutFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ShortcutFocusReason); + item->setFocus(false, Qt::NoFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::NoFocusReason); + QVERIFY(!item->hasFocus()); + + // programmatic focus changes + item->setFocus(true, Qt::OtherFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason); + + QVERIFY(item->hasFocus()); + QVERIFY(item->hasActiveFocus()); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason); + + // tab focus -> TabFocusReason + QTest::keyClick(window.get(), Qt::Key_Tab); + QVERIFY(customText->hasFocus()); + QVERIFY(customText->hasActiveFocus()); + QCOMPARE(qApp->focusObject(), customText); + QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + + QTest::keyClick(window.get(), Qt::Key_Tab); + QVERIFY(customItem->hasFocus()); + QVERIFY(customItem->hasActiveFocus()); + QCOMPARE(qApp->focusObject(), customItem); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + + QTest::keyClick(window.get(), Qt::Key_Tab); + QVERIFY(hyperlink->hasFocus()); + QVERIFY(hyperlink->hasActiveFocus()); + QCOMPARE(qApp->focusObject(), hyperlink); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + + QTest::keyClick(window.get(), Qt::Key_Tab); + QVERIFY(item->hasFocus()); + QVERIFY(item->hasActiveFocus()); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason); + + // backtab -> BacktabFocusReason + QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier); + QVERIFY(hyperlink->hasFocus()); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + + QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier); + QVERIFY(customItem->hasFocus()); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + + QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier); + QVERIFY(customText->hasFocus()); + QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason); + + // click focus -> MouseFocusReason + QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customItem)); + QVERIFY(customItem->hasFocus()); + QVERIFY(customItem->hasActiveFocus()); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + + QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(hyperlink)); + QVERIFY(hyperlink->hasFocus()); + QVERIFY(hyperlink->hasActiveFocus()); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + + QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customText)); + QCOMPARE(textInputChild, textInputChild); + QVERIFY(textInputChild->hasFocus()); + QVERIFY(textInputChild->hasActiveFocus()); + QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + + // touch focus -> MouseFocusReason + std::unique_ptr<QPointingDevice> touchDevice(QTest::createTouchDevice()); + + QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem)); + QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem)); + QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(customItem)); + QVERIFY(customItem->hasFocus()); + QVERIFY(customItem->hasActiveFocus()); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + + QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(hyperlink)); + QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(hyperlink)); + QVERIFY(hyperlink->hasFocus()); + QVERIFY(hyperlink->hasActiveFocus()); + QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); + + // Wheel focus -> MouseFocusReason + QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(), + QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier, + Qt::NoScrollPhase, false); + QGuiApplication::sendEvent(customItem, &wheelEvent); + QVERIFY(customItem->hasActiveFocus()); + QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason); +} + +void tst_QQuickItem::focusInScopeChanges() +{ + std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>(); + window->setSource(testFileUrl("focusInScopeChanges.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowFocused(window.get())); + + QQuickItem *main = window->rootObject(); + QVERIFY(main); + QQuickItem *focusScope = main->findChild<QQuickItem *>("focusScope"); + QQuickItem *rect = main->findChild<QQuickItem *>("rect"); + QQuickItem *textInput = main->findChild<QQuickItem *>("textInput"); + + QVERIFY(focusScope); + QVERIFY(rect); + QVERIFY(textInput); + QVERIFY(window->contentItem()); + + QSignalSpy fsActiveFocusSpy(focusScope, SIGNAL(activeFocusChanged(bool))); + QSignalSpy rectActiveFocusSpy(rect, SIGNAL(activeFocusChanged(bool))); + QSignalSpy textInputActiveFocusSpy(textInput, SIGNAL(activeFocusChanged(bool))); + + // The window's content item will have activeFocus if window is focused + QTRY_VERIFY(window->contentItem()->hasActiveFocus()); + + QVERIFY(!focusScope->hasActiveFocus()); + QVERIFY(!rect->hasActiveFocus()); + QVERIFY(!textInput->hasActiveFocus()); + QCOMPARE(fsActiveFocusSpy.size(), 0); + QCOMPARE(rectActiveFocusSpy.size(), 0); + QCOMPARE(textInputActiveFocusSpy.size(), 0); + + // setting focus to rect shouldn't affect activeFocus as long as its + // parent focus scope doesn't have the activeFocus + rect->setFocus(true); + QCOMPARE(fsActiveFocusSpy.size(), 0); + QCOMPARE(rectActiveFocusSpy.size(), 0); + QCOMPARE(textInputActiveFocusSpy.size(), 0); + + // focusScope is the only child with focus in the parent + // scope, so it will gain activeFocus + focusScope->setFocus(true); + QCOMPARE(fsActiveFocusSpy.size(), 1); + QVERIFY(fsActiveFocusSpy.first().at(0).toBool()); + // rect loses activeFocus because textInput gains it (as a result of code in signal handler) + QCOMPARE(rectActiveFocusSpy.size(), 2); + QVERIFY(!rect->hasActiveFocus()); + QCOMPARE(textInputActiveFocusSpy.size(), 1); + QVERIFY(textInput->hasActiveFocus()); +} + +#ifdef QT_WIDGETS_LIB +void tst_QQuickItem::embeddedInWidgetsFocus() +{ + QWidget root; + QVBoxLayout *layout = new QVBoxLayout(&root); + + QLineEdit *lineEdit1 = new QLineEdit(&root); + lineEdit1->setFocusPolicy(Qt::FocusPolicy::TabFocus); + + QQuickView *quickView = new QQuickView; + quickView->setSource(testFileUrl("embedded.qml")); + QWidget *container = QWidget::createWindowContainer(quickView, &root); + container->setMinimumSize(quickView->size()); + container->setFocusPolicy(Qt::TabFocus); + + QLineEdit *lineEdit2 = new QLineEdit(&root); + lineEdit2->setFocusPolicy(Qt::FocusPolicy::TabFocus); + + layout->addWidget(lineEdit1); + layout->addWidget(container); + layout->addWidget(lineEdit2); + + QQuickItem *rect1 = findItem<QQuickItem>(quickView->rootObject(), "rect1"); + QQuickItem *rect2 = findItem<QQuickItem>(quickView->rootObject(), "rect2"); + QVERIFY(rect1); + QVERIFY(rect2); + + root.show(); + QTRY_VERIFY(root.isVisible()); + QVERIFY(QTest::qWaitForWindowExposed(&root)); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + + lineEdit1->setFocus(); + QTRY_VERIFY(lineEdit1->hasFocus()); + + // Tab forward + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab); + QTRY_VERIFY(container->hasFocus()); + QVERIFY(QTest::qWaitForWindowFocused(quickView)); + QVERIFY(rect1->hasActiveFocus()); + + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab); + QTRY_VERIFY(rect2->hasActiveFocus()); + + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QVERIFY(lineEdit2->hasFocus()); + QVERIFY(!rect2->hasActiveFocus()); + + // Tab backwards + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier); + QTRY_VERIFY(container->hasFocus()); + QVERIFY(QTest::qWaitForWindowFocused(quickView)); + QVERIFY(rect2->hasActiveFocus()); + + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier); + QVERIFY(rect1->hasActiveFocus()); + + QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QVERIFY(lineEdit1->hasFocus()); +} +#endif + QTEST_MAIN(tst_QQuickItem) #include "tst_qquickitem.moc" |