diff options
Diffstat (limited to 'tests/auto')
21 files changed, 559 insertions, 2 deletions
diff --git a/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml new file mode 100644 index 0000000000..fa154b25f3 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +Item { + property var isSelected: null + property string source + implicitWidth: 16 + implicitHeight: 16 + + onSourceChanged: { + updateImageSource() + } + + onIsSelectedChanged: { + updateImageSource() + } + + function updateImageSource() { + let result = isSelected ? source + "_selected_dark.png" : source + "_active_dark.png" + } + +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml new file mode 100644 index 0000000000..46d7524527 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 + +Item { + id: root + width: 640 + height: 480 + property bool works: myView.currentItem.okay + + ListView { + id: myView + model: myModel + anchors.fill: parent + delegate: Row { + property alias okay: image.isSelected + ImageToggle { + id: image + source: "glyph_16_arrow_patch" + isSelected: model.age < 6 + } + Text { + text: "age:" + model.age + " selected:" + image.isSelected + } + } + } + + ListModel { + id: myModel + ListElement { type: "Cat"; age: 3; } + ListElement { type: "Dog"; age: 2; } + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index 9bc359d243..35f1e2c94d 100644 --- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -46,6 +46,7 @@ private slots: void valueWithoutCallingObjectFirst(); void filterOnGroup_removeWhenCompleted(); void qtbug_86017(); + void contextAccessedByHandler(); }; class AbstractItemModel : public QAbstractItemModel @@ -164,6 +165,27 @@ void tst_QQmlDelegateModel::qtbug_86017() QCOMPARE(model->filterGroup(), "selected"); } +void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted() +{ + QQuickView view(testFileUrl("removeFromGroup.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QQuickItem *root = view.rootObject(); + QVERIFY(root); + QQmlDelegateModel *model = root->findChild<QQmlDelegateModel*>(); + QVERIFY(model); + QVERIFY(QTest::qWaitFor([=]{ return model->count() == 2; })); +} + +void tst_QQmlDelegateModel::contextAccessedByHandler() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextAccessedByHandler.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY2(root, qPrintable(component.errorString())); + QVERIFY(root->property("works").toBool()); +} + QTEST_MAIN(tst_QQmlDelegateModel) #include "tst_qqmldelegatemodel.moc" diff --git a/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml new file mode 100644 index 0000000000..7fe366cac8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml @@ -0,0 +1,13 @@ +import QtQml 2.15 + +QtObject { + function test_generator_gc() { + ((function*() { gc() })()).next(); + ((function*() { gc() })()).next(); + ((function*() { gc() })()).next(); + ((function*() { gc() })()).next(); + } + + Component.onCompleted: () => test_generator_gc() + +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 3c3a2a7a99..7da1b2c500 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -240,6 +240,7 @@ private slots: void function(); void topLevelGeneratorFunction(); void generatorCrashNewProperty(); + void generatorCallsGC(); void qtbug_10696(); void qtbug_11606(); void qtbug_11600(); @@ -6505,6 +6506,15 @@ void tst_qqmlecmascript::generatorCrashNewProperty() QCOMPARE(o->property("c").toInt(), 42); } +void tst_qqmlecmascript::generatorCallsGC() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("generatorCallsGC.qml")); + + QScopedPointer<QObject> o(component.create()); // should not crash + QVERIFY2(o != nullptr, qPrintable(component.errorString())); +} + // Test the "Qt.include" method void tst_qqmlecmascript::include() { diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt new file mode 100644 index 0000000000..d69122ef8b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt @@ -0,0 +1 @@ +6:40:Invalid alias target diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml new file mode 100644 index 0000000000..17602ca4b9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + id: root + readonly property QtObject test: QtObject { property int subproperty: 3} + readonly property alias testAlias: root.test.subproperty +} + diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 94ecd6862a..31bf30c57c 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -146,6 +146,7 @@ private slots: void aliasProperties(); void aliasPropertiesAndSignals(); void aliasPropertyChangeSignals(); + void qtbug_89822(); void componentCompositeType(); void i18n(); void i18n_data(); @@ -2232,6 +2233,12 @@ void tst_qqmllanguage::aliasPropertiesAndSignals() QCOMPARE(o->property("test").toBool(), true); } +void tst_qqmllanguage::qtbug_89822() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_89822.qml")); + VERIFY_ERRORS("qtbug_89822.errors.txt"); +} + // Test that the root element in a composite type can be a Component void tst_qqmllanguage::componentCompositeType() { diff --git a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml index aa26956922..09f1d472b7 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml @@ -10,6 +10,59 @@ Item { property variant v2: Qt.vector3d(1,2,3) property real factor: 2.23 + function testTransformation() { + let m = Qt.matrix4x4(); + + m.scale(1, 2, 4); + if (m !== Qt.matrix4x4(1, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 4, 0, + 0, 0, 0, 1)) + return false; + m.scale(Qt.vector3d(-8, -4, -2)); + if (m !== Qt.matrix4x4(-8, 0, 0, 0, + 0,-8, 0, 0, + 0, 0, -8, 0, + 0, 0, 0, 1)) + return false; + m.scale(-1 / 8); + if (m !== Qt.matrix4x4()) + return false; + + m.rotate(180, Qt.vector3d(1, 0, 0)); + if (m !== Qt.matrix4x4(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1)) + return false; + m.rotate(180, Qt.vector3d(0, 1, 0)); + if (m !== Qt.matrix4x4(-1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1)) + return false; + m.rotate(180, Qt.vector3d(0, 0, 1)); + if (m !== Qt.matrix4x4()) + return false; + + m.translate(Qt.vector3d(1, 2, 4)); + if (m !== Qt.matrix4x4(1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 4, + 0, 0, 0, 1)) + return false; + + m = Qt.matrix4x4(); + m.lookAt(Qt.vector3d(1, 2, 4), Qt.vector3d(1, 2, 0), Qt.vector3d(0, 1, 0)); + if (m !== Qt.matrix4x4(1, 0, 0, -1, + 0, 1, 0, -2, + 0, 0, 1, -4, + 0, 0, 0, 1)) + return false; + + return true; + } + Component.onCompleted: { success = true; if (m1.times(m2) != Qt.matrix4x4(26, 26, 26, 26, 52, 52, 52, 52, 78, 78, 78, 78, 104, 104, 104, 104)) success = false; @@ -27,5 +80,6 @@ Item { if (m1.transposed() != Qt.matrix4x4(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)) success = false; if (m1.fuzzyEquals(m2)) success = false; if (!m1.fuzzyEquals(m2, 10)) success = false; + if (!testTransformation()) success = false; } } diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp index 249ecd5aa5..a4f138c8df 100644 --- a/tests/auto/quick/nodes/tst_nodestest.cpp +++ b/tests/auto/quick/nodes/tst_nodestest.cpp @@ -125,12 +125,12 @@ public: setRootNode(root); } - void render() { + void render() override { ++renderCount; renderingOrder = ++globalRendereringOrder; } - void nodeChanged(QSGNode *node, QSGNode::DirtyState state) { + void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override { changedNode = node; changedState = state; QSGBatchRenderer::Renderer::nodeChanged(node, state); diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml new file mode 100644 index 0000000000..b24812c914 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml @@ -0,0 +1,34 @@ +import QtQuick 2.15 + +import Test 1.0 + +Item { + width: 640 + height: 480 + + Rectangle { + anchors.fill: parent + color: "grey" + + Rectangle { + x: 200 + y: 200 + width: 100 + height: 100 + color: "orange" + DragHandler { + grabPermissions: DragHandler.CanTakeOverFromAnything // but not anything with keepMouseGrab! + } + } + } + + ModalLayer { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "red" + opacity: 0.4 + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 4d6866041e..f71febbaf9 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -68,6 +68,7 @@ private slots: void touchPassiveGrabbers_data(); void touchPassiveGrabbers(); void touchPinchAndMouseMove(); + void underModalLayer(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -811,6 +812,59 @@ void tst_DragHandler::touchPinchAndMouseMove() } } +class ModalLayer : public QQuickItem { +public: + explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) { + this->setAcceptedMouseButtons(Qt::AllButtons); + this->setAcceptTouchEvents(true); + this->setKeepMouseGrab(true); + this->setKeepTouchGrab(true); + } + + bool event(QEvent* event) override { + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseTrackingChange: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchCancel: + case QEvent::TouchEnd: { + qCDebug(lcPointerTests) << "BLOCK!" << event->type(); + return true; + } + default: break; + } + return QQuickItem::event(event); + } +}; + +void tst_DragHandler::underModalLayer() // QTBUG-78258 +{ + qmlRegisterType<ModalLayer>("Test", 1, 0, "ModalLayer"); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragHandlerUnderModalLayer.qml"); + QQuickView * window = windowPtr.data(); + QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QPoint p1(250, 250); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + QTest::mouseRelease(window, Qt::LeftButton); +} + QTEST_MAIN(tst_DragHandler) #include "tst_qquickdraghandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp index 19fdae3b44..2b8ddd197d 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp @@ -217,6 +217,7 @@ void tst_QQuickPinchHandler::scale() QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler"); QVERIFY(pinchHandler != nullptr); + QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QQuickEventPoint::GrabTransition, QQuickEventPoint*))); QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject()); QVERIFY(root != nullptr); @@ -238,6 +239,7 @@ void tst_QQuickPinchHandler::scale() // it is outside its bounds. pinchSequence.stationary(0).press(1, p1, window).commit(); QQuickTouchUtils::flush(window); + QTRY_COMPARE(grabChangedSpy.count(), 1); // passive grab QPoint pd(10, 10); // move one point until PinchHandler activates @@ -247,6 +249,8 @@ void tst_QQuickPinchHandler::scale() QQuickTouchUtils::flush(window); } QCOMPARE(pinchHandler->active(), true); + // first point got a passive grab; both points got exclusive grabs + QCOMPARE(grabChangedSpy.count(), 3); QLineF line(p0, p1); const qreal startLength = line.length(); diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml index dc960a24d0..fb4db5ae54 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml @@ -894,6 +894,37 @@ CanvasTestCase { comparePixel(ctx, 39,0, 0,0,0,0); } + function test_lineDashReset(row) { + var canvas = createCanvasObject(row); + var ctx = canvas.getContext('2d'); + ctx.reset(); + ctx.strokeStyle = "#ff0000"; + ctx.lineWidth = 2; + var pattern = [2, 3, 5, 1, 6, 3] + ctx.setLineDash(pattern) + + compare(ctx.getLineDash(), pattern); + + pattern = [] + ctx.setLineDash(pattern) + compare(ctx.getLineDash(), pattern); + + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(40, 0); + ctx.stroke(); + + comparePixel(ctx, 0,0, 255,0,0,255); + comparePixel(ctx, 4,0, 255,0,0,255); + comparePixel(ctx, 5,0, 255,0,0,255); + comparePixel(ctx, 14,0, 255,0,0,255); + comparePixel(ctx, 20,0, 255,0,0,255); + comparePixel(ctx, 21,0, 255,0,0,255); + comparePixel(ctx, 22,0, 255,0,0,255); + comparePixel(ctx, 34,0, 255,0,0,255); + comparePixel(ctx, 35,0, 255,0,0,255); + } + function test_lineDashOffset(row) { var canvas = createCanvasObject(row); var ctx = canvas.getContext('2d'); diff --git a/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml new file mode 100644 index 0000000000..ec419f8935 --- /dev/null +++ b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Item { + id: myObject + readonly property int testProperty: 0 + readonly property QtObject myproperty: myObject +} + diff --git a/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml new file mode 100644 index 0000000000..88fd8509cc --- /dev/null +++ b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml @@ -0,0 +1,13 @@ +import QtQuick 2.11 + +Rectangle { + objectName: "rootItem" + color: "white" + width: 800 + height: 600 + + RecursiveProperty { + objectName: "recursiveProperty" + + } +} diff --git a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp index b44977bd5a..0471619049 100644 --- a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp +++ b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp @@ -62,6 +62,7 @@ private slots: void testNotifyPropertyChangeCallBack(); void testFixResourcePathsForObjectCallBack(); void testComponentOnCompleteSignal(); + void testPropertyNames(); }; void tst_qquickdesignersupport::customData() @@ -586,6 +587,50 @@ void tst_qquickdesignersupport::testComponentOnCompleteSignal() } } +void tst_qquickdesignersupport::testPropertyNames() +{ +#ifdef Q_CC_MINGW + QSKIP("QQuickDesignerSupportProperties::registerCustomData segfaults on mingw. QTBUG-90869"); +#endif + + QScopedPointer<QQuickView> view(new QQuickView); + view->engine()->setOutputWarningsToStandardError(false); + view->setSource(testFileUrl("propertyNameTest.qml")); + + QVERIFY(view->errors().isEmpty()); + QQuickItem *rootItem = view->rootObject(); + QVERIFY(rootItem); + + QQuickDesignerSupport::PropertyNameList names = QQuickDesignerSupportProperties::allPropertyNames(rootItem); + QVERIFY(!names.isEmpty()); + QVERIFY(names.contains("width")); + QVERIFY(names.contains("height")); + QVERIFY(names.contains("clip")); + QVERIFY(names.contains("opacity")); + QVERIFY(names.contains("childrenRect")); + QVERIFY(names.contains("activeFocus")); + QVERIFY(names.contains("border.width")); + names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(rootItem); + QVERIFY(!names.isEmpty()); + QVERIFY(names.contains("width")); + QVERIFY(names.contains("height")); + QVERIFY(names.contains("opacity")); + QVERIFY(names.contains("clip")); + QVERIFY(!names.contains("childrenRect")); + QVERIFY(!names.contains("activeFocus")); + QVERIFY(names.contains("border.width")); + + QQuickItem *recursiveProperty = findItem<QQuickItem>(rootItem, QLatin1String("recursiveProperty")); + QVERIFY(recursiveProperty); + names = QQuickDesignerSupportProperties::allPropertyNames(recursiveProperty); + QVERIFY(!names.isEmpty()); + QVERIFY(names.contains("testProperty")); + QVERIFY(names.contains("myproperty.testProperty")); + + names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(recursiveProperty); + QVERIFY(!names.isEmpty()); + QVERIFY(!names.contains("testProperty")); +} QTEST_MAIN(tst_qquickdesignersupport) diff --git a/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml new file mode 100644 index 0000000000..a690c4243b --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml @@ -0,0 +1,51 @@ +import QtQuick 2.12 + +Item { + width: 240; height: 240 + Loader { + id: loader + sourceComponent: surfaceParent + anchors.fill: parent + + onStatusChanged: { + if (status === Loader.Ready) { + holder.create() + holder.item.parent = item + } else if (status === Loader.Null){ + holder.item.parent = null + } + } + } + + property var holder: QtObject { + property bool created: false + function create() + { + if (!created) + surfaceComponent.createObject(item) + created = true + } + + property Item item: Item { + anchors.fill: parent + Component { + id: surfaceComponent + Item { + anchors.fill: parent + TextInput { + width: parent.width + font.pixelSize: 40 + text: "focus me" + } + } + } + } + } + + Component { + id: surfaceParent + Rectangle { + anchors.fill: parent + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index f65650cf9c..c8f251dbe1 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -33,6 +33,7 @@ #include <QtQuick/qquickitemgrabresult.h> #include <QtQuick/qquickview.h> #include <QtGui/private/qinputmethod_p.h> +#include <QtQuick/private/qquickloader_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquicktextinput_p.h> #include <QtQuick/private/qquickitemchangelistener_p.h> @@ -75,6 +76,7 @@ private slots: void qtbug_50516(); void qtbug_50516_2_data(); void qtbug_50516_2(); + void focusableItemReparentedToLoadedComponent(); void keys(); #if QT_CONFIG(shortcut) @@ -1312,6 +1314,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); diff --git a/tests/auto/quick/qquicktextinput/data/focusReason.qml b/tests/auto/quick/qquicktextinput/data/focusReason.qml new file mode 100644 index 0000000000..7ac913d363 --- /dev/null +++ b/tests/auto/quick/qquicktextinput/data/focusReason.qml @@ -0,0 +1,39 @@ +import QtQuick 2.2 + +Rectangle { + width: 400 + height: 400 + + Column { + spacing: 5 + TextInput { + id: first + objectName: "first" + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + KeyNavigation.backtab: third + KeyNavigation.tab: second + KeyNavigation.down: second + } + TextInput { + id: second + objectName: "second" + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + KeyNavigation.up: first + KeyNavigation.backtab: first + KeyNavigation.tab: third + } + TextInput { + objectName: "third" + id: third + width: 100 + Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 } + KeyNavigation.backtab: second + KeyNavigation.tab: first + } + Component.onCompleted: { + first.focus = true + } + } +} diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index ac502bcb28..7c5c09055d 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -237,6 +237,8 @@ private slots: void QTBUG_77814_InsertRemoveNoSelection(); void checkCursorDelegateWhenPaddingChanged(); + + void focusReason(); private: void simulateKey(QWindow *, int key); @@ -7084,6 +7086,84 @@ void tst_qquicktextinput::checkCursorDelegateWhenPaddingChanged() QCOMPARE(cursorDelegate->y(), textInput->topPadding()); } +/*! + Verifies that TextInput items get focus in/out events with the + correct focus reason set. + + Up and Down keys translates to Backtab and Tab focus reasons. + + See QTBUG-75862. +*/ +void tst_qquicktextinput::focusReason() +{ + QQuickView view; + view.setSource(testFileUrl("focusReason.qml")); + + QQuickTextInput *first = view.rootObject()->findChild<QQuickTextInput *>("first"); + QQuickTextInput *second = view.rootObject()->findChild<QQuickTextInput *>("second"); + QQuickTextInput *third = view.rootObject()->findChild<QQuickTextInput *>("third"); + QVERIFY(first && second && third); + + class FocusEventFilter : public QObject + { + public: + using QObject::QObject; + + QHash<QObject*, Qt::FocusReason> lastFocusReason; + protected: + bool eventFilter(QObject *o, QEvent *e) + { + if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) { + QFocusEvent *fe = static_cast<QFocusEvent*>(e); + lastFocusReason[o] = fe->reason(); + } + return QObject::eventFilter(o, e); + } + } eventFilter; + first->installEventFilter(&eventFilter); + second->installEventFilter(&eventFilter); + third->installEventFilter(&eventFilter); + + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QCOMPARE(qApp->focusObject(), first); + // on some platforms we don't get ActiveWindowFocusReason; tolerate this, + // it's not what we are testing in this test + if (eventFilter.lastFocusReason[first] != Qt::ActiveWindowFocusReason) { + QEXPECT_FAIL("", qPrintable(QString("No window activation event on the %1 platform") + .arg(QGuiApplication::platformName())), + Continue); + } + QCOMPARE(eventFilter.lastFocusReason[first], Qt::ActiveWindowFocusReason); + + QTest::mouseClick(&view, Qt::LeftButton, {}, + (second->boundingRect().center() + second->position()).toPoint()); + QTRY_COMPARE(qApp->focusObject(), second); + QCOMPARE(eventFilter.lastFocusReason[first], Qt::MouseFocusReason); + QCOMPARE(eventFilter.lastFocusReason[second], Qt::MouseFocusReason); + + QTest::keyClick(&view, Qt::Key_Tab); + QCOMPARE(qApp->focusObject(), third); + QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason); + QCOMPARE(eventFilter.lastFocusReason[third], Qt::TabFocusReason); + + QTest::keyClick(&view, Qt::Key_Backtab); + QCOMPARE(qApp->focusObject(), second); + QCOMPARE(eventFilter.lastFocusReason[third], Qt::BacktabFocusReason); + QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason); + + QTest::keyClick(&view, Qt::Key_Up); + QCOMPARE(qApp->focusObject(), first); + QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason); + QCOMPARE(eventFilter.lastFocusReason[first], Qt::BacktabFocusReason); + + QTest::keyClick(&view, Qt::Key_Down); + QCOMPARE(qApp->focusObject(), second); + QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason); + QCOMPARE(eventFilter.lastFocusReason[first], Qt::TabFocusReason); +} + QTEST_MAIN(tst_qquicktextinput) #include "tst_qquicktextinput.moc" |