aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp70
-rw-r--r--tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml12
-rw-r--r--tests/auto/qml/qmlformat/data/enumWithValues.qml12
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp3
-rw-r--r--tests/auto/qml/qmllint/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qmllint/data/something.qml2
-rw-r--r--tests/auto/qml/qmllint/importRelScript/CMakeLists.txt18
-rw-r--r--tests/auto/qml/qmllint/importRelScript/Main.qml6
-rw-r--r--tests/auto/qml/qmllint/importRelScript/script.js3
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp27
-rw-r--r--tests/auto/qml/qqmlcomponent/data/bindingInRequired.qml16
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp19
-rw-r--r--tests/auto/qml/qqmldelegatemodel/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/reset.qml28
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml28
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp136
-rw-r--r--tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml29
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp16
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp10
-rw-r--r--tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp3
-rw-r--r--tests/auto/qml/qqmllanguage/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/BindingOverrider.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/SimpleWidget.qml25
-rw-r--r--tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir4
-rw-r--r--tests/auto/qml/qqmllanguage/data/nestedVectors.qml27
-rw-r--r--tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt13
-rw-r--r--tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp7
-rw-r--r--tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h20
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp1
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h32
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp76
-rw-r--r--tests/auto/qml/qqmllistmodel/data/deadModelData.qml45
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp62
-rw-r--r--tests/auto/qml/qqmllistreference/data/compositeListProp.qml11
-rw-r--r--tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp23
-rw-r--r--tests/auto/qml/qqmltablemodel/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp56
-rw-r--r--tests/auto/qml/qv4estable/tst_qv4estable.cpp6
-rw-r--r--tests/auto/qmltest/listview/tst_listview.qml4
-rw-r--r--tests/auto/qmltest/selftests/tst_findChild.qml2
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp1
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp36
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp2
-rw-r--r--tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp3
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp25
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp2
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp2
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml90
-rw-r--r--tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp5
-rw-r--r--tests/auto/quick/qquicklistview/data/emptymodel.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml52
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp22
-rw-r--r--tests/auto/quick/qquicklistview2/data/nestedSnap.qml63
-rw-r--r--tests/auto/quick/qquicklistview2/data/visibleBoundToCountGreaterThanZero.qml31
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp82
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp10
-rw-r--r--tests/auto/quick/qquickpathview/data/qtbug46487.qml28
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp63
-rw-r--r--tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp10
-rw-r--r--tests/auto/quick/qquickstates/data/anchorRewindBug3.qml38
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp25
-rw-r--r--tests/auto/quick/qquicktableview/data/invalidateModelContextObject.qml38
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp213
-rw-r--r--tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp52
-rw-r--r--tests/auto/quick/qquicktextedit/data/hAlignVisual.qml6
-rw-r--r--tests/auto/quick/qquicktextedit/data/inFlickable.qml7
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp161
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp19
-rw-r--r--tests/auto/quick/qquickview_extra/data/qtbug_87228.qml2
-rw-r--r--tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp4
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp56
-rw-r--r--tests/auto/quickcontrols/CMakeLists.txt7
-rw-r--r--tests/auto/quickcontrols/controls/basic/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/controls/data/combobox/shader.frag19
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_button.qml17
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_buttongroup.qml6
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_checkbox.qml4
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_combobox.qml42
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_control.qml4
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_delaybutton.qml6
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_radiobutton.qml4
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_splitview.qml99
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml27
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_swipeview.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_switch.qml12
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml12
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tumbler.qml158
-rw-r--r--tests/auto/quickcontrols/controls/fusion/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/controls/imagine/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/controls/ios/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/macos/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/controls/material/CMakeLists.txt5
-rw-r--r--tests/auto/quickcontrols/controls/shared.cmake12
-rw-r--r--tests/auto/quickcontrols/controls/universal/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/controls/windows/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/focus/tst_focus.cpp7
-rw-r--r--tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml32
-rw-r--r--tests/auto/quickcontrols/palette/data/comboBoxPopupWithThemeDefault.qml17
-rw-r--r--tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml33
-rw-r--r--tests/auto/quickcontrols/palette/data/toolTipPaletteUpdate.qml19
-rw-r--r--tests/auto/quickcontrols/palette/tst_palette.cpp125
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml1
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/explicitBackgroundSizeBinding.qml21
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp19
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt39
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithListView.qml58
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithRepeater.qml58
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp107
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp3
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml17
-rw-r--r--tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp240
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp16
-rw-r--r--tests/auto/quickcontrols/qquickpopup/BLACKLIST5
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml27
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml28
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp129
-rw-r--r--tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp10
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp1
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp4
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp20
-rw-r--r--tests/manual/quickcontrols/screenshots/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/screenshots/screenshots.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Menu.qml3
-rw-r--r--tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/testbench.qml6
133 files changed, 3240 insertions, 311 deletions
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 75fdd1cb0c..7c671040a3 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -249,8 +249,6 @@ built-ins/String/prototype/toLocaleLowerCase/special_casing_conditional.js fails
built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js fails
built-ins/String/prototype/toLowerCase/special_casing_conditional.js fails
built-ins/TypedArray/prototype/constructor.js fails
-built-ins/TypedArray/prototype/fill/fill-values-conversion-operations-consistent-nan.js fails
-built-ins/TypedArray/prototype/slice/bit-precision.js fails
built-ins/TypedArray/prototype/sort/arraylength-internal.js fails
built-ins/TypedArray/prototype/sort/comparefn-call-throws.js fails
built-ins/TypedArray/prototype/sort/comparefn-calls.js fails
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 2c5dc7af43..f9bd5c28aa 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -314,6 +314,11 @@ private slots:
void deleteDefineCycle();
void deleteFromSparseArray();
+ void generatorFunctionInTailCallPosition();
+ void generatorMethodInTailCallPosition();
+
+ void consoleLogSequence();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -6345,6 +6350,71 @@ void tst_QJSEngine::deleteFromSparseArray()
QVERIFY(result.property(20000).isUndefined());
}
+void tst_QJSEngine::generatorFunctionInTailCallPosition() {
+ QJSEngine engine;
+ QJSValue result = engine.evaluate(R"(
+ "use strict";
+ function* gen() {
+ yield 0;
+ }
+ function caller() { return gen(); }
+ caller();
+ )");
+
+ QVERIFY(!result.isError());
+ QVERIFY(!result.isUndefined());
+}
+
+void tst_QJSEngine::generatorMethodInTailCallPosition() {
+ QJSEngine engine;
+ QJSValue result = engine.evaluate(R"(
+ "use strict";
+ class Class {
+ *gen() {
+ yield 0;
+ }
+
+ caller() { return this.gen(); }
+ }
+ var c = new Class();
+ c.caller();
+ )");
+
+ QVERIFY(!result.isError());
+ QVERIFY(!result.isUndefined());
+}
+
+static unsigned stringListFetchCount = 0;
+class StringListProvider : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringList strings READ strings CONSTANT)
+
+public:
+ QStringList strings() const
+ {
+ ++stringListFetchCount;
+ QStringList ret;
+ for (int i = 0; i < 10; ++i)
+ ret.append(QString::number(i));
+ return ret;
+ }
+};
+
+void tst_QJSEngine::consoleLogSequence()
+{
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::ConsoleExtension);
+
+ engine.globalObject().setProperty(
+ QStringLiteral("object"), engine.newQObject(new StringListProvider));
+
+ QTest::ignoreMessage(QtDebugMsg, "[0,1,2,3,4,5,6,7,8,9]");
+
+ engine.evaluate(QStringLiteral("console.log(object.strings)"));
+ QCOMPARE(stringListFetchCount, 1);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml b/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml
new file mode 100644
index 0000000000..bbf978936f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ enum AxisAlignment {
+ Bottom = 0,
+ Left = 1,
+ Right = 2
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/enumWithValues.qml b/tests/auto/qml/qmlformat/data/enumWithValues.qml
new file mode 100644
index 0000000000..2dbe7fbac5
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/enumWithValues.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle{
+enum AxisAlignment{
+Bottom = 0,
+Left = 1,
+Right = 2
+}
+}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 0c3a2a276c..877cd92f95 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -333,6 +333,9 @@ void TestQmlformat::testFormat_data()
QTest::newRow("javascriptBlock")
<< "javascriptBlock.qml"
<< "javascriptBlock.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("enumWithValues")
+ << "enumWithValues.qml"
+ << "enumWithValues.formatted.qml" << QStringList{} << RunOption::OnCopy;
}
void TestQmlformat::testFormat()
diff --git a/tests/auto/qml/qmllint/CMakeLists.txt b/tests/auto/qml/qmllint/CMakeLists.txt
index 422e7a08b3..dafac26475 100644
--- a/tests/auto/qml/qmllint/CMakeLists.txt
+++ b/tests/auto/qml/qmllint/CMakeLists.txt
@@ -48,3 +48,10 @@ if (TARGET qmljsrootgen)
QT_QMLJSROOTGEN_PRESENT
)
endif()
+
+if(QT_FEATURE_process)
+ add_subdirectory(importRelScript)
+ add_dependencies(tst_qmllint tst_qmllint_import_rel_script)
+ target_compile_definitions(tst_qmllint PUBLIC
+ TST_QMLLINT_IMPORT_REL_SCRIPT_ARGS="@${CMAKE_CURRENT_BINARY_DIR}/importRelScript/.rcc/qmllint/tst_qmllint_import_rel_script.rsp")
+endif()
diff --git a/tests/auto/qml/qmllint/data/something.qml b/tests/auto/qml/qmllint/data/something.qml
new file mode 100644
index 0000000000..38998f606d
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/something.qml
@@ -0,0 +1,2 @@
+import ModuleInImportPath
+A {}
diff --git a/tests/auto/qml/qmllint/importRelScript/CMakeLists.txt b/tests/auto/qml/qmllint/importRelScript/CMakeLists.txt
new file mode 100644
index 0000000000..4b8f26b5bf
--- /dev/null
+++ b/tests/auto/qml/qmllint/importRelScript/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_policy(SET QTP0001 NEW)
+
+set(CMAKE_AUTOMOC ON)
+
+qt_add_qml_module(tst_qmllint_import_rel_script
+ URI ImportRelScript
+ QML_FILES
+ Main.qml
+ script.js
+ PLUGIN_TARGET
+ tst_qmllint_import_rel_script
+)
+
+qt_autogen_tools_initial_setup(tst_qmllint_import_rel_script)
+add_dependencies(tst_qmllint_import_rel_script Qt::qmllint)
diff --git a/tests/auto/qml/qmllint/importRelScript/Main.qml b/tests/auto/qml/qmllint/importRelScript/Main.qml
new file mode 100644
index 0000000000..341f1d730c
--- /dev/null
+++ b/tests/auto/qml/qmllint/importRelScript/Main.qml
@@ -0,0 +1,6 @@
+import QtQml
+import "script.js" as JS
+
+QtObject {
+ Component.onCompleted: JS.f()
+}
diff --git a/tests/auto/qml/qmllint/importRelScript/script.js b/tests/auto/qml/qmllint/importRelScript/script.js
new file mode 100644
index 0000000000..27b14ed35c
--- /dev/null
+++ b/tests/auto/qml/qmllint/importRelScript/script.js
@@ -0,0 +1,3 @@
+function f() {
+ return 1
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index c18c9ff326..56e31dba8f 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -97,10 +97,17 @@ private Q_SLOTS:
void testLineEndings();
void ignoreSettingsNotCommandLineOptions();
+ void backslashedQmldirPath();
+
#if QT_CONFIG(library)
void testPlugin();
void quickPlugin();
#endif
+
+#if QT_CONFIG(process)
+ void importRelScript();
+#endif
+
private:
enum DefaultImportOption { NoDefaultImports, UseDefaultImports };
enum ContainOption { StringNotContained, StringContained };
@@ -2076,5 +2083,25 @@ void TestQmllint::ignoreSettingsNotCommandLineOptions()
QCOMPARE(output, QString());
}
+void TestQmllint::backslashedQmldirPath()
+{
+ const QString qmldirPath
+ = testFile(u"ImportPath/ModuleInImportPath/qmldir"_s).replace('/', QDir::separator());
+ const QString output = runQmllint(
+ testFile(u"something.qml"_s), true, QStringList{ u"-i"_s, qmldirPath });
+ QVERIFY(output.isEmpty());
+}
+
+#if QT_CONFIG(process)
+void TestQmllint::importRelScript()
+{
+ QProcess proc;
+ proc.start(m_qmllintPath, { QStringLiteral(TST_QMLLINT_IMPORT_REL_SCRIPT_ARGS) });
+ QVERIFY(proc.waitForFinished());
+ QVERIFY(proc.readAllStandardOutput().isEmpty());
+ QVERIFY(proc.readAllStandardError().isEmpty());
+}
+#endif
+
QTEST_MAIN(TestQmllint)
#include "tst_qmllint.moc"
diff --git a/tests/auto/qml/qqmlcomponent/data/bindingInRequired.qml b/tests/auto/qml/qqmlcomponent/data/bindingInRequired.qml
new file mode 100644
index 0000000000..03d6d0f805
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/bindingInRequired.qml
@@ -0,0 +1,16 @@
+import QtQml
+
+QtObject {
+
+ property Component object: QtObject {
+ property QtObject obj
+ }
+
+ property QtObject outer
+ property QtObject inner
+
+ Component.onCompleted: {
+ inner = object.createObject(this, { obj: null })
+ outer = object.createObject(this, { obj: Qt.binding(() => inner) })
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 288b4d1a01..1ccf7a6f23 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -148,6 +148,7 @@ private slots:
void loadFromQrc();
void removeBinding();
void complexObjectArgument();
+ void bindingInRequired();
private:
QQmlEngine engine;
@@ -1506,6 +1507,24 @@ void tst_qqmlcomponent::complexObjectArgument()
QCOMPARE(o->objectName(), QStringLiteral("26 - 25"));
}
+void tst_qqmlcomponent::bindingInRequired()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("bindingInRequired.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QObject *outer = object->property("outer").value<QObject *>();
+ QVERIFY(outer);
+
+ QObject *inner = object->property("inner").value<QObject *>();
+ QVERIFY(inner);
+
+ QCOMPARE(inner, outer->property("obj").value<QObject *>());
+ QVERIFY(!inner->property("obj").value<QObject *>());
+}
+
QTEST_MAIN(tst_qqmlcomponent)
#include "tst_qqmlcomponent.moc"
diff --git a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
index 705ee6f357..edf0a1165b 100644
--- a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
@@ -23,6 +23,7 @@ qt_internal_add_test(tst_qqmldelegatemodel
Qt::QmlModelsPrivate
Qt::QmlPrivate
Qt::Quick
+ Qt::QuickPrivate
Qt::QuickTestUtilsPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qml/qqmldelegatemodel/data/reset.qml b/tests/auto/qml/qqmldelegatemodel/data/reset.qml
new file mode 100644
index 0000000000..0fcd5e8afa
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/reset.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import Test
+
+Window {
+ id: root
+ width: 200
+ height: 200
+
+ property alias listView: listView
+
+ ResettableModel {
+ id: resetModel
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: resetModel
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+ color: "olivedrab"
+
+ required property string display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml b/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
new file mode 100644
index 0000000000..cb1f226737
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import Test
+
+Window {
+ id: root
+ width: 200
+ height: 200
+
+ property alias listView: listView
+
+ ResetInConstructorModel {
+ id: resetInConstructorModel
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: resetInConstructorModel
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+ color: "olivedrab"
+
+ required property string display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index 1f85f68891..e9c22ca1e6 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -3,6 +3,7 @@
#include <QtTest/qtest.h>
#include <QtCore/QConcatenateTablesProxyModel>
+#include <QtCore/qtimer.h>
#include <QtGui/QStandardItemModel>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlapplicationengine.h>
@@ -10,9 +11,16 @@
#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuickTest/quicktest.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtTest/QSignalSpy>
+
+using namespace QQuickVisualTestUtils;
+
class tst_QQmlDelegateModel : public QQmlDataTest
{
Q_OBJECT
@@ -22,6 +30,8 @@ public:
private slots:
void resettingRolesRespected();
+ void resetInQAIMConstructor();
+ void reset();
void valueWithoutCallingObjectFirst_data();
void valueWithoutCallingObjectFirst();
void qtbug_86017();
@@ -36,16 +46,9 @@ private slots:
void clearCacheDuringInsertion();
};
-class AbstractItemModel : public QAbstractItemModel
+class BaseAbstractItemModel : public QAbstractItemModel
{
- Q_OBJECT
public:
- AbstractItemModel()
- {
- for (int i = 0; i < 3; ++i)
- mValues.append(QString::fromLatin1("Item %1").arg(i));
- }
-
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid())
@@ -83,10 +86,21 @@ public:
return mValues.at(index.row());
}
-private:
+protected:
QVector<QString> mValues;
};
+class AbstractItemModel : public BaseAbstractItemModel
+{
+ Q_OBJECT
+public:
+ AbstractItemModel()
+ {
+ for (int i = 0; i < 3; ++i)
+ mValues.append(QString::fromLatin1("Item %1").arg(i));
+ }
+};
+
tst_QQmlDelegateModel::tst_QQmlDelegateModel()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
@@ -145,7 +159,109 @@ void tst_QQmlDelegateModel::resettingRolesRespected()
QObject *root = engine.rootObjects().constFirst();
QVERIFY(!root->property("success").toBool());
model->change();
- QTRY_VERIFY(root->property("success").toBool());
+ QTRY_VERIFY_WITH_TIMEOUT(root->property("success").toBool(), 100);
+}
+
+class ResetInConstructorModel : public BaseAbstractItemModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ ResetInConstructorModel()
+ {
+ beginResetModel();
+ QTimer::singleShot(0, this, &ResetInConstructorModel::finishReset);
+ }
+
+private:
+ void finishReset()
+ {
+ mValues.append("First");
+ endResetModel();
+ }
+};
+
+void tst_QQmlDelegateModel::resetInQAIMConstructor()
+{
+ qmlRegisterTypesAndRevisions<ResetInConstructorModel>("Test", 1);
+
+ QQuickApplicationHelper helper(this, "resetInQAIMConstructor.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *listView = window->property("listView").value<QQuickListView *>();
+ QVERIFY(listView);
+ QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
+ QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
+ QVERIFY(firstDelegateItem);
+ QCOMPARE(firstDelegateItem->property("display").toString(), "First");
+}
+
+class ResettableModel : public BaseAbstractItemModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ ResettableModel()
+ {
+ mValues.append("First");
+ }
+
+ void callBeginResetModel()
+ {
+ beginResetModel();
+ mValues.clear();
+ }
+
+ void appendData()
+ {
+ mValues.append(QString::fromLatin1("Item %1").arg(mValues.size()));
+ }
+
+ void callEndResetModel()
+ {
+ endResetModel();
+ }
+};
+
+// Tests that everything works as expected when calling beginResetModel/endResetModel
+// after the QAIM subclass constructor has run.
+void tst_QQmlDelegateModel::reset()
+{
+ qmlRegisterTypesAndRevisions<ResettableModel>("Test", 1);
+
+ QQuickApplicationHelper helper(this, "reset.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *listView = window->property("listView").value<QQuickListView *>();
+ QVERIFY(listView);
+ QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
+ QVERIFY(firstDelegateItem);
+ QCOMPARE(firstDelegateItem->property("display").toString(), "First");
+
+ const auto delegateModel = QQuickItemViewPrivate::get(listView)->model;
+ QSignalSpy rootIndexChangedSpy(delegateModel, SIGNAL(rootIndexChanged()));
+ QVERIFY(rootIndexChangedSpy.isValid());
+
+ auto *model = listView->model().value<ResettableModel *>();
+ model->callBeginResetModel();
+ model->appendData();
+ model->callEndResetModel();
+ // This is verifies that handleModelReset isn't called
+ // more than once during this process, since it unconditionally emits rootIndexChanged.
+ QCOMPARE(rootIndexChangedSpy.count(), 1);
+
+ QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
+ firstDelegateItem = listView->itemAtIndex(0);
+ QVERIFY(firstDelegateItem);
+ QCOMPARE(firstDelegateItem->property("display").toString(), "Item 0");
}
void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data()
diff --git a/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml
new file mode 100644
index 0000000000..321bd21ad8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml
@@ -0,0 +1,29 @@
+import QtQml
+
+QtObject {
+ function test_proxy() {
+ let base = {
+ id: 'baseid',
+ name: 'basename',
+ length: 42
+ };
+
+ let handler = {
+ get: function (ao, prop) {
+ return Reflect.get(ao, prop);
+ }
+ };
+
+ let r = new Proxy(base, handler);
+ let validCount = 0;
+ if (r.id === base.id)
+ ++validCount;
+ if (r.length === base.length)
+ ++validCount;
+ if (r.name === base.name)
+ ++validCount;
+ return validCount;
+ }
+
+ property int result: test_proxy()
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 4023824f75..63655fa60d 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -400,6 +400,7 @@ private slots:
void sequenceConversionMethod();
void proxyIteration();
void proxyHandlerTraps();
+ void lookupsDoNotBypassProxy();
void gcCrashRegressionTest();
void cmpInThrows();
void frozenQObject();
@@ -921,7 +922,9 @@ void tst_qqmlecmascript::bindingLoop()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
- QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
+ const auto urlString = component.url().toString();
+ const QString warning = urlString + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\":\n"
+ + urlString + ":11:13";
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
@@ -10056,6 +10059,17 @@ void tst_qqmlecmascript::proxyHandlerTraps()
QVERIFY(value.isString() && value.toString() == QStringLiteral("SUCCESS"));
}
+void tst_qqmlecmascript::lookupsDoNotBypassProxy()
+{
+ QQmlEngine engine;
+ // we need a component to have a proper compilation to byte code;
+ // otherwise, we don't actually end up with lookups
+ QQmlComponent comp(&engine, testFileUrl("lookupsDoNotBypassProxy.qml"));
+ QVERIFY(comp.isReady());
+ std::unique_ptr<QObject> obj { comp.create() };
+ QCOMPARE(obj->property("result").toInt(), 3);
+}
+
void tst_qqmlecmascript::cmpInThrows()
{
QJSEngine engine;
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 00d28cfe60..b82a1f4174 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -1337,6 +1337,8 @@ void tst_qqmlengine::createComponentOnSingletonDestruction()
void tst_qqmlengine::uiLanguage()
{
+ const QRegularExpression bindingLoopWarningRegex(".*QML QtObject: Binding loop detected for property \"textToTranslate\".*");
+
{
QQmlEngine engine;
@@ -1348,19 +1350,19 @@ void tst_qqmlengine::uiLanguage()
QQmlComponent component(&engine, testFileUrl("uiLanguage.qml"));
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, bindingLoopWarningRegex);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
QVERIFY(engine.uiLanguage().isEmpty());
QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 1);
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, bindingLoopWarningRegex);
engine.setUiLanguage("TestLanguage");
QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 2);
QCOMPARE(object->property("chosenLanguage").toString(), "TestLanguage");
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, bindingLoopWarningRegex);
engine.evaluate("Qt.uiLanguage = \"anotherLanguage\"");
QCOMPARE(engine.uiLanguage(), QString("anotherLanguage"));
QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 3);
@@ -1371,7 +1373,7 @@ void tst_qqmlengine::uiLanguage()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("uiLanguage.qml"));
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, bindingLoopWarningRegex);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
diff --git a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
index 4908ca210b..46154640d0 100644
--- a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
+++ b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
@@ -220,7 +220,8 @@ void tst_qqmlinfo::attachedObject()
const QString qmlBindingLoopMessageFull = component.url().toString() + ":8:9: " + qmlBindingLoopMessage;
QTest::ignoreMessage(QtWarningMsg, qPrintable(qmlBindingLoopMessageFull));
- const QString cppBindingLoopMessage = "QML AttachedObject (parent or ancestor of Attached): Binding loop detected for property \"a\"";
+ const QString cppBindingLoopMessage = "QML AttachedObject (parent or ancestor of Attached): Binding loop detected for property \"a\":\n"
+ + component.url().toString() + ":5:5";
const QString cppBindingLoopMessageFull = component.url().toString() + ":4:1: " + cppBindingLoopMessage;
QTest::ignoreMessage(QtWarningMsg, qPrintable(cppBindingLoopMessageFull));
diff --git a/tests/auto/qml/qqmllanguage/CMakeLists.txt b/tests/auto/qml/qqmllanguage/CMakeLists.txt
index e07f741bf6..fc9bbf17df 100644
--- a/tests/auto/qml/qqmllanguage/CMakeLists.txt
+++ b/tests/auto/qml/qqmllanguage/CMakeLists.txt
@@ -27,6 +27,8 @@ qt_internal_add_test(tst_qqmllanguage
TESTDATA ${test_data}
)
+add_subdirectory(testhelper)
+
#### Keys ignored in scope 1:.:.:qqmllanguage.pro:<TRUE>:
# OTHER_FILES = "data/readonlyObjectProperty.qml"
# QML_IMPORT_NAME = "StaticTest"
diff --git a/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
new file mode 100644
index 0000000000..de7e1e96a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
@@ -0,0 +1,5 @@
+import QtQml 2.15
+
+SimpleWidget {
+ width: 20
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
new file mode 100644
index 0000000000..9150ebaa4e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.15
+
+Item {
+ id: outer
+
+ property real innerWidth: 0
+
+ Item {
+ id: inner
+ width: style.width
+ onWidthChanged: outer.innerWidth = width
+ }
+
+ width: inner.width
+
+ onWidthChanged: {
+ if (width !== inner.width)
+ inner.width = width // overwrite binding
+ }
+
+ QtObject {
+ id: style
+ property int width: 50
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml
new file mode 100644
index 0000000000..f549e851a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQml
+
+QtObject {
+ required property int i
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml
new file mode 100644
index 0000000000..1f9e7e3a42
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+import QtQml
+
+QtObject {
+ property QtObject o: QtObject {
+ required property int i
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir
new file mode 100644
index 0000000000..46e397ca76
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir
@@ -0,0 +1,4 @@
+module SingletonWithRequiredProperties
+
+singleton SingletonWithRequired1 1.0 SingletonWithRequired1.qml
+singleton SingletonWithRequired2 1.0 SingletonWithRequired2.qml
diff --git a/tests/auto/qml/qqmllanguage/data/nestedVectors.qml b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml
new file mode 100644
index 0000000000..0bcea52133
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml
@@ -0,0 +1,27 @@
+import Test
+import QtQml
+
+NestedVectors {
+ id: self
+
+ property var list1
+
+ Component.onCompleted: {
+ list1 = self.getList()
+
+ let list2 = []
+ let data1 = []
+ data1.push(2)
+ data1.push(3)
+ data1.push(4)
+
+ let data2 = []
+ data2.push(5)
+ data2.push(6)
+
+ list2.push(data1)
+ list2.push(data2)
+
+ self.setList(list2)
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt b/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt
new file mode 100644
index 0000000000..6a58889335
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt
@@ -0,0 +1,13 @@
+qt_policy(SET QTP0001 NEW)
+qt_add_library(tst_qqmllanguage_qmlmodule STATIC)
+qt_autogen_tools_initial_setup(tst_qqmllanguage_qmlmodule)
+qt_add_qml_module(tst_qqmllanguage_qmlmodule
+ URI testhelper
+ VERSION 1.0
+ SOURCES
+ "declarativelyregistered.h"
+ "declarativelyregistered.cpp"
+)
+
+qt_autogen_tools_initial_setup(tst_qqmllanguage_qmlmoduleplugin)
+target_link_libraries(tst_qqmllanguage PRIVATE tst_qqmllanguage_qmlmoduleplugin)
diff --git a/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp
new file mode 100644
index 0000000000..24fcd83d42
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "declarativelyregistered.h"
+
+PurelyDeclarativeSingleton::PurelyDeclarativeSingleton() = default;
+
+#include "moc_declarativelyregistered.cpp"
diff --git a/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h
new file mode 100644
index 0000000000..4845cc68b9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DECLARATIVELYREGISTERED_LANGUAGE_H
+#define DECLARATIVELYREGISTERED_LANGUAGE_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqmlregistration.h>
+
+class PurelyDeclarativeSingleton : public QObject
+{
+ Q_OBJECT
+ QML_SINGLETON
+ QML_ELEMENT
+public:
+ PurelyDeclarativeSingleton();
+};
+
+
+#endif
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index e796fb6cd5..12fe042c20 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -160,6 +160,7 @@ void registerTypes()
qmlRegisterTypesAndRevisions<ByteArrayReceiver>("Test", 1);
qmlRegisterTypesAndRevisions<Counter>("Test", 1);
+ qmlRegisterTypesAndRevisions<NestedVectors>("Test", 1);
}
QVariant myCustomVariantTypeConverter(const QString &data)
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index fe1a825e87..bfee2d07c9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -2608,4 +2608,36 @@ public:
}
};
+class NestedVectors : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ NestedVectors(QObject *parent = nullptr) : QObject(parent)
+ {
+ std::vector<int> data;
+ data.push_back(1);
+ data.push_back(2);
+ data.push_back(3);
+ m_list.push_back(data);
+ data.clear();
+ data.push_back(4);
+ data.push_back(5);
+ m_list.push_back(data);
+ }
+
+ Q_INVOKABLE std::vector<std::vector<int>> getList()
+ {
+ return m_list;
+ }
+
+ Q_INVOKABLE void setList(std::vector<std::vector<int>> list)
+ {
+ m_list = list;
+ }
+
+private:
+ std::vector<std::vector<int>> m_list;
+};
+
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 7dd68df62e..7992896506 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -248,6 +248,8 @@ private slots:
void compositeSingletonSelectors();
void compositeSingletonRegistered();
void compositeSingletonCircular();
+ void compositeSingletonRequiredProperties();
+ void compositeSingletonRequiredProperties_data();
void singletonsHaveContextAndEngine();
@@ -429,6 +431,10 @@ private slots:
void typedObjectList();
+ void nestedVectors();
+
+ void overrideInnerBinding();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -1920,9 +1926,9 @@ void tst_qqmllanguage::valueTypes()
QQmlComponent component(&engine, testFileUrl("valueTypes.qml"));
VERIFY_ERRORS(0);
- QString message = component.url().toString() + ":2:1: QML MyTypeObject: Binding loop detected for property \"rectProperty.width\"";
- QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ const auto bindingLoopRegex = QRegularExpression(".*QML MyTypeObject: Binding loop detected for property \"rectProperty.width\".*");
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopRegex);
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopRegex);
QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create()));
QVERIFY(object != nullptr);
@@ -4867,6 +4873,36 @@ void tst_qqmllanguage::compositeSingletonCircular()
QCOMPARE(o->property("value").toInt(), 2);
}
+void tst_qqmllanguage::compositeSingletonRequiredProperties()
+{
+ QFETCH(QString, warning);
+ QFETCH(QString, singletonName);
+ QQmlEngine engine;
+ engine.addImportPath(dataDirectory());
+ {
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning));
+ std::unique_ptr<QObject> singleton {engine.singletonInstance<QObject *>(
+ "SingletonWithRequiredProperties",
+ singletonName
+ )};
+ QVERIFY(!singleton);
+ }
+}
+
+void tst_qqmllanguage::compositeSingletonRequiredProperties_data()
+{
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("singletonName");
+
+ QString warning1 = testFileUrl("SingletonWithRequiredProperties/SingletonWithRequired1.qml").toString()
+ + ":5:5: Required property i was not initialized";
+ QString warning2 = testFileUrl("SingletonWithRequiredProperties/SingletonWithRequired2.qml").toString()
+ + ":6:9: Required property i was not initialized";
+
+ QTest::addRow("toplevelRequired") << warning1 << "SingletonWithRequired1";
+ QTest::addRow("subObjectRequired") << warning2 << "SingletonWithRequired2";
+}
+
void tst_qqmllanguage::singletonsHaveContextAndEngine()
{
QObject *qmlSingleton = nullptr;
@@ -5677,6 +5713,9 @@ void tst_qqmllanguage::retrieveQmlTypeId()
QVERIFY(qmlTypeId("Test", 1, 0, "MyExtendedUncreateableBaseClass") >= 0);
QVERIFY(qmlTypeId("Test", 1, 0, "MyUncreateableBaseClass") >= 0);
QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0);
+
+ // Must also work for declaratively registered types whose module wasn't imported so far
+ QVERIFY(qmlTypeId("testhelper", 1, 0, "PurelyDeclarativeSingleton") >= 0);
}
void tst_qqmllanguage::polymorphicFunctionLookup()
@@ -8194,6 +8233,37 @@ void tst_qqmllanguage::typedObjectList()
QVERIFY(list.at(&list, 0) != nullptr);
}
+void tst_qqmllanguage::nestedVectors()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("nestedVectors.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ NestedVectors *n = qobject_cast<NestedVectors *>(o.data());
+ QVERIFY(n);
+
+ const std::vector<std::vector<int>> expected1 { { 1, 2, 3 }, { 4, 5 } };
+ const QVariant list1 = n->property("list1");
+ QCOMPARE(e.fromVariant<std::vector<std::vector<int>>>(list1), expected1);
+
+ const std::vector<std::vector<int>> expected2 { { 2, 3, 4 }, { 5, 6 } };
+ QCOMPARE(n->getList(), expected2);
+}
+
+void tst_qqmllanguage::overrideInnerBinding()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("BindingOverrider.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("width").toReal(), 20.0);
+ QCOMPARE(o->property("innerWidth").toReal(), 20.0);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/data/deadModelData.qml b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
new file mode 100644
index 0000000000..fc3dd6fa11
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
@@ -0,0 +1,45 @@
+import QtQml
+
+QtObject {
+ function swapCorpses() {
+ const lhsData = getModelData(lhsButtonListModel);
+ const rhsData = getModelData(rhsButtonListModel);
+
+ lhsButtonListModel.clear();
+ rhsButtonListModel.clear();
+
+ addToModel(lhsButtonListModel, rhsData);
+ addToModel(rhsButtonListModel, lhsData);
+ }
+
+ property ListModel l1: ListModel {
+ id: lhsButtonListModel
+ }
+
+ property ListModel l2: ListModel {
+ id: rhsButtonListModel
+ }
+
+ Component.onCompleted: {
+ lhsButtonListModel.append({ "ident": 1, "buttonText": "B 1"});
+ lhsButtonListModel.append({ "ident": 2, "buttonText": "B 2"});
+ lhsButtonListModel.append({ "ident": 3, "buttonText": "B 3"});
+
+ rhsButtonListModel.append({ "ident": 4, "buttonText": "B 4"});
+ rhsButtonListModel.append({ "ident": 5, "buttonText": "B 5"});
+ rhsButtonListModel.append({ "ident": 6, "buttonText": "B 6"});
+ }
+
+ function getModelData(model) {
+ var dataList = []
+ for (var i = 0; i < model.count; ++i)
+ dataList.push(model.get(i));
+
+ return dataList;
+ }
+
+ function addToModel(model, buttonData) {
+ for (var i = 0; i < buttonData.length; ++i)
+ model.append(buttonData[i]);
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 6fd173fb0c..8f0e657e54 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -119,6 +119,7 @@ private slots:
void objectOwnershipFlip();
void enumsInListElement();
void protectQObjectFromGC();
+ void deadModelData();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -1931,6 +1932,67 @@ void tst_qqmllistmodel::protectQObjectFromGC()
}
}
+void tst_qqmllistmodel::deadModelData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("deadModelData.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QQmlListModel *l1 = o->property("l1").value<QQmlListModel *>();
+ QVERIFY(l1);
+ QQmlListModel *l2 = o->property("l2").value<QQmlListModel *>();
+ QVERIFY(l2);
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), i + 1);
+ QCOMPARE(i1->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 1)));
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), i + 4);
+ QCOMPARE(i2->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 4)));
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: ident is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: buttonText is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ }
+
+ QMetaObject::invokeMethod(o.data(), "swapCorpses");
+
+ // We get default-created values for all the roles now.
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), double());
+ QCOMPARE(i1->property("buttonText").value<QString>(), QString());
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), double());
+ QCOMPARE(i2->property("buttonText").value<QString>(), QString());
+ }
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qqmllistreference/data/compositeListProp.qml b/tests/auto/qml/qqmllistreference/data/compositeListProp.qml
index 99965f289b..7aa3d0845a 100644
--- a/tests/auto/qml/qqmllistreference/data/compositeListProp.qml
+++ b/tests/auto/qml/qqmllistreference/data/compositeListProp.qml
@@ -1,5 +1,14 @@
import QtQml
QtObject {
- property list<AListItem> items
+ id: self
+ property list<AListItem> items: [ self ]
+
+ Component.onCompleted: {
+ items.push(self);
+ items.push(null);
+ items[2] = self;
+ items.splice(1, 0, self);
+ items.unshift(self);
+ }
}
diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
index 66c17aa0d5..4d633bb3ab 100644
--- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
+++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
@@ -862,9 +862,31 @@ void tst_qqmllistreference::compositeListProperty()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("compositeListProp.qml"));
+
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("Cannot append QObject_QML_[0-9]+\\(0x[0-9a-f]+\\) "
+ "to a QML list of AListItem_QMLTYPE_[0-9]+\\*"));
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("Cannot append QObject_QML_[0-9]+\\(0x[0-9a-f]+\\) "
+ "to a QML list of AListItem_QMLTYPE_[0-9]+\\*"));
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("Cannot insert QObject_QML_[0-9]+\\(0x[0-9a-f]+\\) "
+ "into a QML list of AListItem_QMLTYPE_[0-9]+\\*"));
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("Cannot splice QObject_QML_[0-9]+\\(0x[0-9a-f]+\\) "
+ "into a QML list of AListItem_QMLTYPE_[0-9]+\\*"));
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("Cannot unshift QObject_QML_[0-9]+\\(0x[0-9a-f]+\\) "
+ "into a QML list of AListItem_QMLTYPE_[0-9]+\\*"));
+
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
+ QQmlListReference list1(object.data(), "items");
+ QCOMPARE(list1.size(), 5);
+ for (qsizetype i = 0; i < 5; ++i)
+ QCOMPARE(list1.at(i), nullptr);
+
QQmlComponent item(&engine, testFileUrl("AListItem.qml"));
QScopedPointer<QObject> i1(item.create());
QScopedPointer<QObject> i2(item.create());
@@ -872,7 +894,6 @@ void tst_qqmllistreference::compositeListProperty()
QVERIFY(!i2.isNull());
// We know the element type now.
- QQmlListReference list1(object.data(), "items");
QVERIFY(list1.listElementType() != nullptr);
QVERIFY(list1.append(i1.data()));
QVERIFY(list1.replace(0, i2.data()));
diff --git a/tests/auto/qml/qqmltablemodel/CMakeLists.txt b/tests/auto/qml/qqmltablemodel/CMakeLists.txt
index ebd3e94835..2800fd642c 100644
--- a/tests/auto/qml/qqmltablemodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmltablemodel/CMakeLists.txt
@@ -23,6 +23,7 @@ qt_internal_add_test(tst_qqmltablemodel
Qt::Quick
Qt::QuickPrivate
Qt::QuickTestUtilsPrivate
+ Qt::LabsQmlModelsPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
index cd6eb3e9c5..860c782fd3 100644
--- a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
+++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
@@ -13,6 +13,7 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtLabsQmlModels/private/qqmltablemodel_p.h>
class tst_QQmlTableModel : public QQmlDataTest
{
@@ -179,6 +180,18 @@ void tst_QQmlTableModel::appendRemoveRow()
QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant());
QCOMPARE(columnCountSpy.size(), 0);
QCOMPARE(rowCountSpy.size(), ++rowCountSignalEmissions);
+
+ QVariantMap variantMap;
+ variantMap["name"] = QLatin1String("VariantMap");
+ variantMap["age"] = int(QMetaType::QVariantMap);
+ auto *tableModel = view.rootObject()->property("testModel") .value<QQmlTableModel *>();
+ tableModel->appendRow(QVariant::fromValue(variantMap));
+ QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("VariantMap"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), int(QMetaType::QVariantMap));
+ QCOMPARE(columnCountSpy.size(), 0);
+ QCOMPARE(rowCountSpy.size(), ++rowCountSignalEmissions);
}
void tst_QQmlTableModel::appendRowToEmptyModel()
@@ -432,6 +445,31 @@ void tst_QQmlTableModel::insertRow()
QCOMPARE(rowCountSpy.size(), ++rowCountSignalEmissions);
QTRY_COMPARE(tableView->rows(), 5);
QCOMPARE(tableView->columns(), 2);
+
+ // Pass variant map object to qml table model
+ QVariantMap variantMap;
+ variantMap["name"] = QLatin1String("VariantMap");
+ variantMap["age"] = int(QMetaType::QVariantMap);
+ auto *tableModel = view.rootObject()->property("testModel") .value<QQmlTableModel *>();
+ tableModel->insertRow(5, QVariant::fromValue(variantMap));
+ QCOMPARE(model->rowCount(), 6);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(5, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("VariantMap"));
+ QCOMPARE(model->data(model->index(5, 1, QModelIndex()), roleNames.key("display")).toInt(), int(QMetaType::QVariantMap));
+ QCOMPARE(columnCountSpy.size(), 0);
+ QCOMPARE(rowCountSpy.size(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 6);
+ QCOMPARE(tableView->columns(), 2);
}
void tst_QQmlTableModel::moveRow()
@@ -716,6 +754,24 @@ void tst_QQmlTableModel::setRow()
QCOMPARE(rowCountSpy.size(), ++rowCountSignalEmissions);
QTRY_COMPARE(tableView->rows(), 3);
QCOMPARE(tableView->columns(), 2);
+
+ QVariantMap variantMap;
+ variantMap["name"] = QLatin1String("VariantMap");
+ variantMap["age"] = int(QMetaType::QVariantMap);
+ auto *tableModel = view.rootObject()->property("testModel") .value<QQmlTableModel *>();
+ tableModel->setRow(0, QVariant::fromValue(variantMap));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("VariantMap"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), int(QMetaType::QVariantMap));
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Wot"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.size(), 0);
+ QCOMPARE(rowCountSpy.size(), rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
}
void tst_QQmlTableModel::setDataThroughDelegate()
diff --git a/tests/auto/qml/qv4estable/tst_qv4estable.cpp b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
index 45df62b23e..7d137ae7d2 100644
--- a/tests/auto/qml/qv4estable/tst_qv4estable.cpp
+++ b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
@@ -18,7 +18,7 @@ void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow()
QV4::ESTable estable;
// Fill the ESTable with values so it is at max capacity.
- QCOMPARE_EQ(estable.m_capacity, 8);
+ QCOMPARE_EQ(estable.m_capacity, 8U);
for (uint i = 0; i < estable.m_capacity; ++i) {
estable.set(QV4::Value::fromUInt32(i), QV4::Value::fromUInt32(i));
}
@@ -27,8 +27,8 @@ void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow()
for (uint i = 0; i < estable.m_capacity; ++i) {
QVERIFY(estable.m_keys[i].sameValueZero(QV4::Value::fromUInt32(i)));
}
- QCOMPARE_EQ(estable.m_capacity, 8);
- QCOMPARE_EQ(estable.m_size, 8);
+ QCOMPARE_EQ(estable.m_capacity, 8U);
+ QCOMPARE_EQ(estable.m_size, 8U);
// Remove the first item from the set to verify that asan does not trip.
// Relies on the CI platform propagating asan flag to all tests.
diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml
index e9c91e10f2..039114114b 100644
--- a/tests/auto/qmltest/listview/tst_listview.qml
+++ b/tests/auto/qmltest/listview/tst_listview.qml
@@ -38,12 +38,16 @@ Item {
ListView {
id: modelchange
+ width: 100
+ height: 100
model: firstmodel
delegate: Text { text: model.name }
}
ListView {
id: modelalter
+ width: 100
+ height: 100
model: altermodel
delegate: Text { text: model.name }
}
diff --git a/tests/auto/qmltest/selftests/tst_findChild.qml b/tests/auto/qmltest/selftests/tst_findChild.qml
index c8af04810a..3232191150 100644
--- a/tests/auto/qmltest/selftests/tst_findChild.qml
+++ b/tests/auto/qmltest/selftests/tst_findChild.qml
@@ -71,6 +71,8 @@ TestCase {
}
ListView {
+ width: 100
+ height: 100
model: 5
delegate: Item {
objectName: "listViewItem" + index
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp b/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp
index fdc8981932..4c6e331efa 100644
--- a/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp
@@ -60,6 +60,7 @@ void tst_HowToQml::activeFocusDebugging()
auto *window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
window->show();
+ window->requestActivate();
QTest::ignoreMessage(QtDebugMsg, QRegularExpression("activeFocusItem: .*\"ActiveFocusDebuggingMain\""));
QVERIFY(QTest::qWaitForWindowActive(window));
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 0a126a13e8..ce5473c8a5 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -101,6 +101,7 @@ private slots:
void restartAnimationGroupWhenDirty();
void restartNestedAnimationGroupWhenDirty();
void targetsDeletedNotRemoved();
+ void alwaysRunToEndSetFalseRestartBug();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -2295,6 +2296,41 @@ void tst_qquickanimations::targetsDeletedNotRemoved()
}
}
+//QTBUG-125224
+void tst_qquickanimations::alwaysRunToEndSetFalseRestartBug()
+{
+ QQuickRectangle rect;
+ QQuickSequentialAnimation sequential;
+ QQuickPropertyAnimation beginAnim;
+ QQuickPropertyAnimation endAnim;
+
+ beginAnim.setTargetObject(&rect);
+ beginAnim.setProperty("x");
+ beginAnim.setTo(200);
+ beginAnim.setDuration(1000);
+
+ endAnim.setTargetObject(&rect);
+ endAnim.setProperty("x");
+ endAnim.setFrom(200);
+ endAnim.setDuration(1000);
+
+ beginAnim.setGroup(&sequential);
+ endAnim.setGroup(&sequential);
+
+ sequential.setLoops(-1);
+ sequential.setAlwaysRunToEnd(true);
+
+ QCOMPARE(sequential.loops(), -1);
+ QVERIFY(sequential.alwaysRunToEnd());
+ sequential.start();
+ sequential.stop();
+ sequential.setAlwaysRunToEnd(false);
+ sequential.start();
+ QCOMPARE(sequential.isRunning(), true);
+ sequential.stop();
+ QCOMPARE(sequential.isRunning(), false);
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index a440c0c2f8..b3633a9dcc 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -246,7 +246,7 @@ void tst_qquickapplication::styleHints()
{
// technically not in QQuickApplication, but testing anyway here
QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; Item { property variant styleHints: Qt.styleHints }", QUrl::fromLocalFile(""));
+ component.setData("import QtQuick 2.0; Item { property variant styleHints: Application.styleHints }", QUrl::fromLocalFile(""));
QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
QVERIFY(item);
QQuickView view;
diff --git a/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp b/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
index 5ce72a747f..d82ebe77c2 100644
--- a/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
+++ b/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
@@ -44,6 +44,9 @@ void tst_QQuickDragAttached::setMimeData_data()
QTest::addRow("text/uri-list, string")
<< makeMap("text/uri-list", QString("https://qt-project.org"))
<< QStringList{"text/uri-list"};
+ QTest::addRow("text/uri-list, RFC2483 string")
+ << makeMap("text/uri-list", QString("https://qt-project.org\r\nhttps://www.test.com"))
+ << QStringList{"text/uri-list"};
QTest::addRow("text/uri-list, strings")
<< makeMap("text/uri-list", QStringList{"file://foo", "https://www.test.com"})
<< QStringList{"text/uri-list"};
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index 343bde5f5f..ca2314c336 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -180,6 +180,11 @@ void tst_qquickimage::imageSource()
QFETCH(bool, cache);
QFETCH(QString, error);
+#if !QT_CONFIG(qml_network)
+ if (remote)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
TestHTTPServer server;
if (remote) {
QVERIFY2(server.listen(), qPrintable(server.errorString()));
@@ -550,6 +555,10 @@ void tst_qquickimage::tiling_QTBUG_6716_data()
void tst_qquickimage::noLoading()
{
+#if !QT_CONFIG(qml_network)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
qRegisterMetaType<QQuickImageBase::Status>();
TestHTTPServer server;
@@ -692,6 +701,10 @@ void tst_qquickimage::sourceSize_QTBUG_16389()
// QTBUG-15690
void tst_qquickimage::nullPixmapPaint()
{
+#if !QT_CONFIG(qml_network)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
QScopedPointer<QQuickView> window(new QQuickView(nullptr));
window->setSource(testFileUrl("nullpixmap.qml"));
window->show();
@@ -714,6 +727,10 @@ void tst_qquickimage::nullPixmapPaint()
void tst_qquickimage::imageCrash_QTBUG_22125()
{
+#if !QT_CONFIG(qml_network)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
TestHTTPServer server;
QVERIFY2(server.listen(), qPrintable(server.errorString()));
server.serveDirectory(dataDirectory(), TestHTTPServer::Delay);
@@ -826,6 +843,10 @@ void tst_qquickimage::sourceSizeChanges()
QTRY_COMPARE(sourceSizeSpy.size(), 3);
// Remote
+#if !QT_CONFIG(qml_network)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
ctxt->setContextProperty("srcImage", server.url("/heart.png"));
QTRY_COMPARE(img->status(), QQuickImage::Ready);
QTRY_COMPARE(sourceSizeSpy.size(), 4);
@@ -959,6 +980,10 @@ void tst_qquickimage::progressAndStatusChanges()
QTRY_COMPARE(statusSpy.size(), 1);
// Loading remote file
+#if !QT_CONFIG(qml_network)
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
ctxt->setContextProperty("srcImage", server.url("/heart.png"));
QTRY_COMPARE(obj->status(), QQuickImage::Loading);
QTRY_COMPARE(obj->progress(), 0.0);
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index 061dea3ad7..1db150e675 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -1527,7 +1527,7 @@ void tst_qquickitem::polishLoopDetection_data()
QTest::newRow("test1.100") << PolishItemSpans({ {1, 100} }) << 0;
QTest::newRow("test1.1002") << PolishItemSpans({ {1, 1002} }) << 3;
- QTest::newRow("test1.2020") << PolishItemSpans({ {1, 2020} }) << 10;
+ QTest::newRow("test1.2020") << PolishItemSpans({ {1, 2020} }) << 5;
QTest::newRow("test5.1") << PolishItemSpans({ {5, 1} }) << 0;
QTest::newRow("test5.10") << PolishItemSpans({ {5, 10} }) << 0;
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index ebd1749e68..f6de1b482c 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -2015,7 +2015,7 @@ void tst_QQuickItem::layoutMirroringIllegalParent()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { LayoutMirroring.enabled: true; LayoutMirroring.childrenInherit: true }", QUrl::fromLocalFile(""));
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1:21: QML QtObject: LayoutDirection attached property only works with Items and Windows");
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1:21: QML QtObject: LayoutMirroring attached property only works with Items and Windows");
QObject *object = component.create();
QVERIFY(object != nullptr);
}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
index 8524366f14..5454cf672b 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -936,7 +936,19 @@ Item {
},
layoutWidth: 0,
expectedWidths: [0]
- }
+ },{
+ tag: "preferred_infinity", // Do not crash/assert when the preferred size is infinity
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 10, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ {minimumWidth: 20, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ ]
+ },
+ layoutWidth: 31, // Important that this is between minimum and preferred width of the layout.
+ expectedWidths: [10, 21] // The result here does not have to be exact. (This
+ // test is mostly concerned about not crashing).
+ }
];
}
@@ -1178,6 +1190,78 @@ Item {
}
Component {
+ id: sizeHintBindingLoopComp
+ Item {
+ id: root
+ anchors.fill: parent
+ property var customWidth: 100
+ RowLayout {
+ id: col
+ Item {
+ id: item
+ implicitHeight: 80
+ implicitWidth: Math.max(col2.implicitWidth, root.customWidth + 20)
+ ColumnLayout {
+ id: col2
+ width: parent.width
+ Item {
+ id: rect
+ implicitWidth: root.customWidth
+ implicitHeight: 80
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function test_sizeHintBindingLoopIssue() {
+ var item = createTemporaryObject(sizeHintBindingLoopComp, container)
+ waitForRendering(item)
+ item.customWidth += 10
+ waitForRendering(item)
+ verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop")
+ LayoutSetup.resetBindingLoopDetectedFlag()
+ }
+
+ Component {
+ id: polishLayoutItemComp
+ Item {
+ anchors.fill: parent
+ implicitHeight: contentLayout.implicitHeight
+ implicitWidth: contentLayout.implicitWidth
+ property alias textLayout: contentLayout
+ RowLayout {
+ width: parent.width
+ height: parent.height
+ ColumnLayout {
+ id: contentLayout
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
+ Layout.maximumWidth: 200
+ Repeater {
+ model: 2
+ Text {
+ Layout.fillWidth: true
+ text: "This is a long text causing line breaks to show the bug."
+ wrapMode: Text.Wrap
+ }
+ }
+ Item {
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+ }
+
+ function test_polishLayoutItemIssue() {
+ var rootItem = createTemporaryObject(polishLayoutItemComp, container)
+ waitForRendering(rootItem)
+ var textItem = rootItem.textLayout.children[1]
+ verify(textItem.y >= rootItem.textLayout.children[0].height)
+ }
+
+ Component {
id: rearrangeNestedLayouts_Component
RowLayout {
id: layout
@@ -1520,8 +1604,8 @@ Item {
compare(rootItem.maxWidth, 66)
// Should not trigger a binding loop
- verify(!BindingLoopDetector.bindingLoopDetected, "Detected binding loop")
- BindingLoopDetector.reset()
+ verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop")
+ LayoutSetup.resetBindingLoopDetectedFlag()
}
diff --git a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
index 26b01b806d..1576a00c81 100644
--- a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
+++ b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
@@ -15,13 +15,12 @@ public:
bool wasBindingLoopDetected() const { return mBindingLoopDetected; }
public slots:
- void reset() { mBindingLoopDetected = false; }
+ void resetBindingLoopDetectedFlag() { mBindingLoopDetected = false; }
void qmlEngineAvailable(QQmlEngine *engine)
{
connect(engine, &QQmlEngine::warnings, this, &Setup::qmlWarnings);
-
- qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "BindingLoopDetector", this);
+ qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "LayoutSetup", this);
}
void qmlWarnings(const QList<QQmlError> &warnings)
diff --git a/tests/auto/quick/qquicklistview/data/emptymodel.qml b/tests/auto/quick/qquicklistview/data/emptymodel.qml
index 3feec691cf..c7f1df31d2 100644
--- a/tests/auto/quick/qquicklistview/data/emptymodel.qml
+++ b/tests/auto/quick/qquicklistview/data/emptymodel.qml
@@ -6,6 +6,8 @@ Rectangle {
}
ListView {
id: list
+ width: 100
+ height: 100
model: model
delegate: Item {
}
diff --git a/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml
new file mode 100644
index 0000000000..0d4c233345
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml
@@ -0,0 +1,52 @@
+import QtQuick
+
+ListView {
+ id: listView
+ width: 240
+ height: 300
+
+ model: ListModel {
+ ListElement { section: "section 1" }
+ ListElement { section: "section 1" }
+ ListElement { section: "section 1" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 2" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 3" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 4" }
+ ListElement { section: "section 5" }
+ ListElement { section: "section 5" }
+ ListElement { section: "section 5" }
+ }
+
+ section.property: "section"
+ section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
+ section.delegate: Rectangle {
+ width: listView.width
+ height: 30
+ Text {
+ anchors.fill: parent
+ text: section
+ }
+ color: "lightblue"
+ }
+
+ snapMode: ListView.SnapToItem
+
+ delegate: Rectangle {
+ width: listView.width
+ height: 30
+ Text {
+ anchors.fill: parent
+ text: index
+ }
+ border {
+ width: 1
+ color: "black"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 46e1254453..747478fc9a 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -171,6 +171,7 @@ private slots:
void headerSnapToItem_data();
void headerSnapToItem();
void snapToItemWithSpacing_QTBUG_59852();
+ void snapToItemWithSectionAtStart();
void snapOneItemResize_QTBUG_43555();
void snapOneItem_data();
void snapOneItem();
@@ -5395,6 +5396,27 @@ void tst_QQuickListView::snapToItemWithSpacing_QTBUG_59852()
releaseView(window);
}
+void tst_QQuickListView::snapToItemWithSectionAtStart() // QTBUG-30768
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("snapToItemWithSectionAtStart.qml")));
+ QQuickListView *listView = qobject_cast<QQuickListView *>(window.rootObject());
+ QTRY_VERIFY(listView);
+
+ // Both sections and elements are 30px high. The list height is 300px, so
+ // it fits exactly 10 elements. We can do some random flicks, but the
+ // content position always MUST be divisible by 30.
+ for (int i = 0; i < 10; ++i) {
+ const bool even = (i % 2 == 0);
+ const QPoint start = even ? QPoint(20, 100 + i * 5) : QPoint(20, 20 + i * 3);
+ const QPoint end = even ? start - QPoint(0, 50 + i * 10) : start + QPoint(0, 50 + i * 5);
+
+ flick(&window, start, end, 180);
+ QTRY_COMPARE(listView->isMoving(), false); // wait until it stops
+ QCOMPARE(int(listView->contentY()) % 30, 0);
+ }
+}
+
static void drag_helper(QWindow *window, QPoint *startPos, const QPoint &delta)
{
QPoint pos = *startPos;
diff --git a/tests/auto/quick/qquicklistview2/data/nestedSnap.qml b/tests/auto/quick/qquicklistview2/data/nestedSnap.qml
new file mode 100644
index 0000000000..d7f064da01
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/nestedSnap.qml
@@ -0,0 +1,63 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+ListView {
+ id: row
+
+ width: 300
+ height: 300
+
+ orientation: Qt.Horizontal
+ snapMode: ListView.SnapOneItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+
+ model: 3
+ delegate: ListView {
+ id: column
+ objectName: "vertical column " + index
+
+ required property int index
+
+ width: 300
+ height: 300
+
+ orientation: Qt.Vertical
+ snapMode: ListView.SnapOneItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+
+ model: 3
+ delegate: Rectangle {
+ id: cell
+
+ required property int index
+
+ width: 300
+ height: 300
+ color: "transparent"
+ border.color: "#000"
+ border.width: 5
+ radius: 15
+
+ Text {
+ anchors.centerIn: parent
+ text: `Row: ${cell.index}`
+ }
+ }
+
+ Text {
+ anchors.verticalCenterOffset: -height
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: `Column: ${column.index}\ncurrentIndex: ${column.currentIndex}`
+ }
+ }
+
+ Text {
+ x: 10; y: 10
+ text: `currentIndex: ${row.currentIndex}`
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/visibleBoundToCountGreaterThanZero.qml b/tests/auto/quick/qquicklistview2/data/visibleBoundToCountGreaterThanZero.qml
new file mode 100644
index 0000000000..e75c779584
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/visibleBoundToCountGreaterThanZero.qml
@@ -0,0 +1,31 @@
+import QtQuick
+import QtQuick.Layouts
+
+ColumnLayout {
+ property alias listView: listView
+
+ ListView {
+ id: listView
+
+ visible: count > 0 // actual defect. countChanged never fires so this never turns true
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: contentHeight // grow with content, initially 0
+
+ model: ListModel {
+ id: idModel
+ }
+
+ delegate: Text {
+ required property string name
+ text: name
+ }
+
+ Timer {
+ running: true
+ interval: 10
+ repeat: true
+ onTriggered: idModel.append({name:"Hello"})
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index c240f36a80..e114cc1591 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
using namespace QQuickViewTestUtils;
using namespace QQuickVisualTestUtils;
+static const int oneSecondInMs = 1000;
+
class tst_QQuickListView2 : public QQmlDataTest
{
Q_OBJECT
@@ -35,6 +37,7 @@ private slots:
void delegateModelRefresh();
void wheelSnap();
void wheelSnap_data();
+ void nestedWheelSnap();
void sectionsNoOverlap();
void metaSequenceAsModel();
@@ -62,6 +65,7 @@ private slots:
void changingOrientationResetsPreviousAxisValues_data();
void changingOrientationResetsPreviousAxisValues();
+ void visibleBoundToCountGreaterThanZero();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
@@ -838,6 +842,67 @@ void tst_QQuickListView2::wheelSnap_data()
<< 210.0;
}
+void tst_QQuickListView2::nestedWheelSnap()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("nestedSnap.qml")));
+
+ quint64 timestamp = 10;
+ auto sendWheelEvent = [&timestamp, &window](const QPoint &pixelDelta, Qt::ScrollPhase phase) {
+ const QPoint pos(100, 100);
+ QWheelEvent event(pos, window.mapToGlobal(pos), pixelDelta, pixelDelta, Qt::NoButton,
+ Qt::NoModifier, phase, false, Qt::MouseEventSynthesizedBySystem);
+ event.setAccepted(false);
+ event.setTimestamp(timestamp);
+ QGuiApplication::sendEvent(&window, &event);
+ timestamp += 50;
+ };
+
+ QQuickListView *outerListView = qobject_cast<QQuickListView *>(window.rootObject());
+ QTRY_VERIFY(outerListView);
+ QSignalSpy outerCurrentIndexSpy(outerListView, &QQuickListView::currentIndexChanged);
+ int movingAtIndex = -1;
+
+ // send horizontal pixel-delta wheel events with phases; confirm that ListView hits the next item boundary
+ sendWheelEvent({}, Qt::ScrollBegin);
+ for (int i = 1; i < 4; ++i) {
+ sendWheelEvent({-50, 0}, Qt::ScrollUpdate);
+ if (movingAtIndex < 0 && outerListView->isMoving())
+ movingAtIndex = i;
+ }
+ QVERIFY(outerListView->isDragging());
+ sendWheelEvent({}, Qt::ScrollEnd);
+ QCOMPARE(outerListView->isDragging(), false);
+ QTRY_COMPARE(outerListView->isMoving(), false); // wait until it stops
+ qCDebug(lcTests) << "outer got moving after" << movingAtIndex
+ << "horizontal events; stopped at" << outerListView->contentX() << outerListView->currentIndex();
+ QCOMPARE_GT(movingAtIndex, 0);
+ QCOMPARE(outerListView->contentX(), 300);
+ QCOMPARE(outerCurrentIndexSpy.size(), 1);
+
+ movingAtIndex = -1;
+ QQuickListView *innerListView = qobject_cast<QQuickListView *>(outerListView->currentItem());
+ QTRY_VERIFY(innerListView);
+ QSignalSpy innerCurrentIndexSpy(innerListView, &QQuickListView::currentIndexChanged);
+
+ // send vertical pixel-delta wheel events with phases; confirm that ListView hits the next item boundary
+ sendWheelEvent({}, Qt::ScrollBegin);
+ for (int i = 1; i < 4; ++i) {
+ sendWheelEvent({0, -50}, Qt::ScrollUpdate);
+ if (movingAtIndex < 0 && innerListView->isMoving())
+ movingAtIndex = i;
+ }
+ QVERIFY(innerListView->isDragging());
+ sendWheelEvent({}, Qt::ScrollEnd);
+ QCOMPARE(innerListView->isDragging(), false);
+ QTRY_COMPARE(innerListView->isMoving(), false); // wait until it stops
+ qCDebug(lcTests) << "inner got moving after" << movingAtIndex
+ << "vertical events; stopped at" << innerListView->contentY() << innerListView->currentIndex();
+ QCOMPARE_GT(movingAtIndex, 0);
+ QCOMPARE(innerListView->contentY(), 300);
+ QCOMPARE(innerCurrentIndexSpy.size(), 1);
+}
+
class FriendlyItemView : public QQuickItemView
{
friend class ItemViewAccessor;
@@ -1153,6 +1218,23 @@ void tst_QQuickListView2::changingOrientationResetsPreviousAxisValues() // QTBUG
QVERIFY(!listView->property("isYReset").toBool());
}
+void tst_QQuickListView2::visibleBoundToCountGreaterThanZero()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("visibleBoundToCountGreaterThanZero.qml")));
+
+ auto *listView = window.rootObject()->property("listView").value<QQuickListView *>();
+ QVERIFY(listView);
+
+ QSignalSpy countChangedSpy(listView, SIGNAL(countChanged()));
+ QVERIFY(countChangedSpy.isValid());
+
+ QTRY_COMPARE_GT_WITH_TIMEOUT(listView->count(), 1, oneSecondInMs);
+ // Using the TRY variant here as well is necessary.
+ QTRY_COMPARE_GT_WITH_TIMEOUT(countChangedSpy.count(), 1, oneSecondInMs);
+ QVERIFY(listView->isVisible());
+}
+
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 6d2aa267dd..d5b3b75215 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -939,6 +939,9 @@ void tst_QQuickMouseArea::doubleClick()
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
QCOMPARE(window.rootObject()->property("released").toInt(), 2);
+
+ // wait long enough to avoid affecting the next test function
+ QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval());
}
void tst_QQuickMouseArea::doubleTap() // QTBUG-112434
@@ -970,6 +973,9 @@ void tst_QQuickMouseArea::doubleTap() // QTBUG-112434
QCOMPARE(mouseArea->pressed(), false);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
+ // avoid getting a double-click event next
+ QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval());
+
// now tap with two fingers simultaneously: only one of them generates synth-mouse
QPoint p2 = p1 + QPoint(50, 5);
QTest::touchEvent(&window, device).press(2, p1).press(3, p2);
@@ -991,8 +997,8 @@ void tst_QQuickMouseArea::doubleTap() // QTBUG-112434
QTest::touchEvent(&window, device).release(4, p1).release(5, p2);
QQuickTouchUtils::flush(&window);
QCOMPARE(window.rootObject()->property("released").toInt(), 4);
- QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
- QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 2);
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 3);
+ QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
QCOMPARE(mouseArea->pressed(), false); // make sure it doesn't get stuck
}
diff --git a/tests/auto/quick/qquickpathview/data/qtbug46487.qml b/tests/auto/quick/qquickpathview/data/qtbug46487.qml
new file mode 100644
index 0000000000..840d77ffe4
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/qtbug46487.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+PathView {
+ id: view
+ property int delegatesCreated: 0
+ property int delegatesDestroyed: 0
+
+ width: 400
+ height: 400
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ pathItemCount: 5
+ currentIndex: 1
+ model: customModel
+ delegate: Text {
+ text: "item: " + index + " of: " + view.count
+ Component.onCompleted: view.delegatesCreated++;
+ Component.onDestruction: view.delegatesDestroyed++;
+ }
+ path: Path {
+ startX: 50
+ startY: 0
+ PathLine {
+ x: 50
+ y: 400
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 90c3060235..7d41d907fb 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -133,6 +133,7 @@ private slots:
void requiredPropertiesInDelegatePreventUnrelated();
void touchMove();
void mousePressAfterFlick();
+ void qtbug46487();
private:
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -2895,6 +2896,68 @@ void tst_QQuickPathView::mousePressAfterFlick() // QTBUG-115121
QCOMPARE(pressedSpy.size(), 0);
}
+class CustomModel : public QAbstractListModel
+{
+public:
+ CustomModel(QObject *parent = 0) : QAbstractListModel(parent) {
+ m_values << 0 << 1 << 2 << 3 << 4;
+ }
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const {
+ Q_UNUSED(parent);
+ return m_values.count();
+ }
+ QVariant data(const QModelIndex &index, int role) const {
+ if (index.row() < 0 || m_values.count() <= index.row())
+ return QVariant();
+
+ return m_values[index.row()];
+ }
+
+ Q_INVOKABLE void shrink() {
+ beginResetModel();
+ m_values.takeLast();
+ m_values.takeLast();
+ endResetModel();
+ }
+
+private:
+ QList<int> m_values;
+};
+
+void tst_QQuickPathView::qtbug46487()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ CustomModel* model = new CustomModel;
+ QQmlContext *ctxt = window->rootContext();
+ ctxt->setContextProperty("customModel", model);
+
+ window->setSource(testFileUrl("qtbug46487.qml"));
+ window->show();
+ qApp->processEvents();
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview);
+
+ QTest::qWait(500);
+
+ // Should create just pathItemCount amount and not destroy any
+ QCOMPARE(pathview->count(), 5);
+ QCOMPARE(pathview->property("delegatesCreated").toInt(), 5);
+ QCOMPARE(pathview->property("delegatesDestroyed").toInt(), 0);
+
+ // Resets the model and removes 2 items.
+ model->shrink();
+ QTest::qWait(500);
+
+ // Should destroy previous items (begin/endResetModel) and
+ // (re)create 3 new items.
+ QCOMPARE(pathview->count(), 3);
+ QCOMPARE(pathview->property("delegatesCreated").toInt(), 5 + 3);
+ QCOMPARE(pathview->property("delegatesDestroyed").toInt(), 5);
+}
+
QTEST_MAIN(tst_QQuickPathView)
#include "tst_qquickpathview.moc"
diff --git a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
index 590f022e4e..4e0534edf0 100644
--- a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
+++ b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
@@ -108,6 +108,11 @@ void tst_qquickpixmapcache::single()
QFETCH(bool, exists);
QFETCH(bool, neterror);
+#if !QT_CONFIG(qml_network)
+ if (target.scheme() == "http")
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
QString expectedError;
if (neterror) {
expectedError = "Error transferring " + target.toString() + " - server replied: Not found";
@@ -196,6 +201,11 @@ void tst_qquickpixmapcache::parallel()
QFETCH(int, incache);
QFETCH(int, cancel);
+#if !QT_CONFIG(qml_network)
+ if (target1.scheme() == "http" || target2.scheme() == "http")
+ QSKIP("Skipping due to lack of QML network feature");
+#endif
+
QList<QUrl> targets;
targets << target1 << target2;
diff --git a/tests/auto/quick/qquickstates/data/anchorRewindBug3.qml b/tests/auto/quick/qquickstates/data/anchorRewindBug3.qml
new file mode 100644
index 0000000000..6d829be363
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/anchorRewindBug3.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width: 100; height: 100
+
+ Rectangle {
+ id: rectangle
+ objectName: "inner"
+ color: "green"
+ // Width and height end up to be 50
+ // after root Component.onCompleted
+ width: 75
+ height: 75
+ anchors.top: root.top
+ anchors.left: root.left
+ }
+
+ // Start with anchored state
+ state: "anchored"
+ states: [
+ State {
+ name: "anchored"
+ AnchorChanges {
+ target: rectangle
+ anchors.top: undefined
+ anchors.left: undefined
+ anchors.right: root.right
+ anchors.bottom: root.bottom
+ }
+ }
+ ]
+
+ Component.onCompleted: {
+ rectangle.width = 50
+ rectangle.height = 50
+ }
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index be1361e4ab..7332db93fd 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -154,6 +154,7 @@ private slots:
void anchorChangesCrash();
void anchorRewindBug();
void anchorRewindBug2();
+ void anchorRewind_keepsSize_whenStateResetsDefaultAnchors();
void script();
void restoreEntryValues();
void explicitChanges();
@@ -1087,6 +1088,30 @@ void tst_qquickstates::anchorRewindBug2()
QCOMPARE(mover->width(), qreal(50.0));
}
+// QTBUG-126057
+void tst_qquickstates::anchorRewind_keepsSize_whenStateResetsDefaultAnchors()
+{
+ // Arrange
+ QQmlEngine engine;
+
+ // NOTE: Contains two nested rectangles, inner is by default anchored to the top left corner of
+ // its parent. A state is initially "anchored" which removes the default anchoring and anchors
+ // the inner rectangle to the bottom right corner of the parent. The size of the inner rectangle
+ // is assigned to 50x50 on Component.onCompleted of outer rectangle.
+ QQmlComponent rectComponent(&engine, testFileUrl("anchorRewindBug3.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(rectComponent.create()));
+ QVERIFY(rect != nullptr);
+ QQuickRectangle *mover = rect->findChild<QQuickRectangle*>("inner");
+ QVERIFY(mover != nullptr);
+
+ // Act
+ QQuickItemPrivate::get(rect.get())->setState("");
+
+ // Assert
+ QCOMPARE(mover->width(), qreal(50.0));
+ QCOMPARE(mover->height(), qreal(50.0));
+}
+
void tst_qquickstates::script()
{
QQmlEngine engine;
diff --git a/tests/auto/quick/qquicktableview/data/invalidateModelContextObject.qml b/tests/auto/quick/qquicktableview/data/invalidateModelContextObject.qml
new file mode 100644
index 0000000000..ff552e856c
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/invalidateModelContextObject.qml
@@ -0,0 +1,38 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+    width: 720
+    height: 480
+    visible: true
+
+ property alias tableView: tableView
+ property int modelData: 15
+
+ Page {
+        anchors.fill: parent
+ header: Rectangle {
+            height: 40
+            color: "red"
+        }
+ TableView {
+ id: tableView
+            anchors.fill: parent
+ model: modelData
+ contentY: Math.max(0, contentHeight - height)
+            contentHeight: 40 * rows
+ rowHeightProvider: () => 40
+            columnWidthProvider: () => 200
+ delegate : Rectangle {
+                width: 40;
+                height: 40;
+                color: "green"
+ Text {
+                    anchors.fill: parent
+                    text: index
+                }
+            }
+        }
+    }
+}
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 96137877cb..7482367057 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -162,12 +162,16 @@ private slots:
void checkSyncView_childViews_data();
void checkSyncView_childViews();
void checkSyncView_differentSizedModels();
- void checkSyncView_differentGeometry();
+ void checkSyncView_differentGeometry_vertical();
+ void checkSyncView_differentGeometry_horizontal();
+ void checkSyncView_differentGeometry_both_directions();
void checkSyncView_connect_late_data();
void checkSyncView_connect_late();
void checkSyncView_pageFlicking();
void checkSyncView_emptyModel();
void checkSyncView_topLeftChanged();
+ void checkSyncView_dontRelayoutWhileFlicking();
+ void checkSyncView_detectTopLeftPositionChanged();
void delegateWithRequiredProperties();
void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable();
void replaceModel();
@@ -278,6 +282,7 @@ private slots:
void checkScroll_data();
void checkScroll();
void checkRebuildJsModel();
+ void invalidateTableInstanceModelContextObject();
};
tst_QQuickTableView::tst_QQuickTableView()
@@ -3070,7 +3075,7 @@ void tst_QQuickTableView::checkSyncView_differentSizedModels()
QVERIFY(tableViewHVPrivate->loadedColumns.isEmpty());
}
-void tst_QQuickTableView::checkSyncView_differentGeometry()
+void tst_QQuickTableView::checkSyncView_differentGeometry_vertical()
{
// Check that you can have two tables in a syncView relation, where
// the sync "child" is larger than the sync view. This means that the
@@ -3083,46 +3088,106 @@ void tst_QQuickTableView::checkSyncView_differentGeometry()
GET_QML_TABLEVIEW(tableViewV);
GET_QML_TABLEVIEW(tableViewHV);
- tableView->setWidth(40);
- tableView->setHeight(40);
+ tableView->setHeight(90);
+ tableViewH->setSyncView(nullptr);
+ tableViewHV->setSyncView(nullptr);
auto tableViewModel = TestModelAsVariant(100, 100);
tableView->setModel(tableViewModel);
- tableViewH->setModel(tableViewModel);
tableViewV->setModel(tableViewModel);
- tableViewHV->setModel(tableViewModel);
WAIT_UNTIL_POLISHED;
- // Check that the column widths are in sync
- for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column) {
- QCOMPARE(tableViewH->columnWidth(column), tableView->columnWidth(column));
- QCOMPARE(tableViewHV->columnWidth(column), tableView->columnWidth(column));
- }
+ // Check that the row heights are in sync
+ for (int row = tableViewV->topRow(); row < tableViewV->bottomRow(); ++row)
+ QCOMPARE(tableViewV->rowHeight(row), tableView->rowHeight(row));
+
+ // Flick in a new row
+ tableView->setContentY(20);
// Check that the row heights are in sync
- for (int row = tableViewV->topRow(); row < tableViewV->bottomRow(); ++row) {
+ for (int row = tableViewV->topRow(); row <= tableViewV->bottomRow(); ++row)
QCOMPARE(tableViewV->rowHeight(row), tableView->rowHeight(row));
- QCOMPARE(tableViewHV->rowHeight(row), tableView->rowHeight(row));
- }
+}
+
+void tst_QQuickTableView::checkSyncView_differentGeometry_horizontal()
+{
+ // Check that you can have two tables in a syncView relation, where
+ // the sync "child" is larger than the sync view. This means that the
+ // child will display more rows and columns than the parent.
+ // In that case, the sync view will anyway need to load the same rows
+ // and columns as the child, otherwise the column and row sizes
+ // cannot be determined for the child.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ tableView->setWidth(90);
+ tableViewV->setSyncView(nullptr);
+ tableViewHV->setSyncView(nullptr);
+
+ auto tableViewModel = TestModelAsVariant(100, 100);
+
+ tableView->setModel(tableViewModel);
+ tableViewH->setModel(tableViewModel);
- // Flick a bit, and do the same test again
- tableView->setContentX(200);
- tableView->setContentY(200);
WAIT_UNTIL_POLISHED;
// Check that the column widths are in sync
- for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column) {
+ for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column)
QCOMPARE(tableViewH->columnWidth(column), tableView->columnWidth(column));
- QCOMPARE(tableViewHV->columnWidth(column), tableView->columnWidth(column));
- }
+
+ // Flick in a new column
+ tableView->setContentX(20);
+
+ // Check that the column widths are in sync
+ for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column)
+ QCOMPARE(tableViewH->columnWidth(column), tableView->columnWidth(column));
+}
+
+void tst_QQuickTableView::checkSyncView_differentGeometry_both_directions() {
+ // Check that you can have two tables in a syncView relation, where
+ // the sync "child" is larger than the sync view. This means that the
+ // child will display more rows and columns than the parent.
+ // In that case, the sync view will anyway need to load the same rows
+ // and columns as the child, otherwise the column and row sizes
+ // cannot be determined for the child.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ tableView->setWidth(90);
+ tableView->setHeight(90);
+ tableViewHV->setSyncView(nullptr);
+
+ auto tableViewModel = TestModelAsVariant(100, 100);
+
+ tableView->setModel(tableViewModel);
+ tableViewH->setModel(tableViewModel);
+ tableViewV->setModel(tableViewModel);
+
+ WAIT_UNTIL_POLISHED;
// Check that the row heights are in sync
- for (int row = tableViewV->topRow(); row < tableViewV->bottomRow(); ++row) {
+ for (int row = tableViewV->topRow(); row < tableViewV->bottomRow(); ++row)
QCOMPARE(tableViewV->rowHeight(row), tableView->rowHeight(row));
- QCOMPARE(tableViewHV->rowHeight(row), tableView->rowHeight(row));
- }
+ // Check that the column widths are in sync
+ for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column)
+ QCOMPARE(tableViewH->columnWidth(column), tableView->columnWidth(column));
+
+ // Flick in a new row
+ tableView->setContentX(20);
+ tableView->setContentY(20);
+
+ // Check that the row heights are in sync
+ for (int row = tableViewV->topRow(); row <= tableViewV->bottomRow(); ++row)
+ QCOMPARE(tableViewV->rowHeight(row), tableView->rowHeight(row));
+ // Check that the column widths are in sync
+ for (int column = tableViewH->leftColumn(); column < tableViewH->rightColumn(); ++column)
+ QCOMPARE(tableViewH->columnWidth(column), tableView->columnWidth(column));
}
void tst_QQuickTableView::checkSyncView_connect_late_data()
@@ -3328,6 +3393,85 @@ void tst_QQuickTableView::checkSyncView_topLeftChanged()
QCOMPARE(tableViewV->topRow(), tableView->topRow());
}
+void tst_QQuickTableView::checkSyncView_dontRelayoutWhileFlicking()
+{
+ // Check that we don't do a full relayout in a sync child when
+ // a new row or column is flicked into the view. Normal load
+ // and unload of edges should suffice, equal to how the main
+ // TableView (syncView) does it.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewHV->setModel(model);
+
+ tableView->setColumnWidthProvider(QJSValue());
+ tableView->setRowHeightProvider(QJSValue());
+ view->rootObject()->setProperty("delegateWidth", 50);
+ view->rootObject()->setProperty("delegateHeight", 50);
+
+ WAIT_UNTIL_POLISHED;
+
+ // To check that we don't do a relayout when flicking horizontally, we use a "trick"
+ // where we check the rebuildOptions when we receive the rightColumnChanged
+ // signal. If this signal is emitted as a part of a relayout, rebuildOptions
+ // would still be different from RebuildOption::None at that point.
+ bool columnFlickedIn = false;
+ connect(tableViewHV, &QQuickTableView::rightColumnChanged, [&] {
+ columnFlickedIn = true;
+ QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
+ });
+
+ // We do the same for vertical flicking
+ bool rowFlickedIn = false;
+ connect(tableViewHV, &QQuickTableView::bottomRowChanged, [&] {
+ rowFlickedIn = true;
+ QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
+ });
+
+ // Move the main tableview so that a new column is flicked in
+ tableView->setContentX(60);
+ QTRY_VERIFY(columnFlickedIn);
+
+ // Move the main tableview so that a new row is flicked in
+ tableView->setContentY(60);
+ QTRY_VERIFY(rowFlickedIn);
+}
+
+void tst_QQuickTableView::checkSyncView_detectTopLeftPositionChanged()
+{
+ // It can happen that, during a resize of columns or rows from using a float-based
+ // slider, that the position of the top-left delegate item is shifted a bit left or
+ // right because of rounding issues. And this again can over time, as you flick, make
+ // the loadedTableOuterRect get slightly out of sync in the sync child compared to the
+ // sync view. TableView will detect if this happens (in syncSyncView), and correct for
+ // it. And this test will test that it works.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewHV->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Writing an auto test to trigger this rounding issue is very hard. So to keep it
+ // simple, we cheat by just moving the loadedTableOuterRect directly, and
+ // check that the syncView child detects it, and corrects it, upon doing a
+ // forceLayout()
+ tableViewPrivate->loadedTableOuterRect.moveLeft(20);
+ tableViewPrivate->loadedTableOuterRect.moveTop(30);
+ tableViewPrivate->relayoutTableItems();
+ tableViewHV->forceLayout();
+
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 20);
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.left(), 20);
+
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 30);
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.top(), 30);
+}
+
void tst_QQuickTableView::checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable()
{
LOAD_TABLEVIEW("plaintableview.qml");
@@ -7523,6 +7667,29 @@ void tst_QQuickTableView::checkRebuildJsModel()
QCOMPARE(tableView->property(modelUpdated).toInt(), 1);
}
+void tst_QQuickTableView::invalidateTableInstanceModelContextObject()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("invalidateModelContextObject.qml"));
+
+ std::unique_ptr<QQuickWindow> window(qobject_cast<QQuickWindow*>(component.create()));
+ QVERIFY(window);
+
+ auto tableView = window->property("tableView").value<QQuickTableView *>();
+ QVERIFY(tableView);
+
+ const int modelData = window->property("modelData").toInt();
+ QTRY_COMPARE(tableView->rows(), modelData);
+
+ bool tableViewDestroyed = false;
+ connect(tableView, &QObject::destroyed, [&] {
+ tableViewDestroyed = true;
+ });
+
+ window.reset();
+ QTRY_COMPARE(tableViewDestroyed, true);
+}
+
QTEST_MAIN(tst_QQuickTableView)
#include "tst_qquicktableview.moc"
diff --git a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
index 63d4f24a54..23d2e006ba 100644
--- a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
+++ b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
@@ -3,14 +3,17 @@
#include <qtest.h>
#include <QtTest/QtTest>
+#include <QtQuick/QQuickView>
#include <QtQuick/QQuickTextDocument>
#include <QtQuick/QQuickItem>
#include <QtQuick/private/qquicktextedit_p.h>
#include <QtQuick/private/qquicktextdocument_p.h>
#include <QtGui/QTextDocument>
+#include <QtGui/QTextBlock>
#include <QtGui/QTextDocumentWriter>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
+#include <QtQuickTest/QtQuickTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
class tst_qquicktextdocument : public QQmlDataTest
@@ -22,6 +25,8 @@ public:
private slots:
void textDocumentWriter();
void textDocumentWithImage();
+ void changeCharFormatInRange_data();
+ void changeCharFormatInRange();
};
QString text = QStringLiteral("foo bar");
@@ -68,6 +73,53 @@ void tst_qquicktextdocument::textDocumentWithImage()
QCOMPARE(image, document.resource(QTextDocument::ImageResource, name).value<QImage>());
}
+void tst_qquicktextdocument::changeCharFormatInRange_data()
+{
+ QTest::addColumn<bool>("editBlock");
+
+ QTest::newRow("begin/end") << true;
+ QTest::newRow("no edit block") << false; // QTBUG-126886 : don't crash
+}
+
+void tst_qquicktextdocument::changeCharFormatInRange()
+{
+ QFETCH(bool, editBlock);
+ QQuickView window(testFileUrl("text.qml"));
+ window.showNormal();
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(textEdit);
+ QVERIFY(textEdit->textDocument());
+
+ auto *doc = textEdit->textDocument()->textDocument();
+ QVERIFY(doc);
+
+ QSignalSpy contentSpy(doc, &QTextDocument::contentsChanged);
+ const auto data = QStringLiteral("Format String");
+ doc->setPlainText(data);
+ const auto block = doc->findBlockByNumber(0);
+
+ auto formatText = [block, data] {
+ QTextLayout::FormatRange formatText;
+ formatText.start = 0;
+ formatText.length = data.size();
+ formatText.format.setForeground(Qt::green);
+ block.layout()->setFormats({formatText});
+ };
+
+ // change the char format of this block, and verify visual effect
+ if (editBlock) {
+ QTextCursor cursor(doc);
+ cursor.beginEditBlock();
+ formatText();
+ cursor.endEditBlock();
+ } else {
+ formatText();
+ }
+
+ QVERIFY(QQuickTest::qWaitForPolish(textEdit));
+ QCOMPARE(contentSpy.size(), editBlock ? 2 : 1);
+}
+
QTEST_MAIN(tst_qquicktextdocument)
#include "tst_qquicktextdocument.moc"
diff --git a/tests/auto/quick/qquicktextedit/data/hAlignVisual.qml b/tests/auto/quick/qquicktextedit/data/hAlignVisual.qml
index 1280a655f0..f532a9aa36 100644
--- a/tests/auto/quick/qquicktextedit/data/hAlignVisual.qml
+++ b/tests/auto/quick/qquicktextedit/data/hAlignVisual.qml
@@ -4,11 +4,11 @@ Rectangle {
width: 200
height: 100
- Text {
- objectName: "textItem"
+ TextEdit {
+ objectName: "textEditItem"
text: "AA\nBBBBBBB\nCCCCCCCCCCCCCCCC"
anchors.centerIn: parent
- horizontalAlignment: Text.AlignLeft
+ horizontalAlignment: TextEdit.AlignLeft
font.pointSize: 12
font.family: "Times New Roman"
}
diff --git a/tests/auto/quick/qquicktextedit/data/inFlickable.qml b/tests/auto/quick/qquicktextedit/data/inFlickable.qml
index 7a896db29b..183ddd6701 100644
--- a/tests/auto/quick/qquicktextedit/data/inFlickable.qml
+++ b/tests/auto/quick/qquicktextedit/data/inFlickable.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
Flickable {
+ id: flick
width: 320; height: 120; contentHeight: text.height
TextEdit {
id: text
@@ -8,4 +9,10 @@ Flickable {
font.pixelSize: 20
text: "several\nlines\nof\ntext\n-\ntry\nto\nflick"
}
+ Text {
+ color: "red"
+ parent: flick // stay on top
+ anchors.right: parent.right
+ text: flick.contentY.toFixed(1)
+ }
}
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 15f538ac3c..44745f8263 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -8,6 +8,8 @@
#include <QtQuick/QQuickTextDocument>
#include <QtQuickTest/QtQuickTest>
#include <QTextDocument>
+#include <QtGui/qtextobject.h>
+#include <QtGui/QTextTable>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
@@ -43,6 +45,8 @@ DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+// #define DEBUG_WRITE_INPUT
+
static bool isPlatformWayland()
{
return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive);
@@ -143,6 +147,8 @@ private slots:
void largeTextObservesViewport();
void largeTextSelection();
void renderingAroundSelection();
+ void largeTextTables_data();
+ void largeTextTables();
void signal_editingfinished();
@@ -221,7 +227,6 @@ private:
void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = {});
bool isMainFontFixed();
- static bool hasWindowActivation();
QStringList standard;
QStringList richText;
@@ -972,8 +977,8 @@ void tst_qquicktextedit::hAlignVisual()
view.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&view));
- QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
- QVERIFY(text != nullptr);
+ QQuickTextEdit *text = view.rootObject()->findChild<QQuickTextEdit*>("textEditItem");
+ QVERIFY(text);
// Try to check whether alignment works by checking the number of black
// pixels in the thirds of the grabbed image.
@@ -1000,7 +1005,7 @@ void tst_qquicktextedit::hAlignVisual()
}
{
// HCenter Align
- text->setHAlign(QQuickText::AlignHCenter);
+ text->setHAlign(QQuickTextEdit::AlignHCenter);
QImage image = view.grabWindow();
const int left = numberOfNonWhitePixels(centeredSection1, centeredSection2, image);
const int mid = numberOfNonWhitePixels(centeredSection2, centeredSection3, image);
@@ -1010,7 +1015,7 @@ void tst_qquicktextedit::hAlignVisual()
}
{
// Right Align
- text->setHAlign(QQuickText::AlignRight);
+ text->setHAlign(QQuickTextEdit::AlignRight);
QImage image = view.grabWindow();
const int left = numberOfNonWhitePixels(centeredSection1, centeredSection2, image);
const int mid = numberOfNonWhitePixels(centeredSection2, centeredSection3, image);
@@ -1022,36 +1027,36 @@ void tst_qquicktextedit::hAlignVisual()
text->setWidth(200);
{
- // Left Align
+ // Right Align
QImage image = view.grabWindow();
- int x = qCeil(text->implicitWidth() * view.devicePixelRatio());
- int left = numberOfNonWhitePixels(0, x, image);
- int right = numberOfNonWhitePixels(x, image.width() - x, image);
- QVERIFY2(left > 0, msgNotGreaterThan(left, 0).constData());
- QCOMPARE(right, 0);
+ const int x = image.width() - qCeil(text->implicitWidth() * view.devicePixelRatio());
+ const int left = numberOfNonWhitePixels(0, x, image);
+ const int right = numberOfNonWhitePixels(x, image.width() - x, image);
+ QCOMPARE(left, 0);
+ QVERIFY2(right > 0, msgNotGreaterThan(left, 0).constData());
}
{
// HCenter Align
- text->setHAlign(QQuickText::AlignHCenter);
+ text->setHAlign(QQuickTextEdit::AlignHCenter);
QImage image = view.grabWindow();
- int x1 = qFloor(image.width() - text->implicitWidth() * view.devicePixelRatio()) / 2;
- int x2 = image.width() - x1;
- int left = numberOfNonWhitePixels(0, x1, image);
- int mid = numberOfNonWhitePixels(x1, x2 - x1, image);
- int right = numberOfNonWhitePixels(x2, image.width() - x2, image);
+ const int x1 = qFloor(image.width() - text->implicitWidth() * view.devicePixelRatio()) / 2;
+ const int x2 = image.width() - x1;
+ const int left = numberOfNonWhitePixels(0, x1, image);
+ const int mid = numberOfNonWhitePixels(x1, x2 - x1, image);
+ const int right = numberOfNonWhitePixels(x2, image.width(), image);
QCOMPARE(left, 0);
QVERIFY2(mid > 0, msgNotGreaterThan(left, 0).constData());
QCOMPARE(right, 0);
}
{
- // Right Align
- text->setHAlign(QQuickText::AlignRight);
+ // Left Align
+ text->setHAlign(QQuickTextEdit::AlignLeft);
QImage image = view.grabWindow();
- int x = image.width() - qCeil(text->implicitWidth() * view.devicePixelRatio());
- int left = numberOfNonWhitePixels(0, x, image);
- int right = numberOfNonWhitePixels(x, image.width() - x, image);
- QCOMPARE(left, 0);
- QVERIFY2(right > 0, msgNotGreaterThan(left, 0).constData());
+ const int x = qCeil(text->implicitWidth() * view.devicePixelRatio());
+ const int left = numberOfNonWhitePixels(0, x, image);
+ const int right = numberOfNonWhitePixels(x, image.width() - x, image);
+ QVERIFY2(left > 0, msgNotGreaterThan(left, 0).constData());
+ QCOMPARE(right, 0);
}
}
@@ -3385,11 +3390,6 @@ bool tst_qquicktextedit::isMainFontFixed()
return ret;
}
-bool tst_qquicktextedit::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
void tst_qquicktextedit::textInput()
{
QQuickView view(testFileUrl("inputMethodEvent.qml"));
@@ -3951,6 +3951,105 @@ void tst_qquicktextedit::renderingAroundSelection()
QTRY_COMPARE(textItem->sortedLinePositions, sortedLinePositions);
}
+struct OffsetAndExpectedBlocks {
+ int tableIndex; // which nested frame
+ qreal tableOffset; // fraction of that frame's height to scroll to
+ int minExpectedBlockCount;
+
+ OffsetAndExpectedBlocks(int i, qreal o, int c)
+ : tableIndex(i), tableOffset(o), minExpectedBlockCount(c) {}
+};
+
+typedef QList<OffsetAndExpectedBlocks> OffsetAndExpectedBlocksList;
+
+void tst_qquicktextedit::largeTextTables_data()
+{
+ QTest::addColumn<int>("tables");
+ QTest::addColumn<int>("tableCols");
+ QTest::addColumn<int>("tableRows");
+ QTest::addColumn<OffsetAndExpectedBlocksList>("steps");
+
+ QTest::newRow("one big table") << 1 << 3 << 70
+ << OffsetAndExpectedBlocksList{
+ OffsetAndExpectedBlocks(1, 0.75, 150),
+ OffsetAndExpectedBlocks(1, 0.5, 150)};
+ QTest::newRow("short tables") << 5 << 3 << 10
+ << OffsetAndExpectedBlocksList{
+ OffsetAndExpectedBlocks(4, 0.75, 35),
+ OffsetAndExpectedBlocks(3, 0.25, 50),
+ OffsetAndExpectedBlocks(2, 0.75, 50)};
+}
+
+void tst_qquicktextedit::largeTextTables() // QTBUG-118636
+{
+ QFETCH(int, tables);
+ QFETCH(int, tableCols);
+ QFETCH(int, tableRows);
+ QFETCH(OffsetAndExpectedBlocksList, steps);
+
+ QStringList lines;
+
+ lines << QLatin1String("<h1>") + QTest::currentDataTag() + "</h1>";
+ for (int t = 0; t < tables; ++t) {
+ if (t > 0)
+ lines << QString("<p>table %1</p>").arg(t);
+ lines << "<table border='1'>";
+ for (int r = 0; r < tableRows; ++r) {
+ lines << " <tr>";
+ for (int c = 0; c < tableCols; ++c)
+ lines << QString(" <td>table %1 cell %2, %3</td>").arg(t).arg(c).arg(r);
+ lines << " </tr>";
+ }
+ lines << "</table>";
+ }
+ lines << "<p>here endeth the tables</p>";
+ QString html = lines.join('\n');
+
+#ifdef DEBUG_WRITE_INPUT
+ QFile f(QLatin1String("/tmp/") + QTest::currentDataTag() + ".html");
+ f.open(QFile::WriteOnly);
+ f.write(html.toUtf8());
+ f.close();
+#endif
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inFlickable.qml")));
+ QQuickFlickable *flick = qmlobject_cast<QQuickFlickable *>(window.rootObject());
+ QVERIFY(flick);
+ QQuickTextEdit *te = window.rootObject()->findChild<QQuickTextEdit *>();
+ QVERIFY(te);
+ auto *tePriv = QQuickTextEditPrivate::get(te);
+ auto font = te->font();
+ font.setPixelSize(10);
+ te->setFont(font);
+
+ te->setTextFormat(QQuickTextEdit::RichText);
+ te->setText(html);
+ te->setFlag(QQuickItem::ItemObservesViewport); // this isn't "large text", but test viewporting anyway
+
+ QTextDocument *doc = te->textDocument()->textDocument();
+ QList<QTextFrame *> frames = doc->rootFrame()->childFrames();
+ frames.prepend(doc->rootFrame());
+ qCDebug(lcTests) << "blocks" << doc->blockCount() << "chars" << doc->characterCount() << "frames" << frames;
+
+ for (const OffsetAndExpectedBlocks &oeb : steps) {
+ QCOMPARE_GT(frames.size(), oeb.tableIndex);
+ const QTextFrame *textFrame = frames.at(oeb.tableIndex);
+ const QTextCursor top = textFrame->firstCursorPosition();
+ const qreal yTop = te->positionToRectangle(top.position()).top();
+ const QTextCursor bottom = textFrame->lastCursorPosition();
+ const qreal yBottom = te->positionToRectangle(bottom.position()).bottom();
+ const qreal y = yTop + (yBottom - yTop) * oeb.tableOffset;
+ qCDebug(lcTests) << "frame" << textFrame << "goes from pos" << top.position() << "y" << yTop
+ << "to pos" << bottom.position() << "y" << yBottom << "; scrolling to" << y
+ << "which is at" << oeb.tableOffset << "of table height" << (yBottom - yTop);
+ flick->setContentY(y);
+ qCDebug(lcTests) << tePriv->renderedRegion << "rendered blocks" << tePriv->renderedBlockCount << ":"
+ << tePriv->firstBlockInViewport << "to" << tePriv->firstBlockPastViewport;
+ QTRY_COMPARE_GE(tePriv->renderedBlockCount, oeb.minExpectedBlockCount);
+ }
+}
+
void tst_qquicktextedit::signal_editingfinished()
{
QQuickView *window = new QQuickView(nullptr);
@@ -6483,8 +6582,8 @@ void tst_qquicktextedit::touchscreenDoesNotSelect()
void tst_qquicktextedit::touchscreenSetsFocusAndMovesCursor()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
+
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("twoInAColumn.qml")));
window.requestActivate();
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index 5e2d49afb8..c944406e10 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -5,6 +5,7 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <private/qinputmethod_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -212,7 +213,6 @@ private:
#if QT_CONFIG(shortcut)
void simulateKeys(QWindow *window, const QKeySequence &sequence);
#endif
- static bool hasWindowActivation();
QQmlEngine engine;
QStringList standard;
@@ -238,11 +238,6 @@ void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
}
}
-bool tst_qquicktextinput::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
#if QT_CONFIG(shortcut)
void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
@@ -7191,17 +7186,15 @@ void tst_qquicktextinput::touchscreenDoesNotSelect()
QTest::touchEvent(&window, touchscreen.data()).press(0, QPoint(x2,y), &window);
QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x2,y), &window);
QQuickTouchUtils::flush(&window);
- QCOMPARE(textInputObject->selectedText().isEmpty(), !expectDefaultSelectByMouse);
- if (expectDefaultSelectByMouse)
- QCOMPARE(textInputObject->cursorPosition(), cursorPos);
- else
- QCOMPARE_NE(textInputObject->cursorPosition(), cursorPos);
+ QCOMPARE(textInputObject->selectedText().isEmpty(), true);
+ QCOMPARE_NE(textInputObject->cursorPosition(), cursorPos);
+ QVERIFY(textInputObject->selectedText().isEmpty());
}
void tst_qquicktextinput::touchscreenSetsFocusAndMovesCursor()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
+
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("twoInAColumn.qml")));
window.requestActivate();
diff --git a/tests/auto/quick/qquickview_extra/data/qtbug_87228.qml b/tests/auto/quick/qquickview_extra/data/qtbug_87228.qml
index ff10eba23d..9327daae6b 100644
--- a/tests/auto/quick/qquickview_extra/data/qtbug_87228.qml
+++ b/tests/auto/quick/qquickview_extra/data/qtbug_87228.qml
@@ -15,6 +15,8 @@ Item {
Rectangle {
ListView {
objectName: "listView"
+ width: 100
+ height: 100
delegate: Text {
required property string desc
text: desc
diff --git a/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp b/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
index 2d2581a3fa..2124a8ee8f 100644
--- a/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
+++ b/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
@@ -56,10 +56,8 @@ void tst_SoftwareRenderer::renderTarget()
rc.polishItems();
- rc.beginFrame();
rc.sync();
rc.render();
- rc.endFrame();
QImage content = window->grabWindow();
QString errorMessage;
@@ -74,10 +72,8 @@ void tst_SoftwareRenderer::renderTarget()
rc.polishItems();
- rc.beginFrame();
rc.sync();
rc.render();
- rc.endFrame();
content = window->grabWindow();
QVERIFY2(QQuickVisualTestUtils::compareImages(content, renderTarget2, &errorMessage),
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
index d7820996be..179acbc728 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -244,6 +244,8 @@ private slots:
void strayTouchDoesntAutograb();
+ void noDoubleClickWithInterveningTouch();
+
protected:
bool eventFilter(QObject *, QEvent *event) override
{
@@ -1303,7 +1305,7 @@ void tst_TouchMouse::touchGrabCausesMouseUngrab()
QVERIFY(leftItem);
EventItem *rightItem = window.rootObject()->findChild<EventItem*>("rightItem");
- QVERIFY(leftItem);
+ QVERIFY(rightItem);
// Send a touch to the leftItem. But leftItem accepts only mouse events, thus
// a mouse event will be synthesized out of this touch and will get accepted by
@@ -1660,6 +1662,58 @@ void tst_TouchMouse::strayTouchDoesntAutograb() // QTBUG-107867
QTest::touchEvent(&window, device).release(0, p1).release(1, p1);
}
+void tst_TouchMouse::noDoubleClickWithInterveningTouch() // QTBUG-116442
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("twosiblingitems.qml")));
+
+ EventItem *leftItem = window.rootObject()->findChild<EventItem*>("leftItem");
+ QVERIFY(leftItem);
+ // simulate a MouseArea: don't accept touch
+ leftItem->setAcceptedMouseButtons(Qt::LeftButton);
+ leftItem->acceptMouse = true;
+
+ EventItem *rightItem = window.rootObject()->findChild<EventItem*>("rightItem");
+ QVERIFY(rightItem);
+ // simulate an item that reacts to either touch or mouse
+ rightItem->setAcceptedMouseButtons(Qt::LeftButton);
+ rightItem->acceptMouse = true;
+ rightItem->setAcceptTouchEvents(true);
+ rightItem->acceptTouch = true;
+
+ const QPoint pLeft(80, 200);
+ const QPoint pRight(240, 200);
+
+ // tap left
+ QTest::touchEvent(&window, device).press(1, pLeft, &window);
+ QTest::touchEvent(&window, device).release(1, pLeft, &window);
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "left tap" << leftItem->eventList;
+ QCOMPARE(leftItem->eventList.size(), 3);
+ QCOMPARE(leftItem->eventList.at(0).type, QEvent::MouseButtonPress);
+ leftItem->eventList.clear();
+
+ // tap right
+ QTest::touchEvent(&window, device).press(1, pRight, &window);
+ QTest::touchEvent(&window, device).release(1, pRight, &window);
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "right tap" << rightItem->eventList;
+ QCOMPARE(rightItem->eventList.size(), 2);
+ QCOMPARE(rightItem->eventList.at(0).type, QEvent::TouchBegin);
+ rightItem->eventList.clear();
+
+ // tap left again: this is NOT a double-click, even though it's within time and space limits
+ QTest::touchEvent(&window, device).press(3, pLeft, &window);
+ QTest::touchEvent(&window, device).release(3, pLeft, &window);
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "left tap again" << leftItem->eventList;
+ QCOMPARE(leftItem->eventList.size(), 3);
+ QCOMPARE(leftItem->eventList.at(0).type, QEvent::MouseButtonPress);
+ QCOMPARE(leftItem->eventList.at(1).type, QEvent::MouseButtonRelease);
+ QCOMPARE(leftItem->eventList.at(2).type, QEvent::UngrabMouse);
+ leftItem->eventList.clear();
+}
+
QTEST_MAIN(tst_TouchMouse)
#include "tst_touchmouse.moc"
diff --git a/tests/auto/quickcontrols/CMakeLists.txt b/tests/auto/quickcontrols/CMakeLists.txt
index f4f2e6b2c6..b7669d4f54 100644
--- a/tests/auto/quickcontrols/CMakeLists.txt
+++ b/tests/auto/quickcontrols/CMakeLists.txt
@@ -24,13 +24,16 @@ add_subdirectory(pressandhold)
add_subdirectory(qquickapplicationwindow)
add_subdirectory(qquickcontrol)
add_subdirectory(qquickcolor)
+add_subdirectory(qquickcontainer)
add_subdirectory(qquickdrawer)
add_subdirectory(qquickheaderview)
add_subdirectory(qquickiconimage)
add_subdirectory(qquickiconlabel)
add_subdirectory(qquickimaginestyle)
-add_subdirectory(qquickmaterialstyle)
-add_subdirectory(qquickmaterialstyleconf)
+if (QT_FEATURE_quickcontrols2_material)
+ add_subdirectory(qquickmaterialstyle)
+ add_subdirectory(qquickmaterialstyleconf)
+endif()
add_subdirectory(qquickmenu)
add_subdirectory(qquickmenubar)
add_subdirectory(qquickninepatchimage)
diff --git a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
index dabab76bbe..74bbf2954e 100644
--- a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
@@ -43,6 +43,9 @@ set_source_files_properties(${test_data}
HEADER_FILE_ONLY ON
)
+set(test_target tst_basic)
+include(../shared.cmake)
+
#### Keys ignored in scope 1:.:.:basic.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols/controls/data/combobox/shader.frag b/tests/auto/quickcontrols/controls/data/combobox/shader.frag
new file mode 100644
index 0000000000..fbbef218e6
--- /dev/null
+++ b/tests/auto/quickcontrols/controls/data/combobox/shader.frag
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+layout(binding = 1) uniform sampler2D source; // this item
+
+layout(std140, binding = 0) uniform buf {
+ float qt_Opacity; // inherited opacity of this item
+};
+
+
+void main() {
+ vec4 p = texture(source, qt_TexCoord0);
+ lowp float g = dot(p.xyz, vec3(0.344, 0.5, 0.156));
+ fragColor = vec4(g, g, g, p.a) * qt_Opacity;
+}
diff --git a/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml b/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
index 4560c93dea..296aa2c507 100644
--- a/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
@@ -858,7 +858,7 @@ TestCase {
// Ensure that clicked is emitted when no handler is defined for the pressAndHold() signal.
// Note that even though signal spies aren't considered in QObject::isSignalConnected(),
// we can't use one here to check for pressAndHold(), because otherwise clicked() won't be emitted.
- wait(Qt.styleHints.mousePressAndHoldInterval + 100)
+ wait(Application.styleHints.mousePressAndHoldInterval + 100)
mouseRelease(control)
compare(clickedSpy.count, 1)
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_button.qml b/tests/auto/quickcontrols/controls/data/tst_button.qml
index 8720b55eff..4ca8ebfd6d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_button.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_button.qml
@@ -4,6 +4,7 @@
import QtQuick
import QtTest
import QtQuick.Controls
+import Qt.test.controls
TestCase {
id: testCase
@@ -448,12 +449,20 @@ TestCase {
verify(!textLabel)
compare(iconImage.x, (control.availableWidth - iconImage.width) / 2)
compare(iconImage.y, (control.availableHeight - iconImage.height) / 2)
+ if (StyleInfo.styleName === "Material") {
+ compare(control.leftPadding, Material.buttonLeftPadding(false, true))
+ compare(control.rightPadding, Material.buttonRightPadding(false, true, false))
+ }
break;
case Button.TextOnly:
verify(!iconImage)
verify(textLabel)
compare(textLabel.x, (control.availableWidth - textLabel.width) / 2)
compare(textLabel.y, (control.availableHeight - textLabel.height) / 2)
+ if (StyleInfo.styleName === "Material") {
+ compare(control.leftPadding, Material.buttonLeftPadding(false, false))
+ compare(control.rightPadding, Material.buttonRightPadding(false, false, true))
+ }
break;
case Button.TextUnderIcon:
verify(iconImage)
@@ -461,6 +470,10 @@ TestCase {
compare(iconImage.x, (control.availableWidth - iconImage.width) / 2)
compare(textLabel.x, (control.availableWidth - textLabel.width) / 2)
verify(iconImage.y < textLabel.y)
+ if (StyleInfo.styleName === "Material") {
+ compare(control.leftPadding, Material.buttonLeftPadding(false, true))
+ compare(control.rightPadding, Material.buttonRightPadding(false, true, true))
+ }
break;
case Button.TextBesideIcon:
verify(iconImage)
@@ -471,6 +484,10 @@ TestCase {
verify(iconImage.x < textLabel.x)
compare(iconImage.y, (control.availableHeight - iconImage.height) / 2)
compare(textLabel.y, (control.availableHeight - textLabel.height) / 2)
+ if (StyleInfo.styleName === "Material") {
+ compare(control.leftPadding, Material.buttonLeftPadding(false, true))
+ compare(control.rightPadding, Material.buttonRightPadding(false, true, true))
+ }
break;
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml b/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
index ef97405234..bec2d8ca88 100644
--- a/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
@@ -304,7 +304,9 @@ TestCase {
id: repeater
Column {
id: column
- property ButtonGroup group: ButtonGroup { buttons: column.children }
+ property ButtonGroup group: ButtonGroup {
+ buttons: column.children.filter((child) => child !== r)
+ }
property alias repeater: r
Repeater {
id: r
@@ -392,7 +394,7 @@ TestCase {
id: checkedButtonColumn
Column {
id: column
- ButtonGroup { buttons: column.children }
+ ButtonGroup { buttons: column.children.filter((child) => child !== repeater) }
Repeater {
id: repeater
delegate: Button {
diff --git a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
index 7d80484123..7a8e563131 100644
--- a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
@@ -201,7 +201,7 @@ TestCase {
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.Checked }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
@@ -220,7 +220,7 @@ TestCase {
// release outside
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
diff --git a/tests/auto/quickcontrols/controls/data/tst_combobox.qml b/tests/auto/quickcontrols/controls/data/tst_combobox.qml
index 8e671a38a6..0cc4de4d0c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_combobox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_combobox.qml
@@ -74,16 +74,7 @@ TestCase {
objectName: "ShaderFX"
width: rect.width
height: rect.height
- fragmentShader: "
- uniform lowp sampler2D source; // this item
- uniform lowp float qt_Opacity; // inherited opacity of this item
- varying highp vec2 qt_TexCoord0;
- void main() {
- lowp vec4 p = texture2D(source, qt_TexCoord0);
- lowp float g = dot(p.xyz, vec3(0.344, 0.5, 0.156));
- gl_FragColor = vec4(g, g, g, p.a) * qt_Opacity;
- }"
-
+ fragmentShader: "qrc:/data/combobox/shader.frag.qsb"
}
}
}
@@ -1956,7 +1947,7 @@ TestCase {
// and then that ComboBox loses focus, its currentIndex should change
// to the index of the edit text (assuming a match is found).
function test_currentIndexChangeOnLostFocus() {
- if (Qt.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls)
+ if (Application.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls)
skip("This platform only allows tab focus for text controls")
let theModel = []
@@ -2004,11 +1995,16 @@ TestCase {
compare(currentIndexSpy.count, 1)
}
+ readonly property font testFont: ({
+ family: "Arial",
+ pixelSize: 12
+ })
+
Component {
- id: appFontTextFieldComponent
+ id: fixedFontTextFieldComponent
TextField {
objectName: "appFontTextField"
- font: Qt.application.font
+ font: testCase.testFont
// We don't want the background's implicit width to interfere with our tests,
// which are about implicit width of the contentItem of ComboBox, which is by default TextField.
background: null
@@ -2016,14 +2012,14 @@ TestCase {
}
Component {
- id: appFontContentItemComboBoxComponent
+ id: fixedFontContentItemComboBoxComponent
ComboBox {
// Override the contentItem so that the font doesn't vary between styles.
contentItem: TextField {
objectName: "appFontContentItemTextField"
// We do this just to be extra sure that the font never comes from the control,
- // as we want it to match that of the TextField in the appFontTextFieldComponent.
- font: Qt.application.font
+ // as we want it to match that of the TextField in the fixedFontTextFieldComponent.
+ font: testCase.testFont
background: null
}
}
@@ -2077,14 +2073,14 @@ TestCase {
function test_implicitContentWidthPolicy_ContentItemImplicitWidth() {
// Set ContentItemImplicitWidth and ensure that implicitContentWidth is as wide as the current item
// by comparing it against the implicitWidth of an identical TextField
- let control = createTemporaryObject(appFontContentItemComboBoxComponent, testCase, {
+ let control = createTemporaryObject(fixedFontContentItemComboBoxComponent, testCase, {
model: ["Short", "Kinda long"],
implicitContentWidthPolicy: ComboBox.ContentItemImplicitWidth
})
verify(control)
compare(control.implicitContentWidthPolicy, ComboBox.ContentItemImplicitWidth)
- let textField = createTemporaryObject(appFontTextFieldComponent, testCase)
+ let textField = createTemporaryObject(fixedFontTextFieldComponent, testCase)
verify(textField)
// Don't set any text on textField because we're not accounting for the widest
// text here, so we want to compare it against an empty TextField.
@@ -2103,14 +2099,14 @@ TestCase {
}
function test_implicitContentWidthPolicy_WidestText(data) {
- let control = createTemporaryObject(appFontContentItemComboBoxComponent, testCase, {
+ let control = createTemporaryObject(fixedFontContentItemComboBoxComponent, testCase, {
model: data.model,
implicitContentWidthPolicy: ComboBox.WidestText
})
verify(control)
compare(control.implicitContentWidthPolicy, ComboBox.WidestText)
- let textField = createTemporaryObject(appFontTextFieldComponent, testCase)
+ let textField = createTemporaryObject(fixedFontTextFieldComponent, testCase)
verify(textField)
textField.text = "Kinda long"
// Note that we don't need to change the current index here, as the implicitContentWidth
@@ -2137,7 +2133,7 @@ TestCase {
// Changes in font should result in the implicitContentWidth being updated.
textField.font.pixelSize *= 2
// We have to change the contentItem's font size manually since we break the
- // style's binding to the control's font when we set Qt.application.font to it.
+ // style's binding to the control's font when we set the fixed font on it.
control.contentItem.font.pixelSize *= 2
control.font.pixelSize *= 2
compare(Math.ceil(control.implicitContentWidth), Math.ceil(textField.implicitWidth))
@@ -2148,14 +2144,14 @@ TestCase {
}
function test_implicitContentWidthPolicy_WidestTextWhenCompleted(data) {
- let control = createTemporaryObject(appFontContentItemComboBoxComponent, testCase, {
+ let control = createTemporaryObject(fixedFontContentItemComboBoxComponent, testCase, {
model: data.model,
implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
})
verify(control)
compare(control.implicitContentWidthPolicy, ComboBox.WidestTextWhenCompleted)
- let textField = createTemporaryObject(appFontTextFieldComponent, testCase)
+ let textField = createTemporaryObject(fixedFontTextFieldComponent, testCase)
verify(textField)
textField.text = "Kinda long"
compare(Math.ceil(control.implicitContentWidth), Math.ceil(textField.implicitWidth))
diff --git a/tests/auto/quickcontrols/controls/data/tst_control.qml b/tests/auto/quickcontrols/controls/data/tst_control.qml
index 4a2bc33b33..5c5e557f11 100644
--- a/tests/auto/quickcontrols/controls/data/tst_control.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_control.qml
@@ -1064,7 +1064,7 @@ TestCase {
verify(control)
compare(control.hovered, false)
- compare(control.hoverEnabled, Qt.styleHints.useHoverEffects)
+ compare(control.hoverEnabled, Application.styleHints.useHoverEffects)
control.hoverEnabled = false
@@ -1098,7 +1098,7 @@ TestCase {
function test_hoverEnabled() {
let control = createTemporaryObject(component, testCase)
- compare(control.hoverEnabled, Qt.styleHints.useHoverEffects)
+ compare(control.hoverEnabled, Application.styleHints.useHoverEffects)
let child = component.createObject(control)
let grandChild = component.createObject(child)
diff --git a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
index 27145cd2ab..82d4f56495 100644
--- a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
@@ -172,7 +172,7 @@ TestCase {
"pressed",
"activated"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
tryVerify(function() { return sequenceSpy.success})
@@ -190,7 +190,7 @@ TestCase {
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
["downChanged", { "down": true }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
@@ -208,7 +208,7 @@ TestCase {
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
["downChanged", { "down": true }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
diff --git a/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml b/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
index 08caac977d..47cbc7b72b 100644
--- a/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
@@ -154,7 +154,7 @@ TestCase {
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
@@ -169,7 +169,7 @@ TestCase {
// release outside
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(sequenceSpy.success)
diff --git a/tests/auto/quickcontrols/controls/data/tst_splitview.qml b/tests/auto/quickcontrols/controls/data/tst_splitview.qml
index e835a8440f..a2054000b6 100644
--- a/tests/auto/quickcontrols/controls/data/tst_splitview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_splitview.qml
@@ -74,20 +74,19 @@ TestCase {
// Note that the indices mentioned here account for handles; they do not
// match the indices reported by QQuickSplitView's logging categories.
compare(item.x, expectedGeometry.x, "Mismatch in actual vs expected x value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.y, expectedGeometry.y, "Mismatch in actual vs expected y value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.width, expectedGeometry.width, "Mismatch in actual vs expected width value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.height, expectedGeometry.height, "Mismatch in actual vs expected height value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
}
}
property real defaultHorizontalHandleWidth: 10
property real defaultVerticalHandleHeight: 10
-
Component {
id: signalSpyComponent
SignalSpy {}
@@ -96,14 +95,14 @@ TestCase {
Component {
id: handleComponent
Rectangle {
- objectName: "handle"
+ objectName: `handle ${x},${y} ${width}x${height} visible: ${visible}`
implicitWidth: defaultHorizontalHandleWidth
implicitHeight: defaultVerticalHandleHeight
color: "#444"
Text {
- objectName: "handleText_" + text
- text: parent.x + "," + parent.y + " " + parent.width + "x" + parent.height
+ objectName: text + "_Text"
+ text: parent.objectName
color: "white"
anchors.centerIn: parent
rotation: 90
@@ -2672,4 +2671,88 @@ TestCase {
verify(!firstHandle.SplitHandle.pressed)
compare(firstItem.width, 125)
}
+
+ Component {
+ id: hiddenItemComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+ orientation: Qt.Horizontal
+
+ component SplitItem: Rectangle {
+ objectName: labelText
+
+ SplitView.preferredWidth: 50
+ SplitView.fillHeight: true
+
+ required property string labelText
+
+ Text {
+ anchors.fill: parent
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: `${parent.labelText} - width: ${parent.width.toFixed(2)}`
+ }
+ }
+
+ SplitItem {
+ color: "blue"
+ labelText: "View 1"
+ }
+ SplitItem {
+ color: "red"
+ labelText: "View 2 (hidden)"
+ visible: false
+ }
+ SplitItem {
+ color: "purple"
+ labelText: "View 3"
+ }
+ SplitItem {
+ color: "yellow"
+ labelText: "View 4"
+ }
+ }
+ }
+
+ function test_resizeHiddenItem() {
+ let control = createTemporaryObject(hiddenItemComponent, testCase)
+ verify(control)
+
+ const standardItemWidth = 50
+ let expectedGeometries = [
+ // First item.
+ { x: 0, y: 0, width: standardItemWidth, height: control.height },
+ // First handle.
+ { x: standardItemWidth, y: 0, width: defaultHorizontalHandleWidth, height: control.height },
+ // The second item and its handle are hidden.
+ { hidden: true },
+ { hidden: true },
+ // Third item.
+ { x: standardItemWidth + defaultHorizontalHandleWidth, y: 0, width: standardItemWidth, height: control.height },
+ // Third handle.
+ { x: (standardItemWidth * 2) + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: control.height },
+ // Fourth item.
+ { x: (standardItemWidth * 2) + (defaultHorizontalHandleWidth * 2), y: 0,
+ width: control.width - (standardItemWidth * 2) - (defaultHorizontalHandleWidth * 2), height: control.height }
+ ]
+ compareSizes(control, expectedGeometries, "before dragging handle")
+
+ // Drag the third handle to the right.
+ let handles = findHandles(control)
+ let thirdHandle = handles[2]
+ // The third (index 4 here) item should get one pixel bigger, and the fourth one pixel smaller.
+ ++expectedGeometries[4].width
+ ++expectedGeometries[5].x // handle
+ ++expectedGeometries[6].x
+ --expectedGeometries[6].width
+ // Use individual events rather than mouseDrag because that will move it past the drag threshold,
+ // which we don't want, since we only want to move by 1 pixel.
+ mousePress(thirdHandle)
+ mouseMove(thirdHandle, thirdHandle.width / 2 + 1, thirdHandle.height / 2, 16)
+ mouseRelease(thirdHandle)
+ compareSizes(control, expectedGeometries, "after dragging handle")
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml b/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
index 8d57e37575..ddbd283c87 100644
--- a/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
@@ -14,7 +14,7 @@ TestCase {
when: windowShown
name: "SwipeDelegate"
- readonly property int dragDistance: Math.max(20, Qt.styleHints.startDragDistance + 5)
+ readonly property int dragDistance: Math.max(20, Application.styleHints.startDragDistance + 5)
Component {
id: backgroundFillComponent
@@ -655,6 +655,20 @@ TestCase {
width: 100
height: 120
+ property int rotation: 0
+
+ transform: [
+ Rotation {
+ angle: rotation
+ origin.x: 0
+ origin.y: 0
+ },
+ Translate {
+ x: (rotation === 90) ? parent.width : 0
+ y: 0
+ }
+ ]
+
model: ListModel {
ListElement { name: "Apple" }
ListElement { name: "Orange" }
@@ -712,15 +726,20 @@ TestCase {
function test_removableDelegates_data() {
return [
{ tag: "mouse", touch: false },
- { tag: "touch", touch: true }
+ { tag: "touch", touch: true },
+ { tag: "mouse_rotation_90", touch: false, rotation: 90 },
+ { tag: "touch_rotation_90", touch: true, rotation: 90 },
]
}
- function test_removableDelegates() {
- var listView = createTemporaryObject(removableDelegatesComponent, testCase);
+ function test_removableDelegates(data) {
+ let listView = createTemporaryObject(removableDelegatesComponent, testCase);
verify(listView);
compare(listView.count, 3);
+ if (data.rotation)
+ listView.rotation = data.rotation;
+
let touch = data.touch ? touchEvent(listView) : null
// Expose the remove button.
diff --git a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
index 0c69e26f12..694741ce45 100644
--- a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
@@ -561,7 +561,7 @@ TestCase {
}
function test_focus() {
- if (Qt.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls)
+ if (Application.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls)
skip("This platform only allows tab focus for text controls")
var control = createTemporaryObject(focusSwipeViewComponent, testCase)
diff --git a/tests/auto/quickcontrols/controls/data/tst_switch.qml b/tests/auto/quickcontrols/controls/data/tst_switch.qml
index 407513b914..8d6160cef1 100644
--- a/tests/auto/quickcontrols/controls/data/tst_switch.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_switch.qml
@@ -210,7 +210,7 @@ TestCase {
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -227,7 +227,7 @@ TestCase {
// release on the right
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -246,7 +246,7 @@ TestCase {
// release on the left
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -265,7 +265,7 @@ TestCase {
// release in the middle
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, 0, 0).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -426,7 +426,7 @@ TestCase {
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, 0).commit()
compare(control.position, 1.0)
compare(control.checked, true)
@@ -462,7 +462,7 @@ TestCase {
// press-drag-release from and to outside the indicator
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width - 1).commit()
compare(control.position, 0.0)
compare(control.checked, false)
diff --git a/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
index 42d58b9668..570e89a531 100644
--- a/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
@@ -201,7 +201,7 @@ TestCase {
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -218,7 +218,7 @@ TestCase {
// release on the right
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -237,7 +237,7 @@ TestCase {
// release on the left
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -256,7 +256,7 @@ TestCase {
// release in the middle
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, 0, 0).commit()
compare(control.pressed, true)
verify(spy.success)
@@ -417,7 +417,7 @@ TestCase {
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
"pressed"]
// Don't want to double-click.
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, 0).commit()
compare(control.position, 1.0)
compare(control.checked, true)
@@ -453,7 +453,7 @@ TestCase {
// press-drag-release from and to outside the indicator
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
- wait(Qt.styleHints.mouseDoubleClickInterval + 50)
+ wait(Application.styleHints.mouseDoubleClickInterval + 50)
touch.press(0, control, control.width - 1).commit()
compare(control.position, 0.0)
compare(control.checked, false)
diff --git a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
index 0bbade9c43..0491ab039e 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
@@ -1149,25 +1149,46 @@ TestCase {
}
}
- function test_setCurrentIndexOnImperativeModelChange() {
- var tumbler = createTemporaryObject(setCurrentIndexOnImperativeModelChangeComponent, testCase);
- verify(tumbler);
+ function test_setCurrentIndexOnImperativeModelChange_data() {
+ return [
+ { tag: "default wrap", setWrap: false, initialWrap: false, newWrap: true },
+ { tag: "wrap=false", setWrap: true, initialWrap: false, newWrap: false },
+ { tag: "wrap=true", setWrap: true, initialWrap: true, newWrap: true },
+ ]
+ }
- tumbler.model = 4
- compare(tumbler.count, 4);
- tumblerView = findView(tumbler);
- tryCompare(tumblerView, "count", 4);
+ function test_setCurrentIndexOnImperativeModelChange(data) {
+ let tumbler = createTemporaryObject(setCurrentIndexOnImperativeModelChangeComponent, testCase,
+ data.setWrap ? {wrap: data.initialWrap} : {})
+ verify(tumbler)
- // 4 - 2 = 2
- compare(tumbler.currentIndex, 2);
+ let model = 4
+ let expectedCurrentIndex = model - 2
- ++tumbler.model;
- compare(tumbler.count, 5);
- compare(tumbler.wrap, true);
- tumblerView = findView(tumbler);
- tryCompare(tumblerView, "count", 5);
- // 5 - 2 = 3
- compare(tumbler.currentIndex, 3);
+ tumbler.model = model
+
+ compare(tumbler.count, model)
+ compare(tumbler.wrap, data.initialWrap)
+ compare(tumbler.currentIndex, expectedCurrentIndex)
+
+ let tumblerView = findView(tumbler)
+ verify(tumblerView)
+ tryCompare(tumblerView, "count", model)
+ tryCompare(tumblerView, "currentIndex", expectedCurrentIndex)
+
+ model = 5
+ expectedCurrentIndex = model - 2
+
+ tumbler.model = model
+
+ compare(tumbler.count, model)
+ compare(tumbler.wrap, data.newWrap)
+ compare(tumbler.currentIndex, expectedCurrentIndex)
+
+ tumblerView = findView(tumbler)
+ verify(tumblerView)
+ tryCompare(tumblerView, "count", model)
+ tryCompare(tumblerView, "currentIndex", expectedCurrentIndex)
}
Component {
@@ -1176,35 +1197,59 @@ TestCase {
Item {
property alias tumbler: tumbler
- property int setting: 4
+ required property int modelValue
+ property alias tumblerWrap: tumbler.wrap
Tumbler {
id: tumbler
- model: setting
+ model: modelValue
onModelChanged: currentIndex = model - 2
}
}
}
- function test_setCurrentIndexOnDeclarativeModelChange() {
- var root = createTemporaryObject(setCurrentIndexOnDeclarativeModelChangeComponent, testCase);
- verify(root);
+ function test_setCurrentIndexOnDeclarativeModelChange_data() {
+ return [
+ { tag: "default wrap", setWrap: false, initialWrap: false, newWrap: true },
+ { tag: "wrap=false", setWrap: true, initialWrap: false, newWrap: false },
+ { tag: "wrap=true", setWrap: true, initialWrap: true, newWrap: true },
+ ]
+ }
- var tumbler = root.tumbler;
- compare(tumbler.count, 4);
- compare(tumbler.wrap, false);
- tumblerView = findView(tumbler);
- tryCompare(tumblerView, "count", 4);
- // 4 - 2 = 2
- compare(tumbler.currentIndex, 2);
+ function test_setCurrentIndexOnDeclarativeModelChange(data) {
+ let model = 4
+ let expectedCurrentIndex = model - 2
- ++root.setting;
- compare(tumbler.count, 5);
- compare(tumbler.wrap, true);
- tumblerView = findView(tumbler);
- tryCompare(tumblerView, "count", 5);
- // 5 - 2 = 3
- compare(tumbler.currentIndex, 3);
+ let root = createTemporaryObject(setCurrentIndexOnDeclarativeModelChangeComponent, testCase,
+ data.setWrap ? {modelValue: model, tumblerWrap: data.initialWrap}
+ : {modelValue: model})
+ verify(root)
+
+ let tumbler = root.tumbler
+ verify(tumbler)
+
+ compare(tumbler.count, model)
+ compare(tumbler.wrap, data.initialWrap)
+ compare(tumbler.currentIndex, expectedCurrentIndex)
+
+ let tumberView = findView(tumbler)
+ verify(tumbler)
+ tryCompare(tumberView, "count", model)
+ tryCompare(tumberView, "currentIndex", expectedCurrentIndex)
+
+ model = 5
+ expectedCurrentIndex = model - 2
+
+ root.modelValue = model
+
+ compare(tumbler.count, model)
+ compare(tumbler.wrap, data.newWrap)
+ compare(tumbler.currentIndex, expectedCurrentIndex)
+
+ tumberView = findView(tumbler)
+ verify(tumbler)
+ tryCompare(tumberView, "count", model)
+ tryCompare(tumberView, "currentIndex", expectedCurrentIndex)
}
function test_displacementAfterResizing() {
@@ -1237,15 +1282,46 @@ TestCase {
height: 200
delegate: Text {text: modelData}
model: 10
- currentIndex: 4
+ currentIndex: 1
}
}
+ //QTBUG-127315
+ Component {
+ id: initialCurrentIndexTumblerNoDelegate
- function test_initialCurrentIndex() {
- var tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: true});
- compare(tumbler.currentIndex, 4);
- tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: false});
- compare(tumbler.currentIndex, 4);
+ Tumbler {
+ anchors.centerIn: parent
+ width: 60
+ height: 200
+ model: 10
+ }
+ }
+
+ function test_initialCurrentIndex_data() {
+ return [
+ { tag: "delegate: true, wrap: true, currentIndex: 1",
+ component: initialCurrentIndexTumbler, wrap: true, currentIndex: 1 },
+ { tag: "delegate: true, wrap: false, currentIndex: 1",
+ component: initialCurrentIndexTumbler, wrap: false, currentIndex: 1 },
+ { tag: "delegate: false, wrap: true, currentIndex: 1",
+ component: initialCurrentIndexTumblerNoDelegate, wrap: true, currentIndex: 1 },
+ { tag: "delegate: false, wrap: false, currentIndex: 1",
+ component: initialCurrentIndexTumblerNoDelegate, wrap: false, currentIndex: 1 },
+ { tag: "delegate: true, wrap: true, currentIndex: 4",
+ component: initialCurrentIndexTumbler, wrap: true, currentIndex: 4 },
+ { tag: "delegate: true, wrap: false, currentIndex: 4",
+ component: initialCurrentIndexTumbler, wrap: false, currentIndex: 4 },
+ { tag: "delegate: false, wrap: true, currentIndex: 4",
+ component: initialCurrentIndexTumblerNoDelegate, wrap: true, currentIndex: 4 },
+ { tag: "delegate: false, wrap: false, currentIndex: 4",
+ component: initialCurrentIndexTumblerNoDelegate, wrap: false, currentIndex: 4 },
+ ]
+ }
+
+ function test_initialCurrentIndex(data) {
+ let tumbler = createTemporaryObject(data.component, testCase,
+ {wrap: data.wrap, currentIndex: data.currentIndex});
+ compare(tumbler.currentIndex, data.currentIndex);
}
// QTBUG-109995
diff --git a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
index 5495efd858..e3d38e1cf3 100644
--- a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
@@ -43,6 +43,9 @@ set_source_files_properties(${test_data}
HEADER_FILE_ONLY ON
)
+set(test_target tst_fusion)
+include(../shared.cmake)
+
#### Keys ignored in scope 1:.:.:fusion.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
index 6efb1a0a45..c296a5b1a4 100644
--- a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
@@ -43,6 +43,9 @@ set_source_files_properties(${test_data}
HEADER_FILE_ONLY ON
)
+set(test_target tst_imagine)
+include(../shared.cmake)
+
#### Keys ignored in scope 1:.:.:imagine.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
index fe6839990f..4e0cb72e68 100644
--- a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
@@ -26,3 +26,5 @@ qt_internal_add_test(tst_ios
TESTDATA ${test_data}
)
+set(test_target tst_ios)
+include(../shared.cmake)
diff --git a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
index 04d186c63f..3202601316 100644
--- a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
@@ -36,3 +36,6 @@ set_source_files_properties(${test_data}
PROPERTIES
HEADER_FILE_ONLY ON
)
+
+set(test_target tst_macos)
+include(../shared.cmake)
diff --git a/tests/auto/quickcontrols/controls/material/CMakeLists.txt b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
index 3e87bf1609..506509c774 100644
--- a/tests/auto/quickcontrols/controls/material/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_material LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS ShaderTools STANDALONE_TEST)
endif()
#####################################################################
@@ -43,6 +43,9 @@ set_source_files_properties(${test_data}
HEADER_FILE_ONLY ON
)
+set(test_target tst_material)
+include(../shared.cmake)
+
#### Keys ignored in scope 1:.:.:material.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols/controls/shared.cmake b/tests/auto/quickcontrols/controls/shared.cmake
new file mode 100644
index 0000000000..ca62b88db6
--- /dev/null
+++ b/tests/auto/quickcontrols/controls/shared.cmake
@@ -0,0 +1,12 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt6_add_shaders(${test_target} "${test_target}_shaders"
+ BATCHABLE
+ PREFIX
+ "/"
+ BASE
+ "../"
+ FILES
+ "../data/combobox/shader.frag"
+)
diff --git a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
index f0c7d2ed80..f39975ab15 100644
--- a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
@@ -43,6 +43,9 @@ set_source_files_properties(${test_data}
HEADER_FILE_ONLY ON
)
+set(test_target tst_universal)
+include(../shared.cmake)
+
#### Keys ignored in scope 1:.:.:universal.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
index a5ba30a51b..1d6ab1fc57 100644
--- a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
@@ -36,3 +36,6 @@ set_source_files_properties(${test_data}
PROPERTIES
HEADER_FILE_ONLY ON
)
+
+set(test_target tst_windows)
+include(../shared.cmake)
diff --git a/tests/auto/quickcontrols/focus/tst_focus.cpp b/tests/auto/quickcontrols/focus/tst_focus.cpp
index ca70146885..78db22ffbc 100644
--- a/tests/auto/quickcontrols/focus/tst_focus.cpp
+++ b/tests/auto/quickcontrols/focus/tst_focus.cpp
@@ -29,6 +29,7 @@ public:
tst_focus();
private slots:
+ void init() override;
void initTestCase() override;
void navigation_data();
@@ -50,8 +51,14 @@ tst_focus::tst_focus()
{
}
+void tst_focus::init()
+{
+ QTest::failOnWarning(QRegularExpression(".?"));
+}
+
void tst_focus::initTestCase()
{
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickStyle::setStyle("Basic");
QQmlDataTest::initTestCase();
}
diff --git a/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml
new file mode 100644
index 0000000000..436d3cdad6
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias topLevelComboBox: topLevelComboBox
+ property alias popup: popup
+ property alias comboBoxInPopup: comboBoxInPopup
+
+ ComboBox {
+ id: topLevelComboBox
+ model: ["ONE", "TWO", "THREE"]
+ }
+
+ Popup {
+ id: popup
+ width: 200
+ height: 200
+ visible: true
+ palette.window: "red"
+
+ ComboBox {
+ id: comboBoxInPopup
+ model: ["ONE", "TWO", "THREE"]
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/comboBoxPopupWithThemeDefault.qml b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithThemeDefault.qml
new file mode 100644
index 0000000000..592793fa3f
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithThemeDefault.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias comboBox: comboBox
+
+ ComboBox {
+ id: comboBox
+ model: 1
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml
new file mode 100644
index 0000000000..d806f30d01
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ property alias topLevelComboBox: topLevelComboBox
+ property alias popup: popup
+ property alias comboBoxInPopup: comboBoxInPopup
+
+ ComboBox {
+ id: topLevelComboBox
+ model: ["ONE", "TWO", "THREE"]
+ }
+
+ Popup {
+ id: popup
+ width: 200
+ height: 200
+ visible: true
+ palette.window: "red"
+
+ ComboBox {
+ id: comboBoxInPopup
+ model: ["ONE", "TWO", "THREE"]
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/toolTipPaletteUpdate.qml b/tests/auto/quickcontrols/palette/data/toolTipPaletteUpdate.qml
new file mode 100644
index 0000000000..3968c54cd0
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/toolTipPaletteUpdate.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+
+ palette { toolTipBase: "white"; toolTipText: "black"}
+
+ Button {
+ objectName: "button"
+ text: qsTr("Button with Tooltip")
+
+ ToolTip.visible: false
+ ToolTip.text: qsTr("This is a tool tip.")
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/tst_palette.cpp b/tests/auto/quickcontrols/palette/tst_palette.cpp
index d8f4bfd804..621475b86e 100644
--- a/tests/auto/quickcontrols/palette/tst_palette.cpp
+++ b/tests/auto/quickcontrols/palette/tst_palette.cpp
@@ -7,17 +7,21 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
+#include <QtQuickTemplates2/private/qquicktooltip_p.h>
#include <QtQuickControls2/qquickstyle.h>
+using namespace QQuickVisualTestUtils;
using namespace QQuickControlsTestUtils;
// Need a more descriptive failure message: QTBUG-87039
@@ -55,6 +59,13 @@ private slots:
void resolve();
void updateBindingPalette();
+
+ void comboBoxPopup_data();
+ void comboBoxPopup();
+ void comboBoxPopupWithThemeDefault_data();
+ void comboBoxPopupWithThemeDefault();
+
+ void toolTipPaletteUpdate();
};
tst_palette::tst_palette()
@@ -485,6 +496,120 @@ void tst_palette::updateBindingPalette()
QCOMPARE(windowPalette->buttonText(), customPalette->buttonText());
}
+void tst_palette::comboBoxPopup_data()
+{
+ QTest::addColumn<QString>("style");
+ QTest::addColumn<QString>("qmlFilePath");
+
+ QTest::newRow("Window, Basic") << "Basic" << "comboBoxPopupWithWindow.qml";
+ QTest::newRow("ApplicationWindow, Basic") << "Basic" << "comboBoxPopupWithApplicationWindow.qml";
+ QTest::newRow("Window, Fusion") << "Fusion" << "comboBoxPopupWithWindow.qml";
+ QTest::newRow("ApplicationWindow, Fusion") << "Fusion" << "comboBoxPopupWithApplicationWindow.qml";
+}
+
+// Unlike regular popups, which should inherit their palette from the window and not the parent popup,
+// combo box popups should inherit their palette from the combo box itself.
+void tst_palette::comboBoxPopup()
+{
+ QFETCH(QString, style);
+ QFETCH(QString, qmlFilePath);
+
+ qmlClearTypeRegistrations();
+ QQuickStyle::setStyle(style);
+
+ QQuickApplicationHelper helper(this, qmlFilePath);
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ const auto *windowPalette = window->property("palette").value<QQuickPalette *>();
+ QVERIFY(windowPalette);
+
+ const auto *popup = window->property("popup").value<QQuickPopup *>();
+ QVERIFY(popup);
+ const auto *popupBackground = popup->background();
+ QCOMPARE(popupBackground->property("color"), QColorConstants::Red);
+ QCOMPARE(popupBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+
+ // This has the default palette.
+ const auto *topLevelComboBox = window->property("topLevelComboBox").value<QQuickComboBox *>();
+ QVERIFY(topLevelComboBox);
+ const auto *topLevelComboBoxBackground = topLevelComboBox->popup()->background();
+ QCOMPARE_NE(topLevelComboBoxBackground->property("color"), QColorConstants::Red);
+ QCOMPARE_NE(topLevelComboBoxBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+
+ // The popup that this combo box is in has its window role set to red,
+ // so the combo box's popup background should be red too.
+ const auto *comboBoxInPopup = window->property("comboBoxInPopup").value<QQuickComboBox *>();
+ QVERIFY(comboBoxInPopup);
+ const auto *comboBoxInPopupBackground = comboBoxInPopup->popup()->background();
+ QCOMPARE(comboBoxInPopupBackground->property("color"), QColorConstants::Red);
+ QCOMPARE(comboBoxInPopupBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+}
+
+void tst_palette::comboBoxPopupWithThemeDefault_data()
+{
+ QTest::addColumn<QString>("style");
+ QTest::addColumn<QColor>("expectedComboBoxPopupBackgroundColor");
+
+ QTest::newRow("Basic") << "Basic" << QColor::fromRgb(0xFFFFFF);
+
+ // We can't test Fusion because it uses the default application palette,
+ // which is the default-constructed QPalette, so the test would always pass.
+}
+
+void tst_palette::comboBoxPopupWithThemeDefault()
+{
+ QFETCH(QString, style);
+ QFETCH(QColor, expectedComboBoxPopupBackgroundColor);
+
+ qmlClearTypeRegistrations();
+ QQuickStyle::setStyle(style);
+
+ QQuickApplicationHelper helper(this, "comboBoxPopupWithThemeDefault.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ const auto *comboBox = window->property("comboBox").value<QQuickComboBox *>();
+ QVERIFY(comboBox);
+ const auto *comboBoxBackground = comboBox->popup()->background();
+ QCOMPARE(comboBoxBackground->property("color"), expectedComboBoxPopupBackgroundColor);
+}
+
+void tst_palette::toolTipPaletteUpdate()
+{
+ QQuickApplicationHelper helper(this, "toolTipPaletteUpdate.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *button = window->findChild<QQuickButton *>("button");
+ QVERIFY(button);
+ auto *attachedToolTip = button->findChild<QQuickToolTipAttached *>();
+ QVERIFY(attachedToolTip);
+ auto *toolTip = attachedToolTip->toolTip();
+ QVERIFY(toolTip);
+
+ auto windowPalette = QQuickWindowPrivate::get(window)->palette();
+ auto toolTipPalette = QQuickPopupPrivate::get(toolTip)->palette();
+
+ QCOMPARE(toolTipPalette->toolTipBase(), windowPalette->toolTipBase());
+ QCOMPARE(toolTipPalette->toolTipText(), windowPalette->toolTipText());
+
+ windowPalette->setToolTipBase(Qt::blue);
+ windowPalette->setToolTipText(Qt::red);
+
+ QCOMPARE(toolTipPalette->toolTipBase(), windowPalette->toolTipBase());
+ QCOMPARE(toolTipPalette->toolTipText(), windowPalette->toolTipText());
+}
+
QTEST_MAIN(tst_palette)
#include "tst_palette.moc"
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml
index 0e4ed277d0..db5470986f 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml
@@ -9,6 +9,7 @@ ApplicationWindow {
height: 400
background: Item {
+ objectName: "background"
implicitWidth: 123
implicitHeight: 456
}
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/explicitBackgroundSizeBinding.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/explicitBackgroundSizeBinding.qml
new file mode 100644
index 0000000000..67fdf7808c
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/explicitBackgroundSizeBinding.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 600
+ height: 400
+
+ property real scaleFactor: 1
+
+ background: Rectangle {
+ objectName: "background"
+ color: "green"
+ width: window.width * window.scaleFactor
+ height: window.height * window.scaleFactor
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
index c7a5df4b68..a02ab9c595 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
@@ -12,6 +12,7 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
@@ -53,6 +54,7 @@ private slots:
void componentComplete();
void opacity();
void backgroundSize();
+ void explicitBackgroundSizeBinding();
};
tst_QQuickApplicationWindow::tst_QQuickApplicationWindow()
@@ -978,6 +980,23 @@ void tst_QQuickApplicationWindow::backgroundSize()
QCOMPARE(background->height(), 678);
}
+void tst_QQuickApplicationWindow::explicitBackgroundSizeBinding()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("explicitBackgroundSizeBinding.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *background = window->background();
+ QCOMPARE(background->width(), window->width());
+ QCOMPARE(background->height(), window->height());
+
+ window->setProperty("scaleFactor", 0.5);
+ QCOMPARE(background->width(), window->width() / 2);
+ QCOMPARE(background->height(), window->height() / 2);
+}
+
QTEST_MAIN(tst_QQuickApplicationWindow)
#include "tst_qquickapplicationwindow.moc"
diff --git a/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt b/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt
new file mode 100644
index 0000000000..c79e508cd1
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcontainer LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*
+)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickcontainer
+ SOURCES
+ tst_qquickcontainer.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::QuickControls2
+ Qt::QuickControlsTestUtilsPrivate
+ Qt::QuickPrivate
+ Qt::QuickTemplates2Private
+ Qt::QuickTest
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qquickcontainer CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qquickcontainer CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithListView.qml b/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithListView.qml
new file mode 100644
index 0000000000..43c3614866
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithListView.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias container: container
+ property alias text1: text1
+ property alias text2: text2
+ property alias text3: text3
+
+ component TextItem: Text {
+ font.pointSize: 24
+ width: container.width
+ height: container.height
+ }
+
+ Component {
+ id: textComponent
+ TextItem {}
+ }
+
+ function addTextItem() {
+ container.addItem(textComponent.createObject(container, { text: " 4 " }))
+ }
+
+ Item {
+ id: root
+ objectName: "root"
+
+ Container {
+ id: container
+ anchors.fill: parent
+ contentItem: ListView {
+ model: container.contentModel
+ snapMode: ListView.SnapOneItem
+ orientation: ListView.Horizontal
+ }
+
+ TextItem {
+ id: text1
+ text: "1 "
+ }
+ TextItem {
+ id: text2
+ text: " 2 "
+ }
+ TextItem {
+ id: text3
+ text: " 3 "
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithRepeater.qml b/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithRepeater.qml
new file mode 100644
index 0000000000..f78b6e273c
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontainer/data/zeroSizeWithRepeater.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias container: container
+ property alias text1: text1
+ property alias text2: text2
+ property alias text3: text3
+
+ component TextItem: Text {
+ font.pointSize: 24
+ width: container.width
+ height: container.height
+ }
+
+ Component {
+ id: textComponent
+ TextItem {}
+ }
+
+ function addTextItem() {
+ container.addItem(textComponent.createObject(container, { text: " 4 " }))
+ }
+
+ Item {
+ id: root
+ objectName: "root"
+
+ Container {
+ id: container
+ anchors.fill: parent
+ contentItem: Row {
+ Repeater {
+ model: container.contentModel
+ }
+ }
+
+ TextItem {
+ id: text1
+ text: "1 "
+ }
+ TextItem {
+ id: text2
+ text: " 2 "
+ }
+ TextItem {
+ id: text3
+ text: " 3 "
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp
new file mode 100644
index 0000000000..ce95f2cc37
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp
@@ -0,0 +1,107 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+#include <QtQuickControls2/qquickstyle.h>
+#include <QtQuickTest/QtQuickTest>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
+
+using namespace QQuickVisualTestUtils;
+using namespace QQuickControlsTestUtils;
+
+class tst_qquickcontainer : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_qquickcontainer();
+
+private slots:
+ void zeroSize_data();
+ void zeroSize();
+};
+
+tst_qquickcontainer::tst_qquickcontainer()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+ qputenv("QML_NO_TOUCH_COMPRESSION", "1");
+
+ QQuickStyle::setStyle("Basic");
+}
+
+void tst_qquickcontainer::zeroSize_data()
+{
+ QTest::addColumn<QString>("qmlFileName");
+ QTest::addColumn<bool>("isItemView");
+
+ QTest::newRow("ListView") << "zeroSizeWithListView.qml" << true;
+ // See QQuickContainerPrivate::maybeCullItem for why this is false.
+ QTest::newRow("Repeater") << "zeroSizeWithRepeater.qml" << false;
+}
+
+// Tests that a zero-size Container with a QQuickItemView sub-class culls its items.
+// Based on a use case involving SwipeView: QTBUG-125416
+void tst_qquickcontainer::zeroSize()
+{
+ QFETCH(QString, qmlFileName);
+ QFETCH(bool, isItemView);
+
+ QQuickControlsApplicationHelper helper(this, qmlFileName);
+ QVERIFY2(helper.ready, helper.failureMessage());
+ centerOnScreen(helper.window);
+ helper.window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(helper.window));
+
+ auto *text1 = helper.window->property("text1").value<QQuickItem *>();
+ QVERIFY(text1);
+ QCOMPARE(QQuickItemPrivate::get(text1)->culled, isItemView);
+
+ auto *text2 = helper.window->property("text2").value<QQuickItem *>();
+ QVERIFY(text2);
+ QCOMPARE(QQuickItemPrivate::get(text2)->culled, isItemView);
+
+ auto *text3 = helper.window->property("text3").value<QQuickItem *>();
+ QVERIFY(text3);
+ QCOMPARE(QQuickItemPrivate::get(text3)->culled, isItemView);
+
+ // Add an item and check that it's culled appropriately.
+ QVERIFY(QMetaObject::invokeMethod(helper.window, "addTextItem"));
+ auto *container = helper.window->property("container").value<QQuickContainer *>();
+ QVERIFY(container);
+ auto *text4 = container->itemAt(3);
+ QVERIFY(text4);
+ QCOMPARE(QQuickItemPrivate::get(text4)->culled, isItemView);
+
+ // Give it a non-zero size (via its parent, which it fills).
+ container->parentItem()->setWidth(text1->implicitWidth());
+ container->parentItem()->setHeight(text1->implicitHeight());
+ if (isItemView) {
+ QVERIFY(QQuickTest::qIsPolishScheduled(helper.window));
+ QVERIFY(QQuickTest::qWaitForPolish(helper.window));
+ }
+ QCOMPARE(QQuickItemPrivate::get(text1)->culled, false);
+ // This one won't be culled for views either, because of cacheBuffer (and
+ // clipping apparently doesn't affect culling, if we were to set clip to true).
+ QCOMPARE(QQuickItemPrivate::get(text2)->culled, false);
+ QCOMPARE(QQuickItemPrivate::get(text3)->culled, isItemView);
+ QCOMPARE(QQuickItemPrivate::get(text4)->culled, isItemView);
+
+ // Go back to a zero size.
+ container->parentItem()->setWidth(0);
+ container->parentItem()->setHeight(0);
+ if (isItemView) {
+ QVERIFY(QQuickTest::qIsPolishScheduled(helper.window));
+ QVERIFY(QQuickTest::qWaitForPolish(helper.window));
+ }
+ QCOMPARE(QQuickItemPrivate::get(text1)->culled, isItemView);
+ QCOMPARE(QQuickItemPrivate::get(text2)->culled, isItemView);
+ QCOMPARE(QQuickItemPrivate::get(text3)->culled, isItemView);
+ QCOMPARE(QQuickItemPrivate::get(text4)->culled, isItemView);
+}
+
+QTEST_MAIN(tst_qquickcontainer)
+
+#include "tst_qquickcontainer.moc"
diff --git a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
index ee0c6262d8..3e88bdb2b0 100644
--- a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
@@ -1051,8 +1051,7 @@ void tst_QQuickDrawer::interactive_data()
void tst_QQuickDrawer::interactive()
{
- if (!(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)))
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QFETCH(QString, source);
QQuickControlsApplicationHelper helper(this, source);
diff --git a/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml b/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
index 8929b00275..091d087dbb 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
@@ -5,14 +5,18 @@ import QtQuick
import QtQuick.Controls
ApplicationWindow {
+ id: root
width: 400
height: 400
+ property bool enabled: true
+ property bool checkable: false
property alias menu: menu
property alias action: action
property alias menuItem: menuItem
property alias subMenu: subMenu
property alias subMenuItem: subMenuItem
+ property alias subMenuAction: subMenuAction
Menu {
id: menu
@@ -20,20 +24,33 @@ ApplicationWindow {
Action {
id: action
text: "&Action"
+ checkable: root.checkable
+ enabled: root.enabled
}
MenuItem {
id: menuItem
text: "Menu &Item"
+ checkable: root.checkable
+ enabled: root.enabled
}
Menu {
id: subMenu
title: "Sub &Menu"
+ Action {
+ id: subMenuAction
+ text: "S&ub Menu Action"
+ checkable: root.checkable
+ enabled: root.enabled
+ }
+
MenuItem {
id: subMenuItem
text: "&Sub Menu Item"
+ checkable: root.checkable
+ enabled: root.enabled
}
}
}
diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
index fbb4e7d5f9..f54678b686 100644
--- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
@@ -47,6 +47,10 @@ private slots:
void contextMenuKeyboard();
void disabledMenuItemKeyNavigation();
void mnemonics();
+#if QT_CONFIG(shortcut)
+ void checkableMnemonics_data();
+ void checkableMnemonics();
+#endif
void menuButton();
void addItem();
void menuSeparator();
@@ -87,9 +91,6 @@ private slots:
void customMenuCullItems();
void customMenuUseRepeaterAsTheContentItem();
void invalidUrlInImgTag();
-
-private:
- static bool hasWindowActivation();
};
tst_QQuickMenu::tst_QQuickMenu()
@@ -97,11 +98,6 @@ tst_QQuickMenu::tst_QQuickMenu()
{
}
-bool tst_QQuickMenu::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
void tst_QQuickMenu::defaults()
{
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
@@ -146,8 +142,7 @@ void tst_QQuickMenu::count()
void tst_QQuickMenu::mouse()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
@@ -278,8 +273,7 @@ void tst_QQuickMenu::pressAndHold()
void tst_QQuickMenu::contextMenuKeyboard()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -468,8 +462,7 @@ void tst_QQuickMenu::contextMenuKeyboard()
// QTBUG-70181
void tst_QQuickMenu::disabledMenuItemKeyNavigation()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -535,8 +528,7 @@ void tst_QQuickMenu::disabledMenuItemKeyNavigation()
void tst_QQuickMenu::mnemonics()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
#ifdef Q_OS_MACOS
QSKIP("Mnemonics are not used on macOS");
@@ -591,10 +583,207 @@ void tst_QQuickMenu::mnemonics()
QCOMPARE(subMenuItemSpy.size(), 1);
}
+#if QT_CONFIG(shortcut)
+namespace CheckableMnemonics {
+using MnemonicKey = std::pair<Qt::Key, QString>;
+
+enum class MenuItemType {
+ Action,
+ MenuItem
+};
+
+enum class SignalName {
+ CheckedChanged = 0x01,
+ Triggered = 0x02,
+};
+Q_DECLARE_FLAGS(SignalNames, SignalName);
+
+class ItemSignalSpy
+{
+public:
+ ItemSignalSpy(MenuItemType type, QObject *item) : item(item)
+ {
+ switch (type) {
+ case MenuItemType::Action:
+ initSignals<QQuickAction>(qobject_cast<QQuickAction *>(item));
+ break;
+ case MenuItemType::MenuItem:
+ initSignals<QQuickMenuItem>(qobject_cast<QQuickMenuItem *>(item));
+ break;
+ }
+ }
+
+ [[nodiscard]] bool isValid() const
+ {
+ return ((checkedChangedSpy && checkedChangedSpy->isValid()) &&
+ (triggeredSpy && triggeredSpy->isValid()));
+ }
+
+ [[nodiscard]] int signalSize(SignalName signal) const
+ {
+ constexpr int INVALID_SIZE = -1; // makes the test fail even when the signal is not expected
+ switch (signal) {
+ case SignalName::CheckedChanged:
+ return checkedChangedSpy ? checkedChangedSpy->size() : INVALID_SIZE;
+ case SignalName::Triggered:
+ return triggeredSpy ? triggeredSpy->size() : INVALID_SIZE;
+ }
+ Q_UNREACHABLE_RETURN(INVALID_SIZE);
+ }
+
+private:
+ template<typename Item>
+ void initSignals(Item *item)
+ {
+ checkedChangedSpy = std::make_unique<QSignalSpy>(item, &Item::checkedChanged);
+ triggeredSpy = std::make_unique<QSignalSpy>(item, &Item::triggered);
+ }
+
+private:
+ QPointer<QObject> item;
+ std::unique_ptr<QSignalSpy> checkedChangedSpy = nullptr;
+ std::unique_ptr<QSignalSpy> triggeredSpy = nullptr;
+};
+
+}
+
+void tst_QQuickMenu::checkableMnemonics_data()
+{
+ if (QKeySequence::mnemonic("&A").isEmpty())
+ QSKIP("Mnemonics are not enabled");
+
+ using namespace CheckableMnemonics;
+
+ QTest::addColumn<bool>("checkable");
+ QTest::addColumn<bool>("enabled");
+ QTest::addColumn<bool>("isSubMenu");
+ QTest::addColumn<MenuItemType>("itemType");
+ QTest::addColumn<MnemonicKey>("mnemonicKey");
+ QTest::addColumn<SignalNames>("expectedSignals");
+
+ QTest::addRow("checkable_enabled_action")
+ << true << true << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
+ << SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
+ QTest::addRow("checkable_disabled_action")
+ << true << false << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
+ << SignalNames{};
+ QTest::addRow("uncheckable_enabled_action")
+ << false << true << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
+ << SignalNames{SignalName::Triggered};
+ QTest::addRow("uncheckable_disabled_action")
+ << false << false << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
+ << SignalNames{};
+
+ QTest::addRow("checkable_enabled_menuItem")
+ << true << true << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
+ << SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
+ QTest::addRow("checkable_disabled_menuItem")
+ << true << false << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
+ << SignalNames{};
+ QTest::addRow("uncheckable_enabled_menuItem")
+ << false << true << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
+ << SignalNames{SignalName::Triggered};
+ QTest::addRow("uncheckable_disabled_menuItem")
+ << false << false << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
+ << SignalNames{};
+
+ QTest::addRow("checkable_enabled_subMenuItem")
+ << true << true << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
+ << SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
+ QTest::addRow("checkable_disabled_subMenuItem")
+ << true << false << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
+ << SignalNames{};
+ QTest::addRow("uncheckable_enabled_subMenuItem")
+ << false << true << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
+ << SignalNames{SignalName::Triggered};
+ QTest::addRow("uncheckable_disabled_subMenuItem")
+ << false << false << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
+ << SignalNames{};
+
+ QTest::addRow("checkable_enabled_subMenuAction")
+ << true << true << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
+ << SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
+ QTest::addRow("checkable_disabled_subMenuAction")
+ << true << false << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
+ << SignalNames{};
+ QTest::addRow("uncheckable_enabled_subMenuAction")
+ << false << true << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
+ << SignalNames{SignalName::Triggered};
+ QTest::addRow("uncheckable_disabled_subMenuAction")
+ << false << false << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
+ << SignalNames{};
+}
+
+// QTBUG-96630
+void tst_QQuickMenu::checkableMnemonics()
+{
+ using namespace CheckableMnemonics;
+
+ QFETCH(bool, checkable);
+ QFETCH(bool, enabled);
+ QFETCH(bool, isSubMenu);
+ QFETCH(MenuItemType, itemType);
+ QFETCH(MnemonicKey, mnemonicKey);
+ QFETCH(SignalNames, expectedSignals);
+
+ QQuickControlsApplicationHelper helper(this, QLatin1String("mnemonics.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ window->setProperty("checkable", checkable);
+ window->setProperty("enabled", enabled);
+
+ QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
+ QVERIFY(menu);
+
+ auto clickKey = [window](const MnemonicKey &mnemonic) mutable {
+ QTest::simulateEvent(window, true, mnemonic.first, Qt::NoModifier, mnemonic.second, false);
+ QTest::simulateEvent(window, false, mnemonic.first, Qt::NoModifier, mnemonic.second, false);
+ };
+
+ constexpr auto EMPTY_ITEM_NAME = "";
+ const char *itemName = EMPTY_ITEM_NAME;
+ switch (itemType) {
+ case MenuItemType::Action:
+ itemName = isSubMenu ? "subMenuAction" : "action";
+ break;
+ case MenuItemType::MenuItem:
+ itemName = isSubMenu ? "subMenuItem" : "menuItem";
+ break;
+ }
+ QCOMPARE_NE(itemName, EMPTY_ITEM_NAME);
+
+ QObject *menuItem = window->property(itemName).value<QObject*>();
+ QVERIFY(menuItem);
+
+ menu->open();
+ QTRY_VERIFY(menu->isOpened());
+
+ if (isSubMenu) {
+ QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>();
+ QVERIFY(subMenu);
+ clickKey(MnemonicKey{Qt::Key_M, "M"}); // "Sub &Menu"
+ QTRY_VERIFY(subMenu->isOpened());
+ }
+
+ const ItemSignalSpy itemSignalSpy(itemType, menuItem);
+ QVERIFY(itemSignalSpy.isValid());
+
+ clickKey(mnemonicKey);
+ QCOMPARE(itemSignalSpy.signalSize(SignalName::CheckedChanged),
+ expectedSignals & SignalName::CheckedChanged ? 1 : 0);
+ QCOMPARE(itemSignalSpy.signalSize(SignalName::Triggered),
+ expectedSignals & SignalName::Triggered ? 1 : 0);
+}
+#endif
+
void tst_QQuickMenu::menuButton()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -648,8 +837,7 @@ void tst_QQuickMenu::addItem()
void tst_QQuickMenu::menuSeparator()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, QLatin1String("menuSeparator.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1037,8 +1225,7 @@ void tst_QQuickMenu::actions()
#if QT_CONFIG(shortcut)
void tst_QQuickMenu::actionShortcuts()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, QLatin1String("actionShortcuts.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1332,8 +1519,7 @@ void tst_QQuickMenu::subMenuKeyboard_data()
void tst_QQuickMenu::subMenuKeyboard()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QFETCH(bool, cascade);
QFETCH(bool, mirrored);
@@ -1461,8 +1647,7 @@ void tst_QQuickMenu::subMenuDisabledKeyboard_data()
// QTBUG-69540
void tst_QQuickMenu::subMenuDisabledKeyboard()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QFETCH(bool, cascade);
QFETCH(bool, mirrored);
@@ -2050,8 +2235,7 @@ void tst_QQuickMenu::menuItemWidthAfterRetranslate()
void tst_QQuickMenu::giveMenuItemFocusOnButtonPress()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, QLatin1String("giveMenuItemFocusOnButtonPress.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
diff --git a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
index bbace286a8..b331fda1a3 100644
--- a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
@@ -38,8 +38,6 @@ private slots:
void checkHighlightWhenMenuDismissed();
private:
- static bool hasWindowActivation();
-
QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
@@ -54,11 +52,6 @@ tst_qquickmenubar::tst_qquickmenubar()
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
}
-bool tst_qquickmenubar::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
void tst_qquickmenubar::delegate()
{
QQmlApplicationEngine engine(testFileUrl("empty.qml"));
@@ -74,8 +67,7 @@ void tst_qquickmenubar::delegate()
void tst_qquickmenubar::mouse()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
@@ -286,8 +278,7 @@ void tst_qquickmenubar::touch()
void tst_qquickmenubar::keys()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQmlApplicationEngine engine(testFileUrl("menubar.qml"));
@@ -478,8 +469,7 @@ void tst_qquickmenubar::keys()
void tst_qquickmenubar::mnemonics()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
#if defined(Q_OS_MACOS) or defined(Q_OS_WEBOS)
QSKIP("Mnemonics are not used on this platform");
diff --git a/tests/auto/quickcontrols/qquickpopup/BLACKLIST b/tests/auto/quickcontrols/qquickpopup/BLACKLIST
index aa31440328..ed8288b5c9 100644
--- a/tests/auto/quickcontrols/qquickpopup/BLACKLIST
+++ b/tests/auto/quickcontrols/qquickpopup/BLACKLIST
@@ -12,3 +12,8 @@ opensuse-leap
[cursorShape]
opensuse-leap
+
+# QTBUG-125237
+[activeFocusItemAfterWindowInactive]
+android
+
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
new file mode 100644
index 0000000000..1d4dc87e41
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias popup: popup
+ property alias button: button
+
+ Button {
+ id: button
+ text: "button"
+ focus: true
+ }
+
+ Popup {
+ id: popup
+ focus: true
+ width: 100
+ height: 100
+ anchors.centerIn: Overlay.overlay
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml b/tests/auto/quickcontrols/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml
new file mode 100644
index 0000000000..a7e66718ee
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: root
+ width: 100
+ height: 100
+ property alias controlsPopup: _controlsPopup
+ property alias blockInputPopup: _blockInputPopup
+ Popup {
+ id: _controlsPopup
+ width: parent.width
+ height: parent.height
+ modal: true
+ Control {
+ id: controls
+ anchors.fill: parent
+ hoverEnabled: true
+ contentItem: Text { text: "Test Control" }
+ }
+ }
+ Popup {
+ id: _blockInputPopup
+ width: parent.width
+ height: parent.height
+ modal: true
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index 09c0b29d2f..ee535caa8e 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -65,6 +65,7 @@ private slots:
void activeFocusAfterExit();
void activeFocusOnDelayedEnter();
void activeFocusDespiteLowerStackingOrder();
+ void activeFocusItemAfterWindowInactive();
void hover_data();
void hover();
void wheel_data();
@@ -102,9 +103,9 @@ private slots:
void focusMultiplePopup();
void contentChildrenChange();
void doubleClickInMouseArea();
+ void resetHoveredStateForItemsWithinPopup();
private:
- static bool hasWindowActivation();
QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
@@ -128,11 +129,6 @@ void tst_QQuickPopup::visible_data()
QTest::newRow("ApplicationWindow") << "applicationwindow.qml";
}
-bool tst_QQuickPopup::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
void tst_QQuickPopup::visible()
{
QFETCH(QString, source);
@@ -511,8 +507,7 @@ void tst_QQuickPopup::closePolicy_data()
void tst_QQuickPopup::closePolicy()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QFETCH(QString, source);
QFETCH(const QPointingDevice *, device);
@@ -654,8 +649,7 @@ void tst_QQuickPopup::closePolicy_grabberInside()
void tst_QQuickPopup::activeFocusOnClose1()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that a popup that never sets focus: true (e.g. ToolTip) doesn't affect
// the active focus item when it closes.
@@ -700,8 +694,7 @@ void tst_QQuickPopup::activeFocusOnClose1()
void tst_QQuickPopup::activeFocusOnClose2()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that a popup that sets focus: true but relinquishes focus (e.g. by
// calling forceActiveFocus() on another item) before it closes doesn't
@@ -742,8 +735,7 @@ void tst_QQuickPopup::activeFocusOnClose2()
void tst_QQuickPopup::activeFocusOnClose3()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that a closing popup that had focus doesn't steal focus from
// another popup that the focus was transferred to.
@@ -778,8 +770,7 @@ void tst_QQuickPopup::activeFocusOnClose3()
void tst_QQuickPopup::activeFocusOnClosingSeveralPopups()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that active focus isn't lost when multiple popup closing simultaneously
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnClosingSeveralPopups.qml"));
@@ -830,8 +821,7 @@ void tst_QQuickPopup::activeFocusOnClosingSeveralPopups()
void tst_QQuickPopup::activeFocusAfterExit()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that after closing a popup the highest one in z-order receives it instead.
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusAfterExit.qml"));
@@ -882,8 +872,7 @@ void tst_QQuickPopup::activeFocusAfterExit()
void tst_QQuickPopup::activeFocusOnDelayedEnter()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Test that after opening two popups, first of which has an animation, does not cause
// the first one to receive focus after the animation stops.
@@ -911,8 +900,7 @@ void tst_QQuickPopup::activeFocusOnDelayedEnter()
// key events due to having active focus.
void tst_QQuickPopup::activeFocusDespiteLowerStackingOrder()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnClose3.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -951,6 +939,51 @@ void tst_QQuickPopup::activeFocusDespiteLowerStackingOrder()
QVERIFY(!popup1->hasActiveFocus());
}
+void tst_QQuickPopup::activeFocusItemAfterWindowInactive()
+{
+ SKIP_IF_NO_WINDOW_ACTIVATION
+
+ QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusAfterWindowInactive.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QQuickPopup *popup = helper.appWindow->property("popup").value<QQuickPopup*>();
+ QQuickButton *button = helper.appWindow->property("button").value<QQuickButton*>();
+ QVERIFY(popup);
+ QVERIFY(button);
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+ QVERIFY(popup->hasActiveFocus());
+ QVERIFY(!button->hasActiveFocus());
+
+ popup->close();
+ QTRY_VERIFY(!popup->isVisible());
+ QVERIFY(button->hasActiveFocus());
+ QCOMPARE(window->activeFocusItem(), button);
+
+ popup->open();
+ QTRY_VERIFY(popup->isVisible());
+
+ QQuickWindow newWindow;
+ newWindow.setTitle("newFocusWindow");
+ newWindow.show();
+ newWindow.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&newWindow));
+
+ popup->close();
+ QTRY_VERIFY(!popup->isVisible());
+ QTRY_VERIFY(!popup->isOpened());
+ QCOMPARE(QGuiApplication::focusWindow(), &newWindow);
+
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+ QCOMPARE(window->activeFocusItem(), button);
+}
+
void tst_QQuickPopup::hover_data()
{
QTest::addColumn<QString>("source");
@@ -1389,8 +1422,7 @@ void tst_QQuickPopup::componentComplete()
void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
// Tests the scenario in the Gallery example, where there are nested popups that should
// close in the correct order when the Escape key is pressed.
@@ -1459,8 +1491,7 @@ void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
void tst_QQuickPopup::closeOnEscapeWithVisiblePopup()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, QStringLiteral("closeOnEscapeWithVisiblePopup.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1587,8 +1618,7 @@ void tst_QQuickPopup::qquickview()
// QTBUG-73447
void tst_QQuickPopup::disabledPalette()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1627,8 +1657,7 @@ void tst_QQuickPopup::disabledPalette()
void tst_QQuickPopup::disabledParentPalette()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1736,8 +1765,7 @@ void tst_QQuickPopup::setOverlayParentToNull()
void tst_QQuickPopup::tabFence()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -1849,8 +1877,7 @@ void tst_QQuickPopup::centerInOverlayWithinStackViewItem()
void tst_QQuickPopup::destroyDuringExitTransition()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
QQuickControlsApplicationHelper helper(this, "destroyDuringExitTransition.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -2287,6 +2314,40 @@ void tst_QQuickPopup::doubleClickInMouseArea()
QCOMPARE(longPressSpy.count(), 0);
}
+void tst_QQuickPopup::resetHoveredStateForItemsWithinPopup()
+{
+ QQuickControlsApplicationHelper helper(this, "resetHoveredForItemsWithinOverlay.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickPopup *controlsPopup = window->property("controlsPopup").value<QQuickPopup*>();
+ QVERIFY(controlsPopup);
+
+ QQuickPopup *blockInputPopup = window->property("blockInputPopup").value<QQuickPopup*>();
+ QVERIFY(controlsPopup);
+
+ controlsPopup->open();
+ QTRY_VERIFY(controlsPopup->isOpened());
+
+ QTest::mouseMove(window, QPoint(window->width() / 2 + 2, window->height() / 2 + 2));
+ QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
+
+ auto *controlItem = qobject_cast<QQuickControl *>(controlsPopup->contentItem()->childItems().at(0));
+ QVERIFY(controlItem);
+ // Check hover enabled for the control item within the popup
+ QTRY_VERIFY(controlItem->isHovered());
+
+ // Open the modal popup window over the existing control item
+ blockInputPopup->open();
+ QTRY_VERIFY(blockInputPopup->isOpened());
+
+ // Control item hovered shall be disabled once we open the modal popup
+ QTRY_VERIFY(!controlItem->isHovered());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp b/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
index 3788799f8a..1e3e0e46ee 100644
--- a/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
+++ b/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
@@ -12,6 +12,7 @@
#include <QtQuick/qquickview.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquicktextarea_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
@@ -29,7 +30,6 @@ private slots:
void touchscreenSetsFocusAndMovesCursor();
private:
- static bool hasWindowActivation();
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
@@ -102,8 +102,7 @@ void tst_QQuickTextArea::touchscreenDoesNotSelect()
void tst_QQuickTextArea::touchscreenSetsFocusAndMovesCursor()
{
- if (!hasWindowActivation())
- QSKIP("Window activation is not supported");
+ SKIP_IF_NO_WINDOW_ACTIVATION
qunsetenv("QT_QUICK_CONTROLS_TEXT_SELECTION_BEHAVIOR");
QQuickView window;
@@ -158,11 +157,6 @@ void tst_QQuickTextArea::touchscreenSetsFocusAndMovesCursor()
QCOMPARE_GT(top->selectedText().size(), 0);
}
-bool tst_QQuickTextArea::hasWindowActivation()
-{
- return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
-}
-
QTEST_QUICKCONTROLS_MAIN(tst_QQuickTextArea)
#include "tst_qquicktextarea.moc"
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index 27334f06bb..a42ea91b6d 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -304,6 +304,7 @@ bool FileDialogTestHelper::openDialog()
void tst_QQuickFileDialogImpl::defaults()
{
+ QTest::failOnWarning(QRegularExpression(".*"));
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
index 6fd64acc1b..b2edbb1cd8 100644
--- a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
@@ -277,6 +277,10 @@ void tst_QQuickMessageDialogImpl::emitCorrectAcceptedAndRejectedSignals()
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
QVERIFY(dialogHelper.openDialog());
+
+ if (QQuickTest::qIsPolishScheduled(dialogHelper.window()))
+ QVERIFY(QQuickTest::qWaitForPolish(dialogHelper.window()));
+
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
auto *buttonBox = dialogHelper.quickDialog->findChild<QQuickDialogButtonBox *>("buttonBox");
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index 175bec4cb8..73411e5b8a 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -14,6 +14,7 @@
#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtGui/QWindow>
#include <QtGui/QScreen>
#include <QtGui/QImage>
@@ -142,6 +143,7 @@ private slots:
void focusPreserved();
void accessibilityHandlesViewChange();
void cleanupRhi();
+ void dontRecreateRootElementOnWindowChange();
private:
QPointingDevice *device = QTest::createTouchDevice();
@@ -991,8 +993,7 @@ void tst_qquickwidget::focusOnClickInProxyWidget()
void tst_qquickwidget::focusPreserved()
{
- if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
- QSKIP("Window Activation is not supported.");
+ SKIP_IF_NO_WINDOW_ACTIVATION
if (QGuiApplication::platformName() == "android")
QSKIP("Test doesn't exit cleanly on Android and generates many warnings - QTBUG-112696");
@@ -1096,6 +1097,21 @@ void tst_qquickwidget::cleanupRhi()
topLevel.create();
}
+void tst_qquickwidget::dontRecreateRootElementOnWindowChange()
+{
+ auto *quickWidget = new QQuickWidget();
+ quickWidget->setSource(testFileUrl("rectangle.qml"));
+ QObject *item = quickWidget->rootObject();
+
+ bool wasDestroyed = false;
+ QObject::connect(item, &QObject::destroyed, this, [&] { wasDestroyed = true; });
+
+ QEvent event(QEvent::WindowChangeInternal);
+ QCoreApplication::sendEvent(quickWidget, &event);
+
+ QVERIFY(!wasDestroyed);
+}
+
QTEST_MAIN(tst_qquickwidget)
#include "tst_qquickwidget.moc"
diff --git a/tests/manual/quickcontrols/screenshots/CMakeLists.txt b/tests/manual/quickcontrols/screenshots/CMakeLists.txt
index 27a8ac882c..32d465b9d4 100644
--- a/tests/manual/quickcontrols/screenshots/CMakeLists.txt
+++ b/tests/manual/quickcontrols/screenshots/CMakeLists.txt
@@ -18,7 +18,7 @@ qt_internal_add_manual_test(screenshots
SOURCES
screenshots.cpp
DEFINES
- SNIPPETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../../../src/imports/controls/doc/snippets"
+ SNIPPETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols/doc/snippets"
LIBRARIES
Qt::Gui
Qt::Quick
diff --git a/tests/manual/quickcontrols/screenshots/screenshots.qml b/tests/manual/quickcontrols/screenshots/screenshots.qml
index cd8d52d658..e3bcfa860d 100644
--- a/tests/manual/quickcontrols/screenshots/screenshots.qml
+++ b/tests/manual/quickcontrols/screenshots/screenshots.qml
@@ -47,7 +47,7 @@ ApplicationWindow {
showDirs: false
}
delegate: ItemDelegate {
- width: parent.width
+ width: snippetsListView.width
text: fileName
focusPolicy: Qt.NoFocus
diff --git a/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml b/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
index d64339501f..e8e1194a01 100644
--- a/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
@@ -32,7 +32,7 @@ QtObject {
clip: true
model: 20
delegate: CheckDelegate {
- width: parent.width
+ width: ListView.view.width
text: "CheckDelegate"
focusPolicy: Qt.StrongFocus
}
diff --git a/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml b/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
index e8171c36bc..d6ed7c54d2 100644
--- a/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
@@ -29,7 +29,7 @@ QtObject {
clip: true
model: 20
delegate: ItemDelegate {
- width: parent.width
+ width: ListView.view.width
text: "ItemDelegate"
focusPolicy: Qt.StrongFocus
}
diff --git a/tests/manual/quickcontrols/testbench/controls/Menu.qml b/tests/manual/quickcontrols/testbench/controls/Menu.qml
index aeadee4f86..a420163042 100644
--- a/tests/manual/quickcontrols/testbench/controls/Menu.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Menu.qml
@@ -42,15 +42,18 @@ QtObject {
MenuItem {
text: "Checked"
+ checkable: true
checked: true
}
MenuItem {
text: "Checked + Pressed"
+ checkable: true
checked: true
down: true
}
MenuItem {
text: "Checked + Disabled"
+ checkable: true
checked: true
enabled: false
}
diff --git a/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml b/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
index 4dab387fcf..364770c187 100644
--- a/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
@@ -31,7 +31,7 @@ QtObject {
clip: true
model: 20
delegate: RadioDelegate {
- width: parent.width
+ width: ListView.view.width
text: "RadioDelegate"
focusPolicy: Qt.StrongFocus
}
diff --git a/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml b/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
index e04f0bb630..1c4c22c608 100644
--- a/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
@@ -48,7 +48,7 @@ QtObject {
clip: true
model: 20
delegate: SwipeDelegate {
- width: parent.width
+ width: ListView.view.width
text: "SwipeDelegate"
focusPolicy: Qt.StrongFocus
diff --git a/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml b/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
index 9508b2ce80..2f6b26c47a 100644
--- a/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
@@ -31,7 +31,7 @@ QtObject {
clip: true
model: 20
delegate: SwitchDelegate {
- width: parent.width
+ width: ListView.view.width
text: "SwitchDelegate"
focusPolicy: Qt.StrongFocus
}
diff --git a/tests/manual/quickcontrols/testbench/testbench.qml b/tests/manual/quickcontrols/testbench/testbench.qml
index 9df1ed873f..1a95de71be 100644
--- a/tests/manual/quickcontrols/testbench/testbench.qml
+++ b/tests/manual/quickcontrols/testbench/testbench.qml
@@ -139,7 +139,11 @@ Ui.ApplicationWindow {
text: "\ue801"
font.family: "fontello"
visible: searchTextField.length > 0
- onClicked: searchTextField.clear()
+ onClicked: {
+ searchTextField.clear()
+ // textEdited is not emitted for clear(), so we have to set this ourselves.
+ settings.lastSearchText = ""
+ }
Layout.leftMargin: -5
}