aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2024-10-04 14:54:44 +0300
committerTarja Sundqvist <tarja.sundqvist@qt.io>2024-10-04 14:54:44 +0300
commit9886ff37350f661aad5b8bfaf2776e8429f208a7 (patch)
tree6bc704d0ad3973b8a5f2a53b3beef67cb848f9a8
parentb078003a3c81f1438dcaf932371d564003c99395 (diff)
parent33e04685b27fc887dea2f71dbdf23850febc54dd (diff)
Merge tag 'v6.2.10-lts' into tqtc/lts-6.2-opensourcev6.2.10-lts-lgpl
Qt 6.2.10-lts release Conflicts solved: dependencies.yaml Change-Id: Ibdf006fa08cddc80ad30fb9ce1089305729d4ece
-rw-r--r--.cmake.conf2
-rw-r--r--.qmake.conf2
-rw-r--r--dependencies.yaml6
-rw-r--r--examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp2
-rw-r--r--examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp2
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.pngbin2532 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.pngbin4629 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.pngbin1271 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.pngbin3230 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.pngbin1037 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.pngbin2817 -> 0 bytes
-rw-r--r--examples/quickcontrols2/imagine/automotive/qml/automotive.qml42
-rw-r--r--src/imports/builtins/jsroot.qmltypes1
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodel.cpp57
-rw-r--r--src/labs/models/doc/src/qmllabsmodels.qdoc4
-rw-r--r--src/qml/Qt6QmlMacros.cmake44
-rw-r--r--src/qml/common/qqmljsmemorypool_p.h10
-rw-r--r--src/qml/doc/src/cppintegration/topic.qdoc3
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc6
-rw-r--r--src/qml/doc/src/qtqml-writing-a-module.qdoc2
-rw-r--r--src/qml/jsapi/qjsengine.cpp5
-rw-r--r--src/qml/jsapi/qjsvalue.cpp3
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp38
-rw-r--r--src/qml/jsruntime/qv4engine.cpp49
-rw-r--r--src/qml/jsruntime/qv4engine_p.h4
-rw-r--r--src/qml/jsruntime/qv4identifierhash.cpp4
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp13
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h3
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp18
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp129
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h34
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp7
-rw-r--r--src/qml/jsruntime/qv4string_p.h22
-rw-r--r--src/qml/qml/ftw/qrecyclepool_p.h9
-rw-r--r--src/qml/qml/qqmldata_p.h64
-rw-r--r--src/qml/qml/qqmlengine.cpp85
-rw-r--r--src/qml/qml/qqmlengine_p.h13
-rw-r--r--src/qml/qml/qqmlimport.cpp9
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp46
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp2
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp19
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp29
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h1
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h1
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp2
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp24
-rw-r--r--src/qmldom/qqmldomreformatter.cpp40
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp3
-rw-r--r--src/quick/doc/images/qml-item-canvas-startAngle.pngbin5099 -> 5826 bytes
-rw-r--r--src/quick/doc/snippets/qml/images/qt-logo.pngbin0 -> 6208 bytes
-rw-r--r--src/quick/doc/snippets/qml/item/itemGrab.qml (renamed from src/quick/doc/snippets/qml/itemGrab.qml)43
-rw-r--r--src/quick/doc/snippets/qml/nestedWindowTransientParent.qml70
-rw-r--r--src/quick/doc/snippets/qml/splashWindow.qml104
-rw-r--r--src/quick/doc/snippets/qml/windowActiveAttached.qml57
-rw-r--r--src/quick/doc/snippets/qml/windowPalette.qml85
-rw-r--r--src/quick/doc/snippets/qml/windowVisibility.qml76
-rw-r--r--src/quick/doc/src/concepts/positioning/righttoleft.qdoc10
-rw-r--r--src/quick/doc/src/examples.qdoc1
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp4
-rw-r--r--src/quick/items/qquickflickable.cpp20
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickimage.cpp1
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp12
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp2
-rw-r--r--src/quick/items/qquicklistview.cpp4
-rw-r--r--src/quick/items/qquickpalette.cpp27
-rw-r--r--src/quick/items/qquickscreen.cpp2
-rw-r--r--src/quick/items/qquicktextedit.cpp12
-rw-r--r--src/quick/items/qquicktextinput.cpp13
-rw-r--r--src/quick/items/qquickwindow.cpp42
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp12
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp2
-rw-r--r--src/quick/util/qquickanimation.cpp41
-rw-r--r--src/quick/util/qquickanimation_p_p.h2
-rw-r--r--src/quick/util/qquickapplication.cpp1
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp7
-rw-r--r--src/quick/util/qquicksystempalette.cpp4
-rw-r--r--src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml67
-rw-r--r--src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc76
-rw-r--r--src/quickdialogs2/quickdialogs2/CMakeLists.txt1
-rw-r--r--src/quicktemplates2/qquickdeferredexecute.cpp18
-rw-r--r--src/quicktemplates2/qquickheaderview.cpp94
-rw-r--r--src/quicktemplates2/qquickmenu.cpp6
-rw-r--r--src/quicktemplates2/qquickpopup.cpp1
-rw-r--r--src/quicktemplates2/qquickswipeview.cpp41
-rw-r--r--src/quicktemplates2/qquickswipeview_p.h1
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp87
-rw-r--r--src/quicktestutils/quick/viewtestutils_p.h11
-rw-r--r--src/quickwidgets/qquickwidget.cpp6
-rw-r--r--tests/auto/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/CMakeLists.txt1
-rw-r--r--tests/auto/cmake/test_internal_singleton/CMakeLists.txt23
-rw-r--r--tests/auto/cmake/test_internal_singleton/Test.qml4
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp219
-rw-r--r--tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/forWithLet.formatted.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/forWithLet.qml8
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp5
-rw-r--r--tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes513
-rw-r--r--tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml5
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp10
-rw-r--r--tests/auto/qml/qqmlecmascript/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp73
-rw-r--r--tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml6
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp13
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h44
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp23
-rw-r--r--tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml50
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp29
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST4
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml49
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp72
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml36
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp58
-rw-r--r--tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml29
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp21
-rw-r--r--tests/auto/quick/qquickgridview/data/qtbug92998.qml53
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp20
-rw-r--r--tests/auto/quick/qquicklistview2/data/fetchMore.qml21
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp109
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml4
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp74
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp49
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp2
-rw-r--r--tests/auto/quickcontrols2/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_swipeview.qml28
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_tumbler.qml42
-rw-r--r--tests/auto/quickcontrols2/deferred/CMakeLists.txt25
-rw-r--r--tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml15
-rw-r--r--tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp100
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp13
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp137
141 files changed, 3351 insertions, 660 deletions
diff --git a/.cmake.conf b/.cmake.conf
index beccb22d04..31a36e3b07 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,2 @@
-set(QT_REPO_MODULE_VERSION "6.2.9")
+set(QT_REPO_MODULE_VERSION "6.2.10")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "")
diff --git a/.qmake.conf b/.qmake.conf
index 1763149193..4c0875fa17 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -5,4 +5,4 @@ DEFINES += QT_NO_JAVA_STYLE_ITERATORS
QQC2_SOURCE_TREE = $$PWD
-MODULE_VERSION = 6.2.9
+MODULE_VERSION = 6.2.10
diff --git a/dependencies.yaml b/dependencies.yaml
index 01e8b423de..1793c7e6b8 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -3,11 +3,11 @@ dependencies:
ref: 017d80e12fa50c50fa6751a039d3a7c9e799f34c
required: true
../tqtc-qtimageformats:
- ref: ab15bcca79372d79271bfda97d93140fd6df9889
+ ref: 89e4bf6b362f2bf6b12aa453c19a98a03b190f2c
required: false
../tqtc-qtshadertools:
- ref: 35662703a974d3929d1ea039c2534271cf60700f
+ ref: e6be6ff4be1820e5377d78efa388321de216b66f
required: false
../tqtc-qtsvg:
- ref: b50df0589ace2172bb6cbcf099f9f6cc2578759b
+ ref: 2dc622e881b357c0899311cc8cc358d8487f5ad4
required: false
diff --git a/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp b/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp
index 6203ebec1e..017e7f4a97 100644
--- a/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp
+++ b/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp
@@ -84,7 +84,7 @@ SqlContactModel::SqlContactModel(QObject *parent) :
if (!query.exec("SELECT * FROM Contacts"))
qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text()));
- setQuery(query);
+ setQuery(std::move(query));
if (lastError().isValid())
qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text()));
}
diff --git a/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp b/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp
index 6203ebec1e..017e7f4a97 100644
--- a/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp
+++ b/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp
@@ -84,7 +84,7 @@ SqlContactModel::SqlContactModel(QObject *parent) :
if (!query.exec("SELECT * FROM Contacts"))
qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text()));
- setQuery(query);
+ setQuery(std::move(query));
if (lastError().isValid())
qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text()));
}
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png
index 26add20cfc..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
index 01d8136d51..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png
index 435acd1466..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
index 9bab57904d..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png
index 8aab4d3280..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png
index a856971185..e69de29bb2 100644
--- a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png
+++ b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png
Binary files differ
diff --git a/examples/quickcontrols2/imagine/automotive/qml/automotive.qml b/examples/quickcontrols2/imagine/automotive/qml/automotive.qml
index 4734175870..7a8ce2224f 100644
--- a/examples/quickcontrols2/imagine/automotive/qml/automotive.qml
+++ b/examples/quickcontrols2/imagine/automotive/qml/automotive.qml
@@ -160,7 +160,7 @@ ApplicationWindow {
Item {}
ColumnLayout {
- spacing: 16
+ spacing: 12
ButtonGroup {
id: viewButtonGroup
@@ -176,6 +176,7 @@ ApplicationWindow {
Button {
text: qsTr("Compact")
font.pixelSize: fontSizeExtraSmall
+ checkable: true
checked: true
Layout.fillWidth: true
@@ -203,13 +204,8 @@ ApplicationWindow {
stepSize: 1
Layout.alignment: Qt.AlignHCenter
- Layout.minimumWidth: 64
- Layout.minimumHeight: 64
Layout.preferredWidth: 128
Layout.preferredHeight: 128
- Layout.maximumWidth: 128
- Layout.maximumHeight: 128
- Layout.fillHeight: true
Label {
text: volumeDial.value.toFixed(0)
@@ -224,7 +220,7 @@ ApplicationWindow {
}
RowLayout {
- Layout.topMargin: 16
+ Layout.topMargin: 8
LargeLabel {
id: radioOption
@@ -282,18 +278,18 @@ ApplicationWindow {
}
model: ListModel {
- ListElement { name: "V-Radio"; frequency: "105.5 MHz" }
- ListElement { name: "World News"; frequency: "93.4 MHz" }
- ListElement { name: "TekStep FM"; frequency: "95.0 MHz" }
- ListElement { name: "Classic Radio"; frequency: "89.9 MHz" }
- ListElement { name: "Buena Vista FM"; frequency: "100.8 MHz" }
- ListElement { name: "Drive-by Radio"; frequency: "99.1 MHz" }
- ListElement { name: "Unknown #1"; frequency: "104.5 MHz" }
- ListElement { name: "Unknown #2"; frequency: "91.2 MHz" }
- ListElement { name: "Unknown #3"; frequency: "93.8 MHz" }
- ListElement { name: "Unknown #4"; frequency: "80.4 MHz" }
- ListElement { name: "Unknown #5"; frequency: "101.1 MHz" }
- ListElement { name: "Unknown #6"; frequency: "92.2 MHz" }
+ ListElement { name: "V-Radio"; frequency: "105.5" }
+ ListElement { name: "World News"; frequency: "93.4" }
+ ListElement { name: "TekStep FM"; frequency: "95.0" }
+ ListElement { name: "Classic Radio"; frequency: "89.9" }
+ ListElement { name: "Buena Vista FM"; frequency: "100.8" }
+ ListElement { name: "Drive-by Radio"; frequency: "99.1" }
+ ListElement { name: "Unknown #1"; frequency: "104.5" }
+ ListElement { name: "Unknown #2"; frequency: "91.2" }
+ ListElement { name: "Unknown #3"; frequency: "93.8" }
+ ListElement { name: "Unknown #4"; frequency: "80.4" }
+ ListElement { name: "Unknown #5"; frequency: "101.1" }
+ ListElement { name: "Unknown #6"; frequency: "92.2" }
}
delegate: ItemDelegate {
id: stationDelegate
@@ -309,12 +305,16 @@ ApplicationWindow {
text: model.name
font: stationDelegate.font
horizontalAlignment: Text.AlignLeft
+ elide: Text.ElideRight
+
Layout.fillWidth: true
}
Label {
text: model.frequency
font: stationDelegate.font
horizontalAlignment: Text.AlignRight
+ elide: Text.ElideRight
+
Layout.fillWidth: true
}
}
@@ -325,14 +325,12 @@ ApplicationWindow {
Frame {
Layout.fillWidth: true
- RowLayout {
+ ColumnLayout {
anchors.fill: parent
Label {
text: qsTr("Sort by")
font.pixelSize: fontSizeExtraSmall
-
- Layout.alignment: Qt.AlignTop
}
ColumnLayout {
diff --git a/src/imports/builtins/jsroot.qmltypes b/src/imports/builtins/jsroot.qmltypes
index 0a8b70babe..39878d8cf3 100644
--- a/src/imports/builtins/jsroot.qmltypes
+++ b/src/imports/builtins/jsroot.qmltypes
@@ -1356,6 +1356,7 @@ Module {
Method { name: "formatTime" }
Method { name: "formatDateTime" }
Method { name: "locale" }
+ Method { name: "url" }
Method { name: "resolvedUrl" }
Method { name: "openUrlExternally" }
Method { name: "font" }
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
index b86ddfe397..b05613b410 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
@@ -252,16 +252,16 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Components access names and paths via the following roles:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
Additionally a file entry can be differentiated from a folder entry via the
@@ -304,6 +304,7 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Component {
id: fileDelegate
+ required property string fileName
Text { text: fileName }
}
@@ -395,6 +396,7 @@ QHash<int, QByteArray> QQuickFolderListModel::roleNames() const
/*!
\qmlproperty int FolderListModel::count
+ \readonly
Returns the number of items in the current folder that match the
filter criteria.
@@ -419,7 +421,8 @@ QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) con
The value must be a \c file: or \c qrc: URL, or a relative URL.
- The default value is an invalid URL.
+ The default value is the application's working directory at the time
+ when the FolderListModel is first initialized.
*/
QUrl QQuickFolderListModel::folder() const
{
@@ -493,6 +496,7 @@ void QQuickFolderListModel::setRootFolder(const QUrl &path)
/*!
\qmlproperty url FolderListModel::parentFolder
+ \readonly
Returns the URL of the parent of the current \l folder.
*/
@@ -566,11 +570,11 @@ void QQuickFolderListModel::componentComplete()
The \a sortField property contains the field to use for sorting.
\c sortField may be one of:
- \value Unsorted no sorting is applied
- \value Name sort by filename
- \value Time sort by time modified
- \value Size sort by file size
- \value Type sort by file type (extension)
+ \value FolderListModel.Unsorted no sorting is applied
+ \value FolderListModel.Name sort by filename (default)
+ \value FolderListModel.Time sort by time modified
+ \value FolderListModel.Size sort by file size
+ \value FolderListModel.Type sort by file type/extension
\sa sortReversed
*/
@@ -818,6 +822,7 @@ void QQuickFolderListModel::setCaseSensitive(bool on)
/*!
\qmlproperty enumeration FolderListModel::status
\since 5.11
+ \readonly
This property holds the status of folder reading. It can be one of:
@@ -884,16 +889,16 @@ void QQuickFolderListModel::setSortCaseSensitive(bool on)
are available:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
*/
QVariant QQuickFolderListModel::get(int idx, const QString &property) const
diff --git a/src/labs/models/doc/src/qmllabsmodels.qdoc b/src/labs/models/doc/src/qmllabsmodels.qdoc
index 598786f1f6..f2b57323dc 100644
--- a/src/labs/models/doc/src/qmllabsmodels.qdoc
+++ b/src/labs/models/doc/src/qmllabsmodels.qdoc
@@ -36,7 +36,7 @@
To use the types in this module, import the module with the following line:
- \code
+ \qml
import Qt.labs.qmlmodels
- \endcode
+ \endqml
*/
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index bda7b9b83a..d805d37feb 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -1053,7 +1053,7 @@ function(qt6_add_qml_plugin target)
if(NOT arg_CLASS_NAME)
if(NOT "${arg_BACKING_TARGET}" STREQUAL "")
- get_target_property(arg_CLASS_NAME ${target} QT_QML_MODULE_CLASS_NAME)
+ get_target_property(arg_CLASS_NAME ${arg_BACKING_TARGET} QT_QML_MODULE_CLASS_NAME)
endif()
if(NOT arg_CLASS_NAME)
_qt_internal_compute_qml_plugin_class_name_from_uri("${arg_URI}" arg_CLASS_NAME)
@@ -1429,9 +1429,11 @@ function(qt6_target_qml_sources target)
get_filename_component(file_out_dir ${file_out} DIRECTORY)
file(MAKE_DIRECTORY ${file_out_dir})
- execute_process(COMMAND
- ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
- )
+ if(EXISTS "${file_absolute}")
+ execute_process(COMMAND
+ ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
+ )
+ endif()
add_custom_command(OUTPUT ${file_out}
COMMAND ${CMAKE_COMMAND} -E copy ${file_src} ${file_out}
@@ -1518,6 +1520,12 @@ function(qt6_target_qml_sources target)
get_source_file_property(qml_file_singleton ${qml_file_src} QT_QML_SINGLETON_TYPE)
get_source_file_property(qml_file_internal ${qml_file_src} QT_QML_INTERNAL_TYPE)
+ if (qml_file_singleton AND qml_file_internal)
+ message(FATAL_ERROR
+ "${qml_file_src} is marked as both internal and as a "
+ "singleton, but singletons cannot be internal!")
+ endif()
+
if (NOT qml_file_versions)
set(qml_file_versions ${qml_module_files_versions})
endif()
@@ -1822,28 +1830,6 @@ function(_qt_internal_qml_type_registration target)
message(FATAL_ERROR "Need target metatypes.json file")
endif()
- cmake_policy(PUSH)
-
- set(registration_cpp_file_dep_args)
- if (CMAKE_GENERATOR MATCHES "Ninja" OR
- (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles"))
- if(POLICY CMP0116)
- # Without explicitly setting this policy to NEW, we get a warning
- # even though we ensure there's actually no problem here.
- # See https://gitlab.kitware.com/cmake/cmake/-/issues/21959
- cmake_policy(SET CMP0116 NEW)
- set(relative_to_dir ${CMAKE_CURRENT_BINARY_DIR})
- else()
- set(relative_to_dir ${CMAKE_BINARY_DIR})
- endif()
- set(dependency_file_cpp "${target_binary_dir}/qmltypes/${type_registration_cpp_file_name}.d")
- set(registration_cpp_file_dep_args DEPFILE ${dependency_file_cpp})
- file(RELATIVE_PATH cpp_file_name "${relative_to_dir}" "${type_registration_cpp_file}")
- file(GENERATE OUTPUT "${dependency_file_cpp}"
- CONTENT "${cpp_file_name}: $<IF:$<BOOL:${genex_list}>,\\\n$<JOIN:${genex_list}, \\\n>, \\\n>"
- )
- endif()
-
add_custom_command(
OUTPUT
${type_registration_cpp_file}
@@ -1863,12 +1849,9 @@ function(_qt_internal_qml_type_registration target)
${CMAKE_COMMAND} -E make_directory "${generated_marker_dir}"
COMMAND
${CMAKE_COMMAND} -E touch "${generated_marker_file}"
- ${registration_cpp_file_dep_args}
COMMENT "Automatic QML type registration for target ${target}"
)
- cmake_policy(POP)
-
# The ${target}_qmllint targets need to depend on the generation of all
# *.qmltypes files in the build. We have no way of reliably working out
# which QML modules a given target depends on at configure time, so we
@@ -2166,7 +2149,8 @@ but this file does not exist. Possible reasons include:
# across those libraries to the end target (executable or shared library).
# The plugin initializers will be linked via usage requirements from the plugin target.
get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY")
+ if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY"
+ OR target_type STREQUAL "MODULE_LIBRARY")
set(link_type "PRIVATE")
else()
set(link_type "INTERFACE")
diff --git a/src/qml/common/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h
index dcf3fafb67..2a1dc43110 100644
--- a/src/qml/common/qqmljsmemorypool_p.h
+++ b/src/qml/common/qqmljsmemorypool_p.h
@@ -81,13 +81,12 @@ public:
free(_blocks);
}
- qDeleteAll(strings);
}
inline void *allocate(size_t size)
{
size = (size + 7) & ~size_t(7);
- if (Q_LIKELY(_ptr && (_ptr + size < _end))) {
+ if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) {
void *addr = _ptr;
_ptr += size;
return addr;
@@ -105,9 +104,8 @@ public:
template <typename Tp, typename... Ta> Tp *New(Ta... args)
{ return new (this->allocate(sizeof(Tp))) Tp(args...); }
- QStringView newString(const QString &string) {
- strings.append(new QString(string));
- return QStringView(*strings.last());
+ QStringView newString(QString string) {
+ return strings.emplace_back(std::move(string));
}
private:
@@ -151,7 +149,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
- QVector<QString*> strings;
+ QStringList strings;
enum
{
diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc
index 14a27c9a3f..1a77039e7c 100644
--- a/src/qml/doc/src/cppintegration/topic.qdoc
+++ b/src/qml/doc/src/cppintegration/topic.qdoc
@@ -205,7 +205,8 @@ dynamically load and introspect objects through the Qt meta object system.
\include warning.qdocinc
For more information on accessing QML objects from C++, see the documentation on
-\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}.
+\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++},
+and the \l {Exposing Data from C++ to QML} section of the Best Practices page.
\section1 Data Type Conversion Between QML and C++
diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
index 493b031b60..6c235ef388 100644
--- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
@@ -177,7 +177,6 @@ An identified module has several restrictions upon it:
\li the module must register its types into the module identifier type
namespace
\li the module may not register types into any other module's namespace
-\li clients must specify a version when importing the module
\endlist
For example, if an identified module is installed into
@@ -194,10 +193,9 @@ module com.example.CustomUi
\endcode
Clients will then be able to import the above module with the following import
-statement (assuming that the module registers types into version 1.0 of its
-namespace):
+statement:
\qml
-import com.example.CustomUi 1.0
+import com.example.CustomUi
\endqml
*/
diff --git a/src/qml/doc/src/qtqml-writing-a-module.qdoc b/src/qml/doc/src/qtqml-writing-a-module.qdoc
index 6ad820552e..a3464926e1 100644
--- a/src/qml/doc/src/qtqml-writing-a-module.qdoc
+++ b/src/qml/doc/src/qtqml-writing-a-module.qdoc
@@ -116,7 +116,7 @@ You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbo
Add the following code to the main.cpp:
\badcode
-#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/QQmlExtensionPlugin>
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
\endcode
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 2c6c579e4f..cdcb5b3bff 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -509,6 +509,9 @@ static QUrl urlForFileName(const QString &fileName)
The script code will be evaluated in the context of the global object.
+ \note If you need to evaluate inside a QML context, use \l QQmlExpression
+ instead.
+
The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
engine; in this case the return value will be the exception
that was thrown (typically an \c{Error} object; see
@@ -539,6 +542,8 @@ static QUrl urlForFileName(const QString &fileName)
exception value will still be returned. Use \c exceptionStackTrace->isEmpty()
to distinguish whether the value was a normal or an exceptional return
value.
+
+ \sa QQmlExpression::evaluate
*/
QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace)
{
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 832eaca8da..f9df4a40c5 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -200,10 +200,11 @@
/*!
\enum QJSValue::ObjectConversionBehavior
- This enum is used to specify how JavaScript objects without an equivalent
+ This enum is used to specify how JavaScript objects and symbols without an equivalent
native Qt type should be treated when converting to QVariant.
\value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted.
+ Symbols are converted to QString.
\value RetainJSObjects The value is retained as QJSValue wrapped in QVariant.
*/
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 20501f9d03..224c898536 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -670,8 +670,8 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
return p1s->toQString() < p2s->toQString();
}
-template <typename RandomAccessIterator, typename T, typename LessThan>
-void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan)
+template <typename RandomAccessIterator, typename LessThan>
+void sortHelper(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
{
top:
int span = int(end - start);
@@ -716,7 +716,7 @@ top:
++low;
qSwap(*end, *low);
- sortHelper(start, low, t, lessThan);
+ sortHelper(start, low, lessThan);
start = low + 1;
++end;
@@ -816,8 +816,36 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn));
- Value *begin = thisObject->arrayData()->values.values;
- sortHelper(begin, begin + len, *begin, lessThan);
+ const auto thisArrayData = thisObject->arrayData();
+ uint startIndex = thisArrayData->mappedIndex(0);
+ uint endIndex = thisArrayData->mappedIndex(len - 1) + 1;
+ if (startIndex < endIndex) {
+ // Values are contiguous. Sort right away.
+ sortHelper(
+ thisArrayData->values.values + startIndex,
+ thisArrayData->values.values + endIndex,
+ lessThan);
+ } else {
+ // Values wrap around the end of the allocation. Close the gap to form a contiguous array.
+ // We're going to sort anyway. So we don't need to care about order.
+
+ // ArrayElementLessThan sorts empty and undefined to the end of the array anyway, but we
+ // probably shouldn't rely on the unused slots to be actually undefined or empty.
+
+ const uint gap = startIndex - endIndex;
+ const uint allocEnd = thisArrayData->values.alloc - 1;
+ for (uint i = 0; i < gap; ++i) {
+ const uint from = allocEnd - i;
+ const uint to = endIndex + i;
+ if (from < startIndex)
+ break;
+
+ std::swap(thisArrayData->values.values[from], thisArrayData->values.values[to]);
+ }
+
+ thisArrayData->offset = 0;
+ sortHelper(thisArrayData->values.values, thisArrayData->values.values + len, lessThan);
+ }
#ifdef CHECK_SPARSE_ARRAYS
thisObject->initSparseArray();
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c047298aba..0927687578 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1506,7 +1506,9 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
// Variant conversion code
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
+static QVariant toVariant(
+ QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint,
+ bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects);
static QObject *qtObjectFromJS(const QV4::Value &value);
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
@@ -1518,7 +1520,9 @@ static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &
return v4->metaTypeToJS(value.metaType(), value.constData());
}
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects, V4ObjectSet *visitedObjects)
+static QVariant toVariant(
+ QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType,
+ bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects)
{
Q_ASSERT (!value.isEmpty());
QV4::Scope scope(e);
@@ -1657,6 +1661,9 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return *ld->d()->locale;
#endif
if (const QV4::DateObject *d = value.as<DateObject>()) {
+ // NOTE: since we convert QTime to JS Date,
+ // round trip will change the variant type (to QDateTime)!
+
auto dt = d->toQDateTime();
// See ExecutionEngine::metaTypeFromJS()'s handling of QMetaType::Date:
if (typeHint == QMetaType::QDate) {
@@ -1670,7 +1677,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return d->toQUrl();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
- // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
+ if (const Symbol *symbol = value.as<Symbol>()) {
+ return createJSValueForObjectsAndSymbols
+ ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
+ : symbol->descriptiveString();
+ }
QV4::ScopedObject o(scope, value);
Q_ASSERT(o);
@@ -1680,16 +1691,17 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return re->toQRegularExpression();
#endif
- if (createJSValueForObjects)
+ if (createJSValueForObjectsAndSymbols)
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
return objectToVariant(e, o, visitedObjects);
}
-QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects)
+QVariant ExecutionEngine::toVariant(
+ const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
{
- return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr);
+ return ::toVariant(this, value, typeHint, createJSValueForObjectsAndSymbols, nullptr);
}
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects)
@@ -1720,7 +1732,8 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->get(ii);
- list << ::toVariant(e, v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects);
+ list << ::toVariant(
+ e, v, QMetaType {}, /*createJSValueForObjectsAndSymbols*/false, visitedObjects);
}
result = list;
@@ -1736,7 +1749,9 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
break;
QString key = name->toQStringNoThrow();
- map.insert(key, ::toVariant(e, val, /*type hint*/ QMetaType {}, /*createJSValueForObjects*/false, visitedObjects));
+ map.insert(key, ::toVariant(
+ e, val, /*type hint*/ QMetaType {},
+ /*createJSValueForObjectsAndSymbols*/false, visitedObjects));
}
result = map;
@@ -2428,7 +2443,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
*reinterpret_cast<QVariantList *>(data) = a->engine()->toVariant(
- *a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList();
+ *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false).toList();
return true;
}
break;
@@ -2442,18 +2457,20 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
break;
}
case QMetaType::QVariant:
- if (const QV4::Managed *m = value.as<QV4::Managed>())
- *reinterpret_cast<QVariant*>(data) = m->engine()->toVariant(value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false);
- else if (value.isNull())
+ if (const QV4::Managed *m = value.as<QV4::Managed>()) {
+ *reinterpret_cast<QVariant*>(data) = m->engine()->toVariant(
+ value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
+ } else if (value.isNull()) {
*reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
- else if (value.isUndefined())
+ } else if (value.isUndefined()) {
*reinterpret_cast<QVariant*>(data) = QVariant();
- else if (value.isBoolean())
+ } else if (value.isBoolean()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.booleanValue());
- else if (value.isInteger())
+ } else if (value.isInteger()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.integerValue());
- else if (value.isDouble())
+ } else if (value.isDouble()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.doubleValue());
+ }
return true;
case QMetaType::QJsonValue:
*reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 0d3b279377..d5e32ebedc 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -682,7 +682,9 @@ public:
QQmlError catchExceptionAsQmlError();
// variant conversions
- QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects = true);
+ QVariant toVariant(
+ const QV4::Value &value, QMetaType typeHint,
+ bool createJSValueForObjectsAndSymbols = true);
QV4::ReturnedValue fromVariant(const QVariant &);
QVariantMap variantMapFromJS(const QV4::Object *o);
diff --git a/src/qml/jsruntime/qv4identifierhash.cpp b/src/qml/jsruntime/qv4identifierhash.cpp
index d349b7c4fe..e974c2c0ad 100644
--- a/src/qml/jsruntime/qv4identifierhash.cpp
+++ b/src/qml/jsruntime/qv4identifierhash.cpp
@@ -127,7 +127,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
if (!d)
return nullptr;
- PropertyKey id = d->identifierTable->asPropertyKey(str);
+ PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
return lookup(id);
}
@@ -146,7 +146,7 @@ inline
const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->asPropertyKey(str);
+ return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
}
inline
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 7c5bc3188c..f6d10a575f 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -285,12 +285,17 @@ void IdentifierTable::sweep()
size -= freed;
}
-PropertyKey IdentifierTable::asPropertyKey(const QString &s)
+PropertyKey IdentifierTable::asPropertyKey(const QString &s,
+ IdentifierTable::KeyConversionBehavior conversionBehvior)
{
uint subtype;
- const uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
- if (subtype == Heap::String::StringType_ArrayIndex)
- return PropertyKey::fromArrayIndex(hash);
+ uint hash = String::createHashValue(s.constData(), s.size(), &subtype);
+ if (subtype == Heap::String::StringType_ArrayIndex) {
+ if (Q_UNLIKELY(conversionBehvior == ForceConversionToId))
+ hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype);
+ else
+ return PropertyKey::fromArrayIndex(hash);
+ }
return resolveStringEntry(s, hash, subtype)->identifier;
}
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index b2a9bc1195..c608e5d89e 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -91,7 +91,8 @@ public:
return asPropertyKey(str->d());
}
- PropertyKey asPropertyKey(const QString &s);
+ enum KeyConversionBehavior { Default, ForceConversionToId };
+ PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default);
PropertyKey asPropertyKey(const char *s, int len);
PropertyKey asPropertyKeyImpl(const Heap::String *str);
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 21a91342fb..9f3c2af174 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -294,8 +294,12 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
bool hasProp = false;
QQmlPropertyData *propertyData = nullptr;
- QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
- name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
+
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, scopeObject));
+ QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(
+ v4, context, wrapper->d(), scopeObject, name,
+ QV4::QObjectWrapper::CheckRevision, &hasProp,
+ &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
@@ -321,9 +325,10 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
if (QObject *contextObject = context->contextObject()) {
bool hasProp = false;
QQmlPropertyData *propertyData = nullptr;
- result = QV4::QObjectWrapper::getQmlProperty(v4, context, contextObject,
- name, QV4::QObjectWrapper::CheckRevision,
- &hasProp, &propertyData);
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, contextObject));
+ result = QV4::QObjectWrapper::getQmlProperty(
+ v4, context, wrapper->d(), contextObject, name,
+ QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
@@ -682,8 +687,9 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec
// Search context object
if (QObject *contextObject = context->contextObject()) {
bool hasProp = false;
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
result = QV4::QObjectWrapper::getQmlProperty(
- engine, context, contextObject, name,
+ engine, context, wrapper->d(), contextObject, name,
QV4::QObjectWrapper::CheckRevision, &hasProp);
if (hasProp) {
if (base)
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 8be952571a..91bace3b01 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -248,7 +248,9 @@ QQmlPropertyData *QObjectWrapper::findProperty(
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
+ReturnedValue QObjectWrapper::getProperty(
+ ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
+ QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, property->coreIndex());
@@ -262,13 +264,13 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
ScopedContext global(scope, engine->qmlContext());
if (!global)
global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QV4::QObjectMethod::create(global, wrapper, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QV4::QObjectMethod::create(global, wrapper, property->coreIndex());
}
}
@@ -287,7 +289,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
-static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+static OptionalReturnedValue getDestroyOrToStringMethod(
+ ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
{
int index = 0;
if (name->equals(v4->id_destroy()))
@@ -345,7 +348,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
ExecutionEngine *v4 = engine();
- if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue;
QQmlPropertyData local;
@@ -373,13 +376,13 @@ ReturnedValue QObjectWrapper::getQmlProperty(
if (hasProperty)
*hasProperty = true;
- return getProperty(v4, d()->object(), result);
+ return getProperty(v4, d(), d()->object(), result);
}
ReturnedValue QObjectWrapper::getQmlProperty(
QV4::ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
- QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty,
- QQmlPropertyData **property)
+ Heap::Object *wrapper, QObject *object, String *name,
+ QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@@ -387,7 +390,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
return QV4::Encode::null();
}
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty))
return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
@@ -409,7 +412,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
if (property && result != &local)
*property = result;
- return getProperty(engine, object, result);
+ return getProperty(engine, wrapper, object, result);
} else {
// Check if this object is already wrapped.
if (!ddata || (ddata->jsWrapper.isUndefined() &&
@@ -429,13 +432,13 @@ ReturnedValue QObjectWrapper::getQmlProperty(
Q_ASSERT(ddata);
QV4::Scope scope(engine);
- QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object));
- if (!wrapper) {
+ QV4::Scoped<QObjectWrapper> rewrapped(scope, wrap(engine, object));
+ if (!rewrapped) {
if (hasProperty)
*hasProperty = false;
return QV4::Encode::null();
}
- return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
+ return rewrapped->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
}
@@ -867,7 +870,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
if (pd) {
QQmlPropertyData local;
local.load(property);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local);
}
return propName->toPropertyKey();
}
@@ -892,7 +895,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
if (pd) {
QQmlPropertyData local;
local.load(method);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local);
}
return methodName->toPropertyKey();
}
@@ -925,14 +928,14 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d()))
return *methodValue;
QQmlData *ddata = QQmlData::get(qobj, false);
if (!ddata || !ddata->propertyCache) {
QQmlPropertyData local;
QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, &local);
- return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined();
+ return property ? getProperty(engine, This->d(), qobj, property) : QV4::Encode::undefined();
}
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
@@ -1246,6 +1249,8 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
ddata->ownContext->emitDestruction();
+ if (ddata->ownContext->contextObject() == h->object())
+ ddata->ownContext->setContextObject(nullptr);
ddata->ownContext = nullptr;
ddata->context = nullptr;
}
@@ -1884,12 +1889,24 @@ bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, c
} else if (callType == QMetaType::QObjectStar) {
qobjectPtr = nullptr;
type = callType;
- if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
+ if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) {
qobjectPtr = qobjectWrapper->object();
- else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>())
- queryEngine = qmlTypeWrapper->isSingleton();
- else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr
+ } else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>()) {
+ if (qmlTypeWrapper->isSingleton()) {
+ queryEngine = true;
+ } else if (QObject *obj = qmlTypeWrapper->object()) {
+ // attached object case
+ qobjectPtr = obj;
+ return true;
+ } else {
+ // If this is a plain type wrapper without an instance,
+ // then we got a namespace, and that's a type error
+ type = QMetaType::UnknownType;
+ return false;
+ }
+ } else if (!value.isNull() && !value.isUndefined()) { // null and undefined are nullptr
return false;
+ }
} else if (callType == qMetaTypeId<QVariant>()) {
qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, QMetaType {}));
type = callType;
@@ -2069,35 +2086,49 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
}
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setObject(object);
-
- method->d()->index = index;
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, wrapper, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, valueType, index));
return method.asReturnedValue();
}
-void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
+void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex)
{
Heap::FunctionObject::init(scope);
+ wrapper.set(internalClass->engine, object);
+ index = methodIndex;
}
const QMetaObject *Heap::QObjectMethod::metaObject()
{
- if (valueTypeWrapper)
- return valueTypeWrapper->metaObject();
- return object()->metaObject();
+ Scope scope(internalClass->engine);
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->metaObject();
+ if (QObject *self = object())
+ return self->metaObject();
+ return nullptr;
+}
+
+QObject *Heap::QObjectMethod::object() const
+{
+ Scope scope(internalClass->engine);
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object();
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object();
+ return nullptr;
}
void Heap::QObjectMethod::ensureMethodsCache()
@@ -2193,14 +2224,19 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
d()->ensureMethodsCache();
Scope scope(v4);
- QQmlObjectOrGadget object(d()->object());
-
- if (!d()->object()) {
- if (!d()->valueTypeWrapper)
- return Encode::undefined();
-
- object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr());
- }
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
+ QQmlObjectOrGadget object = [&]() {
+ QV4::Scope scope(v4);
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
+ return QQmlObjectOrGadget(qobject->object());
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
+ return QQmlObjectOrGadget(type->object());
+ if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
+ valueWrapper = value->d();
+ return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
+ }
+ Q_UNREACHABLE();
+ }();
JSCallData cData(thisObject, argv, argc);
CallData *callData = cData.callData(scope);
@@ -2211,8 +2247,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
// The method might change the value.
const auto doCall = [&](const auto &call) {
if (!method->isConstant()) {
- QV4::Scoped<QQmlValueTypeReference> valueTypeReference(
- scope, d()->valueTypeWrapper.get());
+ QV4::Scoped<QQmlValueTypeReference> valueTypeReference(scope, valueWrapper);
if (valueTypeReference) {
QV4::ScopedValue rv(scope, call());
valueTypeReference->d()->writeBack();
@@ -2335,9 +2370,11 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
}
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ if (object) {
+ Scoped<QMetaObjectWrapper> metaObject(scope, this);
+ object->defineDefaultProperty(v4->id_constructor(), metaObject);
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ }
return object.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index f7bdda5f6f..ca0e5758c4 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -96,32 +96,29 @@ private:
};
#define QObjectMethodMembers(class, Member) \
- Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \
+ Member(class, Pointer, Object *, wrapper) \
Member(class, NoMark, QV4QPointer<QObject>, qObj) \
Member(class, NoMark, int, index)
-DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
- DECLARE_MARKOBJECTS(QObjectMethod);
+DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) {
+ DECLARE_MARKOBJECTS(QObjectMethod)
QQmlPropertyData *methods;
int methodCount;
alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
- void init(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope, Object *wrapper, int index);
void destroy()
{
if (methods != reinterpret_cast<QQmlPropertyData *>(&_singleMethod))
delete[] methods;
- qObj.destroy();
FunctionObject::destroy();
}
void ensureMethodsCache();
const QMetaObject *metaObject();
- QObject *object() const { return qObj.data(); }
- void setObject(QObject *o) { qObj = o; }
-
+ QObject *object() const;
};
struct QMetaObjectWrapper : FunctionObject {
@@ -160,6 +157,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static void initializeBindings(ExecutionEngine *engine);
+ const QMetaObject *metaObject() const
+ {
+ if (QObject *o = object())
+ return o->metaObject();
+ return nullptr;
+ }
+
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(
@@ -169,8 +173,8 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
\
static ReturnedValue getQmlProperty(
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
- QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr,
- QQmlPropertyData **property = nullptr);
+ Heap::Object *wrapper, QObject *object, String *name, RevisionMode revisionMode,
+ bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
@@ -186,7 +190,9 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall);
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
+ static ReturnedValue getProperty(
+ ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
+ QQmlPropertyData *property);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -243,7 +249,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE
if (!o || o->internalClass != lookup->qobjectLookup.ic)
return revertLookup();
- const Heap::QObjectWrapper *This = static_cast<const Heap::QObjectWrapper *>(o);
+ Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o);
QObject *qobj = This->object();
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
@@ -271,7 +277,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE
return revertLookup();
}
- return getProperty(engine, qobj, property);
+ return getProperty(engine, This, qobj, property);
}
struct QQmlValueTypeWrapper;
@@ -283,7 +289,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
- static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index);
static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index f27754f4a3..98300b1dd0 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1544,6 +1544,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
if (done->booleanValue())
break;
++argCount;
+ constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
+ if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
+ scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
+ return { nullptr, 0 };
+ }
v = scope.alloc<Scope::Uninitialized>();
}
}
@@ -1608,7 +1613,7 @@ ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine
return checkedResult(engine, fo.call(&thisObject, argv, argc));
}
- memcpy(frame->jsFrame->args, argv, argc * sizeof(Value));
+ memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
frame->init(fo.function(), frame->jsFrame->argValues<Value>(), argc,
frame->callerCanHandleTailCall());
frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject,
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index a55f72e241..69584c114e 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -230,6 +230,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
return calculateHashValue(ch, end, subtype);
}
+ static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype)
+ {
+ const QChar *end = ch + length;
+ return calculateHashValue<String::DisallowArrayIndex>(ch, end, subtype);
+ }
+
static uint createHashValue(const char *ch, int length, uint *subtype)
{
const char *end = ch + length;
@@ -243,15 +249,19 @@ protected:
static qint64 virtualGetLength(const Managed *m);
public:
- template <typename T>
+ enum IndicesBehavior {Default, DisallowArrayIndex};
+ template <IndicesBehavior Behavior = Default, typename T>
static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
{
// array indices get their number as hash value
- uint h = stringToArrayIndex(ch, end);
- if (h != UINT_MAX) {
- if (subtype)
- *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
- return h;
+ uint h = UINT_MAX;
+ if constexpr (Behavior != DisallowArrayIndex) {
+ h = stringToArrayIndex(ch, end);
+ if (h != UINT_MAX) {
+ if (subtype)
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
+ return h;
+ }
}
while (ch < end) {
diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h
index 3ba488d586..76381324aa 100644
--- a/src/qml/qml/ftw/qrecyclepool_p.h
+++ b/src/qml/qml/ftw/qrecyclepool_p.h
@@ -132,8 +132,7 @@ template<typename T, int Step>
T *QRecyclePool<T, Step>::New()
{
T *rv = d->allocate();
- new (rv) T;
- return rv;
+ return new (rv) T;
}
template<typename T, int Step>
@@ -141,8 +140,7 @@ template<typename T1>
T *QRecyclePool<T, Step>::New(const T1 &a)
{
T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return new (rv) T(a);
}
template<typename T, int Step>
@@ -150,8 +148,7 @@ template<typename T1>
T *QRecyclePool<T, Step>::New(T1 &a)
{
T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return new (rv) T(a);
}
template<typename T, int Step>
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 27088e1032..87d61f5839 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -155,24 +155,24 @@ public:
};
struct NotifyList {
- quint64 connectionMask;
-
- quint16 maximumTodoIndex;
- quint16 notifiesSize;
-
- QQmlNotifierEndpoint *todo;
- QQmlNotifierEndpoint**notifies;
+ QAtomicInteger<quint64> connectionMask;
+ QQmlNotifierEndpoint *todo = nullptr;
+ QQmlNotifierEndpoint**notifies = nullptr;
+ quint16 maximumTodoIndex = 0;
+ quint16 notifiesSize = 0;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
- NotifyList *notifyList;
+ QAtomicPointer<NotifyList> notifyList;
- inline QQmlNotifierEndpoint *notify(int index);
+ inline QQmlNotifierEndpoint *notify(int index) const;
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
- void disconnectNotifiers();
+
+ enum class DeleteNotifyList { Yes, No };
+ void disconnectNotifiers(DeleteNotifyList doDelete);
// The context that created the C++ object; not refcounted to prevent cycles
QQmlContextData *context = nullptr;
@@ -326,23 +326,31 @@ bool QQmlData::wasDeleted(const QObject *object)
return ddata && ddata->isQueuedForDeletion;
}
-QQmlNotifierEndpoint *QQmlData::notify(int index)
+inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
+{
+ return connectionMask & (1ULL << quint64(index % 64));
+}
+
+QQmlNotifierEndpoint *QQmlData::notify(int index) const
{
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
Q_ASSERT(index <= 0xFFFF);
- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
+ NotifyList *list = notifyList.loadRelaxed();
+ if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
return nullptr;
- } else if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else if (index <= notifyList->maximumTodoIndex) {
- notifyList->layout();
- }
- if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else {
- return nullptr;
+ if (index < list->notifiesSize)
+ return list->notifies[index];
+
+ if (index <= list->maximumTodoIndex) {
+ list->layout();
+ if (index < list->notifiesSize)
+ return list->notifies[index];
}
+
+ return nullptr;
}
/*
@@ -351,7 +359,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index)
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
+ // This can be called from any thread.
+ // We still use relaxed semantics. If we're on a thread different from the "home" thread
+ // of the QQmlData, two interesting things might happen:
+ //
+ // 1. The list might go away while we hold it. In that case we are dealing with an object whose
+ // QObject dtor is being executed concurrently. This is UB already without the notify lists.
+ // Therefore, we don't need to consider it.
+ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
+ // we "misreport" the endpoint. Since ordering of events across threads is inherently
+ // nondeterministic, either result is correct in that case. We can accept it.
+
+ NotifyList *list = notifyList.loadRelaxed();
+ return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
}
bool QQmlData::hasBindingBit(int coreIndex) const
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index ff8e2de238..e14931ddba 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -529,7 +529,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
// Disconnect the notifiers now - during object destruction this would be too late, since
// the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to
// get the metaobject anymore.
- d->disconnectNotifiers();
+ d->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
}
}
@@ -590,7 +590,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (!ddata->notifyList)
+
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
return;
auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
@@ -1447,49 +1450,73 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask.storeRelaxed(
+ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64)));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask.storeRelaxed(0);
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1573,7 +1600,7 @@ void QQmlData::destroyed(QObject *object)
guard->objectDestroyed(object);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 77ea35d034..15315a9000 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -130,9 +130,16 @@ public:
};
struct QPropertyChangeTrigger : QPropertyObserver {
- QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger), m_expression(expression) {}
- QQmlJavaScriptExpression * m_expression;
- QObject *target = nullptr;
+ Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger)
+
+ QPropertyChangeTrigger(QQmlJavaScriptExpression *expression)
+ : QPropertyObserver(&QPropertyChangeTrigger::trigger)
+ , m_expression(expression)
+ {
+ }
+
+ QPointer<QObject> target;
+ QQmlJavaScriptExpression *m_expression;
int propertyIndex = 0;
static void trigger(QPropertyObserver *, QUntypedPropertyData *);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index d04d6751cd..e33d942833 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1519,6 +1519,15 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
// The url for the path containing files for this import
QString url = resolveLocalUrl(base, uri);
+ if (url.isEmpty()) {
+ QQmlError error;
+ error.setDescription(
+ QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
+ error.setUrl(baseUrl);
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
if (!url.endsWith(Slash) && !url.endsWith(Backslash))
url += Slash;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index f937a3fff8..d156862ea4 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -390,19 +390,35 @@ void QQmlPropertyCapture::captureProperty(
captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
}
+bool QQmlJavaScriptExpression::needsPropertyChangeTrigger(QObject *target, int propertyIndex)
+{
+ TriggerList **prev = &qpropertyChangeTriggers;
+ TriggerList *current = qpropertyChangeTriggers;
+ while (current) {
+ if (!current->target) {
+ *prev = current->next;
+ QRecyclePool<TriggerList>::Delete(current);
+ current = *prev;
+ } else if (current->target == target && current->propertyIndex == propertyIndex) {
+ return false; // already installed
+ } else {
+ prev = &current->next;
+ current = current->next;
+ }
+ }
+
+ return true;
+}
+
void QQmlPropertyCapture::captureTranslation()
{
// use a unique invalid index to avoid needlessly querying the metaobject for
// the correct index of of the translationLanguage property
int const invalidIndex = -2;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == engine && trigger->propertyIndex == invalidIndex)
- return; // already installed
+ if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
+ trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
- auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
-
- trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
void QQmlPropertyCapture::captureBindableProperty(
@@ -412,16 +428,14 @@ void QQmlPropertyCapture::captureBindableProperty(
// the automatic capturing process already takes care of everything
if (!expression->mustCaptureBindableProperty())
return;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == o && trigger->propertyIndex == c)
- return; // already installed
+
+ if (expression->needsPropertyChangeTrigger(o, c)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
}
- auto trigger = expression->allocatePropertyChangeTrigger(o, c);
- QUntypedBindable bindable;
- void *argv[] = { &bindable };
- metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
- bindable.observe(trigger);
}
void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify)
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index bfdc922729..4edfeda633 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -166,6 +166,8 @@ public:
QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; }
bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); }
+
+ bool needsPropertyChangeTrigger(QObject *target, int propertyIndex);
QPropertyChangeTrigger *allocatePropertyChangeTrigger(QObject *target, int propertyIndex);
protected:
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index fdf74f4873..80a7ca1e1a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -282,7 +282,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_ENUMS
+ // Clone enums registered with the metatype system
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 0cc544e266..51dee8fa5f 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -163,7 +163,8 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP
void QQmlPropertyBindingJS::expressionChanged()
{
- if (!asBinding()->propertyDataPtr)
+ auto binding = asBinding();
+ if (!binding->propertyDataPtr)
return;
if (QQmlData::wasDeleted(asBinding()->target()))
return;
@@ -185,8 +186,9 @@ void QQmlPropertyBindingJS::expressionChanged()
return;
}
m_error.setTag(InEvaluationLoop);
- asBinding()->evaluateRecursive();
- asBinding()->notifyRecursive();
+ PendingBindingObserverList bindingObservers;
+ binding->evaluateRecursive(bindingObservers);
+ binding->notifyNonRecursive(bindingObservers);
m_error.setTag(NoTag);
}
@@ -263,10 +265,17 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
setIsUndefined(true);
//suspend binding evaluation state for reset and subsequent read
auto state = QtPrivate::suspendCurrentBindingStatus();
- prop.reset();
+ prop.reset(); // May re-allocate the bindingData
QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
QtPrivate::restoreBindingStatus(state);
writeBackCurrentValue(std::move(currentValue));
+
+ // Re-fetch binding data
+ bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ bindingDataPointer = QPropertyBindingDataPointer {bindingData};
+
// reattach the binding (without causing a new notification)
if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n"
@@ -278,7 +287,7 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
firstObserver = bindingDataPointer.firstObserver();
bindingData->d_ref() = reinterpret_cast<quintptr>(this) | QtPrivate::QPropertyBindingData::BindingBit;
if (firstObserver)
- bindingDataPointer.setObservers(firstObserver.ptr);
+ prependObserver(firstObserver);
} else {
QQmlError qmlError;
auto location = jsExpression()->sourceLocation();
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 0580af56f0..93736b0002 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -85,19 +85,31 @@ bool QQmlTypeWrapper::isSingleton() const
return d()->type().isSingleton();
}
+const QMetaObject *QQmlTypeWrapper::metaObject() const
+{
+ const QQmlType type = d()->type();
+ if (!type.isValid())
+ return nullptr;
+
+ if (type.isSingleton())
+ return type.metaObject();
+
+ return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine()));
+}
+
QObject *QQmlTypeWrapper::object() const
{
const QQmlType type = d()->type();
if (!type.isValid())
return nullptr;
- QQmlEngine *qmlEngine = engine()->qmlEngine();
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
if (type.isSingleton())
- return QQmlEnginePrivate::get(qmlEngine)->singletonInstance<QObject *>(type);
+ return qmlEngine->singletonInstance<QObject *>(type);
return qmlAttachedPropertiesObject(
d()->object,
- type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine)));
+ type.attachedPropertiesFunction(qmlEngine));
}
QObject* QQmlTypeWrapper::singletonObject() const
@@ -226,7 +238,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
// check for property.
bool ok;
- const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok);
+ const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(
+ v4, context, w->d(), qobjectSingleton, name,
+ QV4::QObjectWrapper::IgnoreRevision, &ok);
if (hasProperty)
*hasProperty = ok;
@@ -267,8 +281,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
QObject *ao = qmlAttachedPropertiesObject(
object,
type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine())));
- if (ao)
- return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty);
+ if (ao) {
+ return QV4::QObjectWrapper::getQmlProperty(
+ v4, context, w->d(), ao, name, QV4::QObjectWrapper::IgnoreRevision,
+ hasProperty);
+ }
// Fall through to base implementation
}
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 71e9ee04eb..56d50bbf83 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -102,6 +102,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
V4_NEEDS_DESTROY
bool isSingleton() const;
+ const QMetaObject *metaObject() const;
QObject *object() const;
QObject *singletonObject() const;
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index ed58157c26..a473fc5458 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -163,6 +163,7 @@ public:
int typeId() const;
QMetaType type() const;
bool write(QObject *target, int propertyIndex) const;
+ const QMetaObject *metaObject() const { return d()->metaObject(); }
QQmlPropertyData dataForPropertyKey(PropertyKey id) const;
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index a9ee30de49..9ccdf42587 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -403,7 +403,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
QStringLiteral("jsroot.qmltypes") };
const auto importBuiltins = [&](const QStringList &imports) {
for (auto const &dir : imports) {
- QDirIterator it { dir, qmltypesFiles, QDir::NoFilter, QDirIterator::Subdirectories };
+ QDirIterator it { dir, qmltypesFiles, QDir::NoFilter };
while (it.hasNext() && !qmltypesFiles.isEmpty()) {
readQmltypes(it.next(), &result.objects, &result.dependencies);
qmltypesFiles.removeOne(it.fileName());
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index c817cd61be..ee55fa214a 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -70,21 +70,35 @@ static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get())
template<typename QQmlJSScopePtr, typename Action>
static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
{
+ if (!type)
+ return false;
+
// NB: among other things, getQQmlJSScopeFromSmartPtr() also resolves const
// vs non-const pointer issue, so use it's return value as the type
using T = decltype(
getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(std::declval<QQmlJSScope::ConstPtr>()));
+ const bool isValueType = (type->accessSemantics() == QQmlJSScope::AccessSemantics::Value);
+
QDuplicateTracker<T> seen;
for (T scope = type; scope && !seen.hasSeen(scope);
scope = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->baseType())) {
- // Extensions override their base types
- for (T extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->extensionType());
- extension && !seen.hasSeen(extension);
- extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType())) {
+ QDuplicateTracker<T> seenExtensions;
+ // Extensions override the types they extend. However, usually base
+ // types of extensions are ignored. The unusual cases are when we
+ // have a value type or when we have the QObject type, in which case
+ // we also study the extension's base type hierarchy.
+ const bool isQObject = scope->internalName() == QLatin1String("QObject");
+ T extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->extensionType());
+ do {
+ if (!extension || seenExtensions.hasSeen(extension))
+ break;
+
if (check(extension))
return true;
- }
+
+ extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType());
+ } while (isValueType || isQObject);
if (check(scope))
return true;
diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp
index ee48a71c29..a2bbb25581 100644
--- a/src/qmldom/qqmldomreformatter.cpp
+++ b/src/qmldom/qqmldomreformatter.cpp
@@ -585,16 +585,27 @@ protected:
return false;
}
+
+ void outputScope(VariableScope scope) {
+ switch (scope) {
+ case VariableScope::Const:
+ out("const ");
+ break;
+ case VariableScope::Let:
+ out("let ");
+ break;
+ case VariableScope::Var:
+ out("var ");
+ break;
+ default:
+ break;
+ }
+ }
+
bool visit(PatternElement *ast) override
{
if (ast->isForDeclaration) {
- if (ast->scope == VariableScope::Var) {
- out("var ");
- } else if (ast->scope == VariableScope::Let) {
- out("let ");
- } else if (ast->scope == VariableScope::Const) {
- out("const ");
- }
+ outputScope(ast->scope);
}
accept(ast->bindingTarget);
switch (ast->type) {
@@ -678,7 +689,7 @@ protected:
if (ast->initialiser) {
accept(ast->initialiser);
} else if (ast->declarations) {
- out("var ");
+ outputScope(ast->declarations->declaration->scope);
accept(ast->declarations);
}
out("; "); // ast->firstSemicolonToken
@@ -867,12 +878,15 @@ protected:
out(ast->identifierToken);
}
out(ast->lparenToken);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
+ const bool needParentheses = ast->formals &&
+ (ast->formals->next ||
+ (ast->formals->element && ast->formals->element->bindingTarget));
+ if (ast->isArrowFunction && needParentheses)
out("(");
int baseIndent = lw.increaseIndent(1);
accept(ast->formals);
lw.decreaseIndent(1, baseIndent);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
+ if (ast->isArrowFunction && needParentheses)
out(")");
out(ast->rparenToken);
if (ast->isArrowFunction && !ast->formals)
@@ -963,7 +977,11 @@ protected:
bool visit(FormalParameterList *ast) override
{
for (FormalParameterList *it = ast; it; it = it->next) {
- out(it->element->bindingIdentifier.toString()); // TODO
+ // compare FormalParameterList::finish
+ if (auto id = it->element->bindingIdentifier.toString(); !id.startsWith(u"arg#"))
+ out(id);
+ if (it->element->bindingTarget)
+ accept(it->element->bindingTarget);
if (it->next)
out(", ");
}
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index 377a4c1e6f..d3cf23b490 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -1259,6 +1259,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
addCacheItem(cacheItem, it);
reuseItem(cacheItem, index, flags);
cacheItem->referenceObject();
+
+ if (index == m_compositor.count(group) - 1)
+ requestMoreIfNecessary();
return cacheItem->object;
}
diff --git a/src/quick/doc/images/qml-item-canvas-startAngle.png b/src/quick/doc/images/qml-item-canvas-startAngle.png
index bf82c3aa4b..7930284896 100644
--- a/src/quick/doc/images/qml-item-canvas-startAngle.png
+++ b/src/quick/doc/images/qml-item-canvas-startAngle.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/images/qt-logo.png b/src/quick/doc/snippets/qml/images/qt-logo.png
new file mode 100644
index 0000000000..30c621c9c6
--- /dev/null
+++ b/src/quick/doc/snippets/qml/images/qt-logo.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/item/itemGrab.qml
index e26e3dc55e..78fc53833b 100644
--- a/src/quick/doc/snippets/qml/itemGrab.qml
+++ b/src/quick/doc/snippets/qml/item/itemGrab.qml
@@ -54,44 +54,33 @@ Item {
width: 320
height: 480
-//! [grab-source]
+//! [grab-to-file]
Rectangle {
- id: source
+ id: sourceRectangle
width: 100
height: 100
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
+
+ Component.onCompleted: {
+ sourceRectangle.grabToImage(function(result) {
+ result.saveToFile("something.png")
+ })
+ }
}
-//! [grab-source]
+//! [grab-to-file]
-//! [grab-image-target]
+//! [grab-to-image]
Image {
id: image
}
-//! [grab-image-target]
- Timer {
- repeat: false
- running: true
- interval: 1000
- onTriggered: {
-//! [grab-to-file]
- // ...
- source.grabToImage(function(result) {
- result.saveToFile("something.png");
- });
-//! [grab-to-file]
-
-//! [grab-to-cache]
-
- // ...
- source.grabToImage(function(result) {
- image.source = result.url;
- },
- Qt.size(50, 50));
-//! [grab-to-cache]
- }
- }
+Component.onCompleted: {
+ sourceRectangle.grabToImage(function(result) {
+ image.source = result.url
+ }, Qt.size(50, 50))
+}
+//! [grab-to-image]
}
diff --git a/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
new file mode 100644
index 0000000000..1946ee4af0
--- /dev/null
+++ b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+//![0]
+Window {
+ // visible is false by default
+ Window {
+ transientParent: null
+ visible: true
+ }
+//![0]
+
+ id: outer
+ Timer {
+ interval: 2000
+ running: true
+ onTriggered: outer.visible = true
+ }
+//![1]
+}
+//![1]
diff --git a/src/quick/doc/snippets/qml/splashWindow.qml b/src/quick/doc/snippets/qml/splashWindow.qml
new file mode 100644
index 0000000000..17d4c674e3
--- /dev/null
+++ b/src/quick/doc/snippets/qml/splashWindow.qml
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [entire]
+import QtQuick
+
+Window {
+ id: mainWindow
+ title: "Main Window"
+ color: "#456"
+ property real defaultSpacing: 10
+
+ property Splash splash: Splash {
+ onTimeout: mainWindow.show()
+ }
+
+ component Splash: Window {
+ id: splash
+
+ // a splash screen has no titlebar
+ flags: Qt.SplashScreen
+ // the transparent color lets background behind the image edges show through
+ color: "transparent"
+ modality: Qt.ApplicationModal // in case another application window is showing
+ title: "Splash Window" // for the taskbar/dock, task switcher etc.
+ visible: true
+
+ // here we use the Screen attached property to center the splash window
+ //! [screen-properties]
+ x: (Screen.width - splashImage.width) / 2
+ y: (Screen.height - splashImage.height) / 2
+ //! [screen-properties]
+ width: splashImage.width
+ height: splashImage.height
+
+ property int timeoutInterval: 2000
+ signal timeout
+
+ Image {
+ id: splashImage
+ source: "images/qt-logo.png"
+ }
+
+ TapHandler {
+ onTapped: splash.timeout()
+ }
+
+ Timer {
+ interval: splash.timeoutInterval; running: true; repeat: false
+ onTriggered: {
+ splash.visible = false
+ splash.timeout()
+ }
+ }
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/snippets/qml/windowActiveAttached.qml b/src/quick/doc/snippets/qml/windowActiveAttached.qml
new file mode 100644
index 0000000000..9d2ddc2b99
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowActiveAttached.qml
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+
+Text {
+ text: Window.active ? "active" : "inactive"
+}
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowPalette.qml b/src/quick/doc/snippets/qml/windowPalette.qml
new file mode 100644
index 0000000000..c566d61dbb
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowPalette.qml
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+
+//![declaration-and-color]
+Window {
+ visible: true
+
+ // here we use the Window.active and Window.palette ordinary properties
+ color: active ? palette.active.window : palette.inactive.window
+//![declaration-and-color]
+
+ // colors that are not customized here come from SystemPalette
+ palette.active.window: "peachpuff"
+ palette.windowText: "brown"
+
+ Text {
+ anchors.centerIn: parent
+ // here we use the Window.active attached property and the Item.palette property
+ color: Window.active ? palette.active.windowText : palette.inactive.windowText
+ text: Window.active ? "active" : "inactive"
+ }
+
+ Button {
+ text: "Button"
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 6
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+//![closing-brace]
+}
+//![closing-brace]
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowVisibility.qml b/src/quick/doc/snippets/qml/windowVisibility.qml
new file mode 100644
index 0000000000..b5f89a3894
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowVisibility.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [entire]
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ id: win
+ flags: Qt.Window | Qt.WindowFullscreenButtonHint
+ visibility: fullscreenButton.checked ? Window.FullScreen : Window.Windowed
+
+ Button {
+ id: fullscreenButton
+ anchors {
+ right: parent.right
+ top: parent.top
+ margins: 6
+ }
+ width: height
+ checkable: true
+ Binding on checked { value: win.visibility === Window.FullScreen }
+ text: "â›ļ"
+ ToolTip.visible: hovered
+ ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+ ToolTip.text: win.visibility === Window.FullScreen ? qsTr("restore") : qsTr("fill screen")
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
index 574dfcb58f..da96d31258 100644
--- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
+++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
@@ -171,6 +171,16 @@ This will append the following declaration to the translation file, where you ca
</context>
\endcode
+Next, add the following bindings to the root QML component of your application:
+\code
+LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
+LayoutMirroring.childrenInherit: true
+\endcode
+
+The first binding ensures that the UI will be mirrored appropriately when a
+right-to-left locale is set. The second binding ensures that child items of the
+root component will also respect mirroring.
+
You can test that the layout direction works as expected by running your Qt Quick application with
the compiled translation file:
diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc
index b0cfa247ec..2f6c87e840 100644
--- a/src/quick/doc/src/examples.qdoc
+++ b/src/quick/doc/src/examples.qdoc
@@ -123,7 +123,6 @@ Creator.
\b{QML Types and Controls}
\list
\li \l{Qt Quick Controls - Gallery}{Controls Gallery}
- \li \l{Calendar Example}
\li \l{tableview/gameoflife}{TableView}
\li \l{Qt Quick Examples - Text}{Text and Fonts}
\li \l{Qt Quick Examples - Toggle Switch}{Custom Toggle Switch}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 7503e3ada9..ea1a2372db 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -2404,8 +2404,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::Func
\image qml-item-canvas-startAngle.png
- The \a anticlockwise parameter is \c true for each arc in the figure above
- because they are all drawn in the anticlockwise direction.
+ The \a anticlockwise parameter is \c false for each arc in the figure above
+ because they are all drawn in the clockwise direction.
\sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
Context Standard for arc()}
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 556ccd8e0f..39a0d6659a 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1393,10 +1393,14 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
const QVector2D velocity = firstPointLocalVelocity(event);
bool overThreshold = false;
- if (q->yflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
- if (q->xflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ if (event->pointCount() == 1) {
+ if (q->yflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
+ if (q->xflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ } else {
+ qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event;
+ }
drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
@@ -2567,11 +2571,17 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
QQuickDeliveryAgentPrivate::isTabletEvent(event)))
return false; // don't filter hover events or wheel events, for example
Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
- qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
Q_D(QQuickFlickable);
// If a touch event contains a new press point, don't steal right away: watch the movements for a while
if (isTouch && static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
d->stealMouse = false;
+ // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child
+ if (event->pointCount() > 1) {
+ qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver;
+ d->stealMouse = false;
+ } else {
+ qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
+ }
const auto &firstPoint = event->points().first();
if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) {
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index d94ccddcbc..916ae61cf5 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1673,6 +1673,7 @@ void QQuickGridView::setCellWidth(qreal cellWidth)
d->updateViewport();
emit cellWidthChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
}
}
@@ -1690,6 +1691,7 @@ void QQuickGridView::setCellHeight(qreal cellHeight)
d->updateViewport();
emit cellHeightChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
}
}
/*!
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 88f58cc0fa..52300bebe0 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -286,6 +286,7 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
\value Image.TileVertically the image is stretched horizontally and tiled vertically
\value Image.TileHorizontally the image is stretched vertically and tiled horizontally
\value Image.Pad the image is not transformed
+ \br
\table
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index 68c5f3db97..dc5750dc0a 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -381,17 +381,15 @@ QSharedPointer<QQuickItemGrabResult> QQuickItem::grabToImage(const QSize &target
*
* If the grab could not be initiated, the function returns \c false.
*
- * The following snippet shows how to grab an item and store the results to
- * a file.
+ * The following snippet shows how to grab an item and store the results in
+ * a file:
*
- * \snippet qml/itemGrab.qml grab-source
- * \snippet qml/itemGrab.qml grab-to-file
+ * \snippet qml/item/itemGrab.qml grab-to-file
*
* The following snippet shows how to grab an item and use the results in
- * another image element.
+ * another image element:
*
- * \snippet qml/itemGrab.qml grab-image-target
- * \snippet qml/itemGrab.qml grab-to-cache
+ * \snippet qml/item/itemGrab.qml grab-to-image
*
* \note This function will render the item to an offscreen surface and
* copy that surface from the GPU's memory into the CPU's memory, which can
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index 6b03d6c16b..5342adaddf 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -125,6 +125,8 @@ void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableIt
actions << QQuickStateAction(item->item, QLatin1String("x"), QVariant(to.x()));
actions << QQuickStateAction(item->item, QLatin1String("y"), QVariant(to.y()));
+ actions[0].fromValue = item->itemX();
+ actions[1].fromValue = item->itemY();
m_transitioner->runningJobs << this;
QQuickTransitionManager::transition(actions, trans, item->item);
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index a4c0a8b740..9e7a8f4803 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2213,6 +2213,10 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
\note While an item is in the pool, it might still be alive and respond
to connected signals and bindings.
+ \note For an item to be pooled, it needs to be completely flicked out of the bounds
+ of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer.}
+ Some items will also never be pooled or reused, such as \l currentItem.
+
The following example shows a delegate that animates a spinning rectangle. When
it is pooled, the animation is temporarily paused:
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index 5e95a92238..c2e3245011 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -61,8 +61,8 @@ QT_BEGIN_NAMESPACE
\ingroup qtquick-visual
\brief The QQuickPalette class contains color groups for each QML item state.
- A palette consists of three color groups: Active, Disabled, and Inactive.
- Active color group is the default group, its colors are used for other groups
+ A palette consists of three color groups: \c active, \c disabled, and \c inactive.
+ The \c active color group is the default group: its colors are used for other groups
if colors of these groups aren't explicitly specified.
In the following example, color is applied for all color groups:
@@ -104,18 +104,19 @@ QT_BEGIN_NAMESPACE
\endcode
It is also possible to specify colors like this:
- \code
- palette {
- buttonText: "azure"
- button: "khaki"
- disabled {
- buttonText: "lavender"
- button: "coral"
- }
- }
- \endcode
- This approach is convenient when you need to specify a whole palette with all color groups.
+ \snippet qtquickcontrols-custom-palette-buttons.qml palette
+
+ This approach is especially convenient when you need to specify a whole
+ palette with all color groups; but as with the other cases above, the
+ colors that are not specified are initialized from SystemPalette, or
+ potentially the \l {Styling Qt Quick Controls}{Qt Quick Controls style},
+ if one is in use.
+
+ \note Some Controls styles use some palette colors, but many styles use
+ independent colors.
+
+ \sa Window::palette, Item::palette, Popup::palette, SystemPalette
*/
/*!
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index 913003ef15..914ebd3047 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -75,6 +75,8 @@ QT_BEGIN_NAMESPACE
Note that the Screen type is not valid at Component.onCompleted, because
the Item or Window has not been displayed on a screen by this time.
+
+ \sa {Qt Quick Examples - Window and Screen}
*/
/*!
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index e35c128001..41fbcc7c48 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -396,7 +396,12 @@ QString QQuickTextEdit::text() const
remarkably better performance for modifying especially large rich text
content.
- \sa clear(), textFormat
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property.
+
+ \sa clear(), preeditText, textFormat
*/
void QQuickTextEdit::setText(const QString &text)
{
@@ -428,6 +433,11 @@ void QQuickTextEdit::setText(const QString &text)
\since 5.7
This property contains partial text input from an input method.
+
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa inputMethodHints
*/
QString QQuickTextEdit::preeditText() const
{
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 0096158d58..f41ab2f626 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -125,7 +125,13 @@ void QQuickTextInput::componentComplete()
The text in the TextInput.
- \sa clear()
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property. To get whole text displayed in the TextInput
+ use \l displayText property.
+
+ \sa clear(), displayText, preeditText
*/
QString QQuickTextInput::text() const
{
@@ -2391,7 +2397,10 @@ QString QQuickTextInput::displayText() const
This property contains partial text input from an input method.
- \sa displayText
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa displayText, inputMethodHints
*/
QString QQuickTextInput::preeditText() const
{
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 1a57fdd4f2..597cd300df 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -3269,10 +3269,12 @@ void QQuickWindow::endExternalCommands()
whether it's a dialog, popup, or a regular window, and whether it should
have a title bar, etc.
- The flags which you read from this property might differ from the ones
+ The flags that you read from this property might differ from the ones
that you set if the requested flags could not be fulfilled.
- \sa Qt::WindowFlags
+ \snippet qml/splashWindow.qml entire
+
+ \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen}
*/
/*!
@@ -3357,10 +3359,12 @@ void QQuickWindow::endExternalCommands()
visibility property you will always get the actual state, never
\c AutomaticVisibility.
- When a window is not visible its visibility is Hidden, and setting
+ When a window is not visible, its visibility is \c Hidden, and setting
visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
- \sa visible
+ \snippet qml/windowVisibility.qml entire
+
+ \sa visible, {Qt Quick Examples - Window and Screen}
\since 5.1
*/
@@ -3466,17 +3470,8 @@ void QQuickWindow::endExternalCommands()
Item or Window within which it was declared, you can remove that
relationship by setting \c transientParent to \c null:
- \qml
- import QtQuick.Window 2.13
-
- Window {
- // visible is false by default
- Window {
- transientParent: null
- visible: true
- }
- }
- \endqml
+ \snippet qml/nestedWindowTransientParent.qml 0
+ \snippet qml/nestedWindowTransientParent.qml 1
In order to cause the window to be centered above its transient parent by
default, depending on the window manager, it may also be necessary to set
@@ -3522,6 +3517,9 @@ void QQuickWindow::endExternalCommands()
The active status of the window.
+ \snippet qml/windowPalette.qml declaration-and-color
+ \snippet qml/windowPalette.qml closing-brace
+
\sa requestActivate()
*/
@@ -3535,14 +3533,7 @@ void QQuickWindow::endExternalCommands()
Here is an example which changes a label to show the active state of the
window in which it is shown:
- \qml
- import QtQuick 2.4
- import QtQuick.Window 2.2
-
- Text {
- text: Window.active ? "active" : "inactive"
- }
- \endqml
+ \snippet qml/windowActiveAttached.qml entire
*/
/*!
@@ -4150,10 +4141,11 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
palette which serves as a default for all application windows. You can also set the default palette
for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML.
- ApplicationWindow propagates explicit palette properties to child controls. If you change a specific
- property on the window's palette, that property propagates to all child controls in the window,
+ Window propagates explicit palette properties to child items and controls,
overriding any system defaults for that property.
+ \snippet qml/windowPalette.qml entire
+
\sa Item::palette, Popup::palette, ColorGroup, SystemPalette
//! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
*/
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 4ddef6b4c2..a9299ff17e 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -143,6 +143,18 @@ void QSGSoftwareRenderContext::initializeIfNeeded()
void QSGSoftwareRenderContext::invalidate()
{
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ qDeleteAll(m_fontEnginesToClean);
+ m_fontEnginesToClean.clear();
+
+ qDeleteAll(m_glyphCaches);
+ m_glyphCaches.clear();
+
m_sg->renderContextInvalidated(this);
emit invalidated();
}
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index c6877369c0..ab70d8e09f 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -3251,7 +3251,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
// unmerged batch since the material (and so the shaders) is the same.
QSGGeometry *g = gn->geometry();
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g);
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode);
if (!sms)
return false;
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 8dbfcad38b..b8f0f336f2 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -292,7 +292,7 @@ void QSGSimpleTextureNode::setTextureCoordinatesTransform(QSGSimpleTextureNode::
return;
d->texCoordMode = mode;
qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->sourceRect, d->texCoordMode);
- markDirty(DirtyMaterial);
+ markDirty(DirtyGeometry | DirtyMaterial);
}
/*!
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 63573847af..af60f5b02b 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -2567,7 +2567,38 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
QQmlListProperty<QObject> QQuickPropertyAnimation::targets()
{
Q_D(QQuickPropertyAnimation);
- return QQmlListProperty<QObject>(this, &(d->targets));
+ using ListPtr = QList<QPointer<QObject>> *;
+ using LP = QQmlListProperty<QObject>;
+ LP::AppendFunction appendFn = [](LP *prop, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->append(value);
+ };
+ LP::CountFunction countFn = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->size();
+ };
+
+ LP::AtFunction atFn = [](LP *prop, qsizetype index) -> QObject *
+ {
+ return static_cast<ListPtr>(prop->data)->at(index);
+ };
+
+ LP::ClearFunction clearFN = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->clear();
+ };
+
+ LP::ReplaceFunction replaceFn = [](LP *prop, qsizetype index, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->replace(index, value);
+ };
+
+ LP::RemoveLastFunction removeLastFn = [](LP *prop)
+ {
+ static_cast<ListPtr>(prop->data)->removeLast();
+ };
+
+ return QQmlListProperty<QObject>(this, &(d->targets), appendFn, countFn, atFn, clearFN, replaceFn, removeLastFn);
}
/*!
@@ -2639,7 +2670,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
if (!d->propertyName.isEmpty())
props << d->propertyName;
- QList<QObject*> targets = d->targets;
+ QList<QPointer<QObject>> targets = d->targets;
if (d->target)
targets.append(d->target);
@@ -2668,10 +2699,14 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
for (int i = 0; i < props.count(); ++i) {
for (int j = 0; j < targets.count(); ++j) {
+ const auto& guarded = targets.at(j);
+ if (guarded.isNull())
+ continue;
+ QObject *target = guarded.get();
QQuickStateAction myAction;
QString errorMessage;
const QString &propertyName = props.at(i);
- myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage);
+ myAction.property = d->createProperty(target, propertyName, this, &errorMessage);
if (myAction.property.isValid()) {
if (usingDefaultProperties)
successfullyCreatedDefaultProperty = true;
diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h
index 56121221bf..97bf156d0d 100644
--- a/src/quick/util/qquickanimation_p_p.h
+++ b/src/quick/util/qquickanimation_p_p.h
@@ -279,7 +279,7 @@ public:
QObject *target;
QString propertyName;
QString properties;
- QList<QObject *> targets;
+ QList<QPointer<QObject>> targets;
QList<QObject *> exclude;
QString defaultProperties;
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index fdd9cb2f13..62e792199d 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -235,6 +235,7 @@ QQuickApplication::QQuickApplication(QObject *parent)
connect(guiApp, &QGuiApplication::applicationDisplayNameChanged,
this, &QQuickApplication::displayNameChanged);
+ connect(guiApp, &QGuiApplication::primaryScreenChanged, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenAdded, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenRemoved, this, &QQuickApplication::updateScreens);
updateScreens();
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index b8167ebed3..a1f3fc64fd 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -1833,7 +1833,7 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- if (!relevant)
+ if (!item->clipRect().contains(itemPos))
return targets;
}
@@ -2116,11 +2116,6 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
if (item->acceptTouchEvents()) {
qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
- // If any parent filters the event, we're done.
- hasFiltered.clear();
- if (sendFilteredPointerEvent(&touchEvent, item))
- return;
-
// Deliver the touch event to the given item
qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
QCoreApplication::sendEvent(item, &touchEvent);
diff --git a/src/quick/util/qquicksystempalette.cpp b/src/quick/util/qquicksystempalette.cpp
index 7ccb19b5d3..01f72eab09 100644
--- a/src/quick/util/qquicksystempalette.cpp
+++ b/src/quick/util/qquicksystempalette.cpp
@@ -63,8 +63,8 @@ public:
The SystemPalette type provides access to the Qt application
palettes. This provides information about the standard colors used
for application windows, buttons and other features. These colors
- are grouped into three \e {color groups}: \c Active, \c Inactive,
- and \c Disabled. See the QPalette documentation for details about
+ are grouped into three \e {color groups}: \c active, \c inactive,
+ and \c disabled. See the QPalette documentation for details about
color groups and the properties provided by SystemPalette.
This can be used to color items in a way that provides a more
diff --git a/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
new file mode 100644
index 0000000000..82180fe465
--- /dev/null
+++ b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ visible: true
+
+ //![palette]
+ palette {
+ buttonText: "red"
+ button: "khaki"
+
+ disabled {
+ buttonText: "lavender"
+ button: "coral"
+ }
+ }
+ //![palette]
+
+ ColumnLayout {
+ id: layout
+ anchors.fill: parent
+ anchors.margins: 3
+ Button {
+ text: qsTr("Disabled button")
+ enabled: false
+ }
+
+ Button {
+ text: qsTr("Enabled button")
+ }
+
+ TextField {
+ Layout.fillWidth: true
+ placeholderText: "type something here"
+ }
+ }
+}
+//![entire]
diff --git a/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc
new file mode 100644
index 0000000000..7e12f53a6d
--- /dev/null
+++ b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc
@@ -0,0 +1,76 @@
+//! [detailed-description]
+A \1 provides a styled table header.
+It can either be used as an independent view or header for a \l TableView.
+
+You can add a header for a TableView by assigning a \1
+to the \l {TableView::syncView} property. The header and the table will
+then be kept in sync while flicking.
+
+By default, \1 displays
+\l {QAbstractItemModel::headerData()}{header data}
+from the \l {TableView::syncView}{sync view's} \l {TableView::model}{model}.
+If you don't wish to use this model, you can assign a different model to the
+\l {TableView::model}{model} property. If you assign a model that is a
+QAbstractItemModel, its header data will be used. Otherwise the data in
+the model will be used directly (for example, if you assign a model that
+is simply an array of strings).
+
+By default, \l textRole is set to \c "display", meaning that data from the
+model's \l {Qt::ItemDataRole}{Qt::DisplayRole} will be used. You can set
+this to another role name in order to have that data displayed instead.
+
+The application is responsible for placing the header at the
+correct location in the scene. You can add as many headers as you
+want to a single TableView, which can be useful if you for example want
+to place headers on all four sides of the table.
+
+The following snippet shows how you can add a horizontal and vertical header
+view to a table view:
+
+\snippet qtquickcontrols-headerview.qml 0
+
+A \1 will have
+\l {TableView::resizableColumns}{resizableColumns} set to \c true by default.
+//! [detailed-description]
+
+//! [syncView]
+This property holds the TableView to synchronize with.
+
+Once this property is bound to another TableView, both header and table
+will synchronize with regard to column widths, column spacing, and flicking
+\1.
+
+If the \l model is not explicitly set, then the header will use the syncView's
+model to label the columns.
+
+\sa model TableView
+//! [syncView]
+
+//! [model]
+This property holds the model providing data for the \1 header view.
+
+When model is not explicitly set, the header will use the syncView's
+model once syncView is set.
+
+If model is a QAbstractTableModel, its \1 headerData() will
+be accessed.
+
+If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
+will be accessed.
+
+Otherwise, the behavior is same as setting TableView::model.
+
+\sa TableView {TableView::model} {model} QAbstractTableModel
+//! [model]
+
+//! [textRole]
+This property holds the model role used to display text in each header cell.
+
+When the model has multiple roles, textRole can be set to determine which
+role should be displayed.
+
+If model is a QAbstractItemModel then it will default to "display"; otherwise
+it is empty.
+
+\sa QAbstractItemModel::roleNames()
+//! [textRole]
diff --git a/src/quickdialogs2/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
index f6cf1d8325..f05c4c83a9 100644
--- a/src/quickdialogs2/quickdialogs2/CMakeLists.txt
+++ b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_qml_module(QuickDialogs2
PLUGIN_TARGET qtquickdialogsplugin
DEPENDENCIES
QtQuick/auto
+ QtQuick.Dialogs.quickimpl/auto
SOURCES
qquickabstractdialog.cpp
qquickabstractdialog_p.h
diff --git a/src/quicktemplates2/qquickdeferredexecute.cpp b/src/quicktemplates2/qquickdeferredexecute.cpp
index 817415c492..635b7c142a 100644
--- a/src/quicktemplates2/qquickdeferredexecute.cpp
+++ b/src/quicktemplates2/qquickdeferredexecute.cpp
@@ -75,6 +75,15 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro
int propertyIndex = property.index();
int wasInProgress = enginePriv->inProgressCreations;
+ /* we don't want deferred properties to suddenly depend on arbitrary
+ other properties which might have trigerred the construction of
+ objects as a consequence of a read.
+ */
+ auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
+ auto cleanup = qScopeGuard([&](){
+ QtPrivate::restoreBindingStatus(bindingStatus);
+ });
+
for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
QQmlData::DeferredData *deferData = *dit;
@@ -139,6 +148,15 @@ void completeDeferred(QObject *object, const QString &property)
QQmlData *data = QQmlData::get(object);
QQmlComponentPrivate::DeferredState *state = deferredStates()->take(qHash(object, property));
if (data && state && !data->wasDeleted(object)) {
+ /* we don't want deferred properties to suddenly depend on arbitrary
+ other properties which might have trigerred the construction of
+ objects as a consequence of a read.
+ */
+ auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
+ auto cleanup = qScopeGuard([&](){
+ QtPrivate::restoreBindingStatus(bindingStatus);
+ });
+
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
QQmlComponentPrivate::completeDeferred(ep, state);
}
diff --git a/src/quicktemplates2/qquickheaderview.cpp b/src/quicktemplates2/qquickheaderview.cpp
index 94d5635ad1..298dbfbc05 100644
--- a/src/quicktemplates2/qquickheaderview.cpp
+++ b/src/quicktemplates2/qquickheaderview.cpp
@@ -47,16 +47,9 @@
\inherits TableView
\brief Provides a horizontal header view to accompany a \l TableView.
- A HorizontalHeaderView provides labeling of the columns of a \l TableView.
- To add a horizontal header to a TableView, bind the
- \l {HorizontalHeaderView::syncView} {syncView} property to the TableView:
+ \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
- \snippet qtquickcontrols2-headerview-simple.qml horizontal
-
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's horizontal headerData(); otherwise,
- the model's data().
+ \sa VerticalHeaderView
*/
/*!
@@ -66,112 +59,45 @@
\inherits TableView
\brief Provides a vertical header view to accompany a \l TableView.
- A VerticalHeaderView provides labeling of the rows of a \l TableView.
- To add a vertical header to a TableView, bind the
- \l {VerticalHeaderView::syncView} {syncView} property to the TableView:
-
- \snippet qtquickcontrols2-headerview-simple.qml vertical
+ \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's vertical headerData(); otherwise,
- the model's data().
+ \sa HorizontalHeaderView
*/
/*!
\qmlproperty TableView QtQuick::HorizontalHeaderView::syncView
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to column widths, column spacing, and flicking
- horizontally.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the columns.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {horizontally}
*/
/*!
\qmlproperty TableView QtQuick::VerticalHeaderView::syncView
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to row heights, row spacing, and flicking
- vertically.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the rows.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {vertically}
*/
/*!
\qmlproperty QVariant QtQuick::HorizontalHeaderView::model
- This property holds the model providing data for the horizontal header view.
-
- When model is not explicitly set, the header will use the syncView's
- model once syncView is set.
-
- If model is a QAbstractTableModel, its horizontal headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {horizontal}
*/
/*!
\qmlproperty QVariant QtQuick::VerticalHeaderView::model
- This property holds the model providing data for the vertical header view.
-
- When model is not explicitly set, it will be synchronized with syncView's model
- once syncView is set.
-
- If model is a QAbstractTableModel, its vertical headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {vertical}
*/
/*!
\qmlproperty QString QtQuick::HorizontalHeaderView::textRole
- This property holds the model role used to display text in each header cell.
-
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
-
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
/*!
\qmlproperty QString QtQuick::VerticalHeaderView::textRole
- This property holds the model role used to display text in each header cell.
-
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
-
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
QT_BEGIN_NAMESPACE
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp
index e045375f89..373e78f71c 100644
--- a/src/quicktemplates2/qquickmenu.cpp
+++ b/src/quicktemplates2/qquickmenu.cpp
@@ -745,6 +745,12 @@ QQuickMenu::~QQuickMenu()
// been destroyed before that is called.
while (d->contentModel->count() > 0)
d->removeItem(0, d->itemAt(0));
+
+ if (d->contentItem) {
+ const auto children = d->contentItem->childItems();
+ for (QQuickItem *child : std::as_const(children))
+ QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder);
+ }
}
/*!
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index 4ac642ccc4..de862cfc11 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -340,6 +340,7 @@ void QQuickPopupPrivate::closeOrReject()
dialog->reject();
else
q->close();
+ touchId = -1;
}
bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags)
diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp
index 3277ba524f..8a18d6266d 100644
--- a/src/quicktemplates2/qquickswipeview.cpp
+++ b/src/quicktemplates2/qquickswipeview.cpp
@@ -110,7 +110,7 @@ class QQuickSwipeViewPrivate : public QQuickContainerPrivate
Q_DECLARE_PUBLIC(QQuickSwipeView)
public:
- void resizeItem(QQuickItem *item);
+ void resizeItem(int index, QQuickItem *item);
void resizeItems();
static QQuickSwipeViewPrivate *get(QQuickSwipeView *view);
@@ -144,25 +144,29 @@ public:
int currentIndex = -1;
};
+void QQuickSwipeViewPrivate::resizeItem(int index, QQuickItem *item)
+{
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
+ if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
+ qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
+ item->setProperty("_q_QQuickSwipeView_warned", true);
+ }
+ if (orientation == Qt::Horizontal)
+ item->setPosition({index * (contentItem->width() + spacing), 0});
+ else
+ item->setPosition({0, index * (contentItem->height() + spacing)});
+ item->setSize(QSizeF(contentItem->width(), contentItem->height()));
+}
+
void QQuickSwipeViewPrivate::resizeItems()
{
Q_Q(QQuickSwipeView);
const int count = q->count();
for (int i = 0; i < count; ++i) {
QQuickItem *item = itemAt(i);
- if (item) {
- QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
- // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
- if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
- qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
- item->setProperty("_q_QQuickSwipeView_warned", true);
- }
- if (orientation == Qt::Horizontal)
- item->setPosition({i * (contentItem->width() + spacing), 0});
- else
- item->setPosition({0, i * (contentItem->height() + spacing)});
- item->setSize(QSizeF(contentItem->width(), contentItem->height()));
- }
+ if (item)
+ resizeItem(i, item);
}
}
@@ -301,6 +305,13 @@ QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object)
return new QQuickSwipeViewAttached(object);
}
+void QQuickSwipeView::componentComplete()
+{
+ Q_D(QQuickSwipeView);
+ QQuickContainer::componentComplete();
+ d->resizeItems();
+}
+
void QQuickSwipeView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickSwipeView);
@@ -312,7 +323,7 @@ void QQuickSwipeView::itemAdded(int index, QQuickItem *item)
{
Q_D(QQuickSwipeView);
if (isComponentComplete())
- item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height()));
+ d->resizeItem(index, item);
QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item));
if (attached)
QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index);
diff --git a/src/quicktemplates2/qquickswipeview_p.h b/src/quicktemplates2/qquickswipeview_p.h
index 8a09a37e22..7723a58374 100644
--- a/src/quicktemplates2/qquickswipeview_p.h
+++ b/src/quicktemplates2/qquickswipeview_p.h
@@ -93,6 +93,7 @@ Q_SIGNALS:
Q_REVISION(2, 2) void orientationChanged();
protected:
+ void componentComplete() override;
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void itemAdded(int index, QQuickItem *item) override;
void itemMoved(int index, QQuickItem *item) override;
diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp
index 9051eb15a9..cd6196e677 100644
--- a/src/quicktestutils/quick/viewtestutils.cpp
+++ b/src/quicktestutils/quick/viewtestutils.cpp
@@ -32,6 +32,7 @@
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
+#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtTest/QTest>
@@ -471,6 +472,10 @@ namespace QQuickTouchUtils {
}
+namespace QTest {
+ int Q_TESTLIB_EXPORT defaultMouseDelay();
+}
+
namespace QQuickTest {
/*! \internal
@@ -528,6 +533,88 @@ namespace QQuickTest {
return false;
return true;
}
+
+ // TODO maybe move the generic pointerPress/Move/Release functions to QTestLib later on
+
+ static Qt::MouseButton pressedTabletButton = Qt::NoButton;
+ static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier;
+
+ void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mousePress(window, button, modifiers, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).press(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ pressedTabletButton = button;
+ pressedTabletModifiers = modifiers;
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ button, 0.8, 0, 0, 0, 0, 0, modifiers);
+ break;
+ default:
+ qWarning() << "can't send a press event from" << dev;
+ break;
+ }
+ }
+
+ void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mouseMove(window, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).move(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers);
+ break;
+ default:
+ qWarning() << "can't send a move event from" << dev;
+ break;
+ }
+ }
+
+ void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mouseRelease(window, button, modifiers, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).release(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers);
+ break;
+ default:
+ qWarning() << "can't send a press event from" << dev;
+ break;
+ }
+ }
+
}
QT_END_NAMESPACE
diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h
index fd38844446..feedaef76f 100644
--- a/src/quicktestutils/quick/viewtestutils_p.h
+++ b/src/quicktestutils/quick/viewtestutils_p.h
@@ -203,6 +203,17 @@ namespace QQuickTest {
[[nodiscard]] bool initView(QQuickView &v, const QUrl &url,
bool moveMouseOut = true, QByteArray *errorMessage = nullptr);
[[nodiscard]] bool showView(QQuickView &v, const QUrl &url);
+
+ void pointerPress(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+
+ void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId,
+ const QPoint &p);
+
+ void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId,
+ const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier);
}
QT_END_NAMESPACE
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index d316d12588..c593ca2a81 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -1466,6 +1466,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
// top-level window always.
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ // It's not just the timestamp but also the globalPressPosition, velocity etc.
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1481,10 +1483,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
// See QTBUG-25831
QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ pressEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
e->setAccepted(pressEvent.isAccepted());
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
}
@@ -1544,6 +1548,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1557,6 +1562,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index fd4a6748c1..d9730fbb58 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -13,7 +13,9 @@ endif()
if(TARGET Qt::Quick)
add_subdirectory(quicktest)
endif()
-add_subdirectory(core)
+if (TARGET Qt::QuickTest)
+ add_subdirectory(core)
+endif()
add_subdirectory(qmldevtools)
add_subdirectory(toolsupport)
if(NOT UIKIT AND NOT ANDROID AND NOT QNX) # FIXME: QTBUG-92591 QTBUG-100202
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index ddd5f633e7..c4c8d8382c 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -65,6 +65,7 @@ if(TARGET Qt::Qml)
_qt_internal_test_expect_pass(test_plugins)
endif()
_qt_internal_test_expect_pass(empty_qmldir)
+ _qt_internal_test_expect_fail(test_internal_singleton)
endif()
if(TARGET Qt::Quick)
diff --git a/tests/auto/cmake/test_internal_singleton/CMakeLists.txt b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt
new file mode 100644
index 0000000000..f1db46a0f9
--- /dev/null
+++ b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+cmake_minimum_required(VERSION 3.19)
+
+project(test_internal_singleton)
+
+find_package(Qt6 REQUIRED COMPONENTS Qml)
+
+qt_standard_project_setup()
+
+set_source_files_properties(Test.qml PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+ QT_QML_INTERNAL_TYPE TRUE
+)
+
+qt_add_qml_module(test_internal_singleton
+ URI Controls
+ VERSION 1.0
+ QML_FILES
+ Test.qml
+)
diff --git a/tests/auto/cmake/test_internal_singleton/Test.qml b/tests/auto/cmake/test_internal_singleton/Test.qml
new file mode 100644
index 0000000000..a2eb03bd4e
--- /dev/null
+++ b/tests/auto/cmake/test_internal_singleton/Test.qml
@@ -0,0 +1,4 @@
+pragma singleton
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index a5e8713700..81ac086294 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -108,6 +108,8 @@ private slots:
void collectGarbageNestedWrappersTwoEngines();
void gcWithNestedDataStructure();
void stacktrace();
+ void unshiftAndSort();
+ void unshiftAndPushAndSort();
void numberParsing_data();
void numberParsing();
void automaticSemicolonInsertion();
@@ -284,6 +286,11 @@ private slots:
void thisInConstructor();
void forOfAndGc();
+ void symbolToVariant();
+
+ void garbageCollectedObjectMethodBase();
+ void spreadNoOverflow();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -995,6 +1002,17 @@ private:
int m_called = 1;
};
+class TestQMetaObject2 : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE TestQMetaObject2(int a) : m_called(a) {}
+ int called() const { return m_called; }
+
+private:
+ int m_called = 1;
+};
+
void tst_QJSEngine::newQObjectPropertyCache()
{
QScopedPointer<QObject> obj(new QObject);
@@ -1069,6 +1087,18 @@ void tst_QJSEngine::newQMetaObject() {
QCOMPARE(metaObject.property("C").toInt(), 2);
}
+ {
+ QJSEngine engine;
+ const QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject2::staticMetaObject);
+ engine.globalObject().setProperty(QLatin1String("Example"), metaObject);
+
+ const QJSValue invalid = engine.evaluate(QLatin1String("new Example()"));
+ QVERIFY(invalid.isError());
+ QCOMPARE(invalid.toString(), QLatin1String("Error: Insufficient arguments"));
+
+ const QJSValue valid = engine.evaluate(QLatin1String("new Example(123)"));
+ QCOMPARE(qjsvalue_cast<TestQMetaObject2 *>(valid)->called(), 123);
+ }
}
void tst_QJSEngine::exceptionInSlot()
@@ -2018,6 +2048,81 @@ void tst_QJSEngine::stacktrace()
}
}
+void tst_QJSEngine::unshiftAndSort()
+{
+ QJSEngine engine;
+ QJSValue func = engine.evaluate(R"""(
+ (function (objectArr, currIdx) {
+ objectArr.unshift({"sortIndex": currIdx});
+ objectArr.sort(function(a, b) {
+ if (a.sortIndex > b.sortIndex)
+ return 1;
+ if (a.sortIndex < b.sortIndex)
+ return -1;
+ return 0;
+ });
+ return objectArr;
+ })
+ )""");
+ QVERIFY(func.isCallable());
+ QJSValue objectArr = engine.newArray();
+
+ for (int i = 0; i < 5; ++i) {
+ objectArr = func.call({objectArr, i});
+ QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString()));
+ const int length = objectArr.property("length").toInt();
+
+ // It did add one element
+ QCOMPARE(length, i + 1);
+
+ for (int x = 0; x < length; ++x) {
+ // We didn't sort cruft into the array.
+ QVERIFY(!objectArr.property(x).isUndefined());
+
+ // The array is actually sorted.
+ QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x);
+ }
+ }
+}
+
+void tst_QJSEngine::unshiftAndPushAndSort()
+{
+ QJSEngine engine;
+ QJSValue func = engine.evaluate(R"""(
+ (function (objectArr, currIdx) {
+ objectArr.unshift({"sortIndex": currIdx});
+ objectArr.push({"sortIndex": currIdx + 1});
+ objectArr.sort(function(a, b) {
+ if (a.sortIndex > b.sortIndex)
+ return 1;
+ if (a.sortIndex < b.sortIndex)
+ return -1;
+ return 0;
+ });
+ return objectArr;
+ })
+ )""");
+ QVERIFY(func.isCallable());
+ QJSValue objectArr = engine.newArray();
+
+ for (int i = 0; i < 20; i += 2) {
+ objectArr = func.call({objectArr, i});
+ QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString()));
+ const int length = objectArr.property("length").toInt();
+
+ // It did add 2 elements
+ QCOMPARE(length, i + 2);
+
+ for (int x = 0; x < length; ++x) {
+ // We didn't sort cruft into the array.
+ QVERIFY(!objectArr.property(x).isUndefined());
+
+ // The array is actually sorted.
+ QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x);
+ }
+ }
+}
+
void tst_QJSEngine::numberParsing_data()
{
QTest::addColumn<QString>("string");
@@ -5589,6 +5694,120 @@ void tst_QJSEngine::forOfAndGc()
QTRY_VERIFY(o->property("count").toInt() > 32768);
}
+void tst_QJSEngine::symbolToVariant()
+{
+ QJSEngine engine;
+ const QJSValue val = engine.newSymbol("asymbol");
+ QCOMPARE(val.toVariant(), QStringLiteral("Symbol(asymbol)"));
+
+ const QVariant retained = val.toVariant(QJSValue::RetainJSObjects);
+ QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
+ QVERIFY(retained.value<QJSValue>().strictlyEquals(val));
+
+ QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)"));
+}
+
+class PACHelper : public QObject {
+ Q_OBJECT
+public:
+ Q_INVOKABLE bool shExpMatch(const QString &, const QString &) { return false; }
+ Q_INVOKABLE QString dnsResolve(const QString &) { return QString{}; }
+};
+
+class ProxyAutoConf {
+public:
+ void exposeQObjectMethodsAsGlobal(QJSEngine *engine, QObject *object)
+ {
+ QJSValue helper = engine->newQObject(object);
+ QJSValue g = engine->globalObject();
+ QJSValueIterator it(helper);
+ while (it.hasNext()) {
+ it.next();
+ if (!it.value().isCallable())
+ continue;
+ g.setProperty(it.name(), it.value());
+ }
+ }
+
+ bool parse(const QString & pacBytes)
+ {
+ jsFindProxyForURL = QJSValue();
+ engine = std::make_unique<QJSEngine>();
+ exposeQObjectMethodsAsGlobal(engine.get(), new PACHelper);
+ engine->evaluate(pacBytes);
+ jsFindProxyForURL = engine->globalObject().property(QStringLiteral("FindProxyForURL"));
+ return true;
+ }
+
+ QString findProxyForUrl(const QString &url, const QString &host)
+ {
+ QJSValueList args;
+ args << url << host;
+ engine->collectGarbage();
+ QJSValue callResult = jsFindProxyForURL.call(args);
+ return callResult.toString().trimmed();
+ }
+
+private:
+ std::unique_ptr<QJSEngine> engine;
+ QJSValue jsFindProxyForURL;
+};
+
+QString const pacstring = R"js(
+function FindProxyForURL(host) {
+ list_split_all = Array(
+ "oneoneoneoneo.oneo.oneo.oneoneo.one",
+ "twotwotwotwotw.otwo.twot.wotwotw.otw",
+ "threethreethr.eeth.reet.hreethr.eet",
+ "fourfourfourfo.urfo.urfo.urfourf.our",
+ "fivefivefivef.ivef.ivef.ivefive.fiv",
+ "sixsixsixsixsi.xsix.sixs.ixsixsi.xsi",
+ "sevensevenseve.nsev.ense.venseve.nse",
+ "eight.eighteigh.tei",
+ "*.nin.eninen.ine"
+ )
+ list_myip_direct =
+ "10.254.0.0/255.255.0.0"
+ for (i = 0; i < list_split_all.length; ++i)
+ for (j = 0; j < list_myip_direct.length; ++j)
+ shExpMatch(host, list_split_all)
+ shExpMatch()
+ dnsResolve()}
+)js";
+
+void tst_QJSEngine::garbageCollectedObjectMethodBase()
+{
+ ProxyAutoConf proxyConf;
+ bool pac_read = false;
+
+ const auto processUrl = [&](QString const &url, QString const &host)
+ {
+ if (!pac_read) {
+ proxyConf.parse(pacstring);
+ pac_read = true;
+ }
+ return proxyConf.findProxyForUrl(url, host);
+ };
+
+ const QString url = QStringLiteral("https://servername.domain.test");
+ const QString host = QStringLiteral("servername.domain.test");
+
+ for (size_t i = 0; i < 5; ++i) {
+ auto future = std::async(processUrl, url, host);
+ QCOMPARE(future.get(), QLatin1String("Error: Insufficient arguments"));
+ }
+}
+
+void tst_QJSEngine::spreadNoOverflow()
+{
+ QJSEngine engine;
+
+ const QString program = QString::fromLatin1("var a = [] ;a.length = 555840;Math.max(...a)");
+ const QJSValue result = engine.evaluate(program);
+ QVERIFY(result.isError());
+ QCOMPARE(result.errorType(), QJSValue::RangeError);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml
new file mode 100644
index 0000000000..2a47b2f152
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item { Component.onCompleted: { let f = ([]) => {};
+ let g = ([a]) => {};
+ let h = ([a, b]) => {};
+ let i = ([a, ...b]) => {};
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml
new file mode 100644
index 0000000000..2a47b2f152
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item { Component.onCompleted: { let f = ([]) => {};
+ let g = ([a]) => {};
+ let h = ([a, b]) => {};
+ let i = ([a, ...b]) => {};
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml
new file mode 100644
index 0000000000..5fecc1d180
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ function foo() {
+ for (let i = 0; i < 5; ++i)
+ console.log(i);
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/forWithLet.qml b/tests/auto/qml/qmlformat/data/forWithLet.qml
new file mode 100644
index 0000000000..afed76ebce
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/forWithLet.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+function foo() {
+for (let i = 0; i < 5; ++i)
+console.log(i);
+}
+}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 3837e193e5..29a14900d2 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -257,6 +257,11 @@ void TestQmlformat::testFormat_data()
QTest::newRow("ecmaScriptClassInQml")
<< "ecmaScriptClassInQml.qml"
<< "ecmaScriptClassInQml.formatted.qml" << QStringList {};
+ QTest::newRow("arrowFunctionWithBinding")
+ << "arrowFunctionWithBinding.qml"
+ << "arrowFunctionWithBinding.formatted.qml" << QStringList{};
+ QTest::newRow("forWithLet") << "forWithLet.qml"
+ << "forWithLet.formatted.qml" << QStringList {};
}
void TestQmlformat::testFormat()
diff --git a/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes
new file mode 100644
index 0000000000..96f98dd14e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes
@@ -0,0 +1,513 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -builtins'
+
+Module {
+ dependencies: []
+ Component {
+ name: "Qt"
+ Enum {
+ name: "GlobalColor"
+ values: {
+ "color0": 0,
+ "color1": 1,
+ "black": 2,
+ "white": 3,
+ "darkGray": 4,
+ "gray": 5,
+ "lightGray": 6,
+ "red": 7,
+ "green": 8,
+ "blue": 9,
+ "cyan": 10,
+ "magenta": 11,
+ "yellow": 12,
+ "darkRed": 13,
+ "darkGreen": 14,
+ "darkBlue": 15,
+ "darkCyan": 16,
+ "darkMagenta": 17,
+ "darkYellow": 18,
+ "transparent": 19
+ }
+ }
+ Enum {
+ name: "KeyboardModifiers"
+ values: {
+ "NoModifier": 0,
+ "ShiftModifier": 33554432,
+ "ControlModifier": 67108864,
+ "AltModifier": 134217728,
+ "MetaModifier": 268435456,
+ "KeypadModifier": 536870912,
+ "GroupSwitchModifier": 1073741824,
+ "KeyboardModifierMask": -33554432
+ }
+ }
+ Enum {
+ name: "MouseButtons"
+ values: {
+ "NoButton": 0,
+ "LeftButton": 1,
+ "RightButton": 2,
+ "MidButton": 4, // For backwards compatibility
+ "MiddleButton": 4,
+ "BackButton": 8,
+ "XButton1": 8,
+ "ExtraButton1": 8,
+ "ForwardButton": 16,
+ "XButton2": 16,
+ "ExtraButton2": 16,
+ "TaskButton": 32,
+ "ExtraButton3": 32,
+ "ExtraButton4": 64,
+ "ExtraButton5": 128,
+ "ExtraButton6": 256,
+ "ExtraButton7": 512,
+ "ExtraButton8": 1024,
+ "ExtraButton9": 2048,
+ "ExtraButton10": 4096,
+ "ExtraButton11": 8192,
+ "ExtraButton12": 16384,
+ "ExtraButton13": 32768,
+ "ExtraButton14": 65536,
+ "ExtraButton15": 131072,
+ "ExtraButton16": 262144,
+ "ExtraButton17": 524288,
+ "ExtraButton18": 1048576,
+ "ExtraButton19": 2097152,
+ "ExtraButton20": 4194304,
+ "ExtraButton21": 8388608,
+ "ExtraButton22": 16777216,
+ "ExtraButton23": 33554432,
+ "ExtraButton24": 67108864,
+ "AllButtons": 134217727,
+ "MaxMouseButton": 67108864,
+ "MouseButtonMask": -1
+ }
+ }
+ Enum {
+ name: "Orientation"
+ values: {
+ "Horizontal": 1,
+ "Vertical": 2
+ }
+ }
+ Enum {
+ name: "Orientations"
+ values: {
+ "Horizontal": 1,
+ "Vertical": 2
+ }
+ }
+ Enum {
+ name: "FocusPolicy"
+ values: {
+ "NoFocus": 0,
+ "TabFocus": 1,
+ "ClickFocus": 2,
+ "StrongFocus": 11,
+ "WheelFocus": 15
+ }
+ }
+ Enum {
+ name: "TabFocusBehavior"
+ values: {
+ "NoTabFocus": 0,
+ "TabFocusTextControls": 1,
+ "TabFocusListControls": 2,
+ "TabFocusAllControls": 255
+ }
+ }
+ Enum {
+ name: "SortOrder"
+ values: {
+ "AscendingOrder": 0,
+ "DescendingOrder": 1
+ }
+ }
+ Enum {
+ name: "SplitBehavior"
+ values: {
+ "KeepEmptyParts": 0,
+ "SkipEmptyParts": 1
+ }
+ }
+ Enum {
+ name: "Alignment"
+ values: {
+ "AlignLeft": 1,
+ "AlignLeading": 1,
+ "AlignRight": 2,
+ "AlignTrailing": 2,
+ "AlignHCenter": 4,
+ "AlignJustify": 8,
+ "AlignAbsolute": 16,
+ "AlignHorizontal_Mask": 31,
+ "AlignTop": 32,
+ "AlignBottom": 64,
+ "AlignVCenter": 128,
+ "AlignBaseline": 256,
+ "AlignVertical_Mask": 480,
+ "AlignCenter": 132
+ }
+ }
+ Enum {
+ name: "TextFlag"
+ values: {
+ "TextSingleLine": 256,
+ "TextDontClip": 512,
+ "TextExpandTabs": 1024,
+ "TextShowMnemonic": 2048,
+ "TextWordWrap": 4096,
+ "TextWrapAnywhere": 8192,
+ "TextDontPrint": 16384,
+ "TextIncludeTrailingSpaces": 134217728,
+ "TextHideMnemonic": 32768,
+ "TextJustificationForced": 65536,
+ "TextForceLeftToRight": 131072,
+ "TextForceRightToLeft": 262144,
+ "TextLongestVariant": 524288,
+ "TextBypassShaping": 1048576
+ }
+ }
+ Enum {
+ name: "TextElideMode"
+ values: {
+ "ElideLeft": 0,
+ "ElideRight": 1,
+ "ElideMiddle": 2,
+ "ElideNone": 3
+ }
+ }
+ Enum {
+ name: "WindowType"
+ values: {
+ "Widget": 0,
+ "Window": 1,
+ "Dialog": 3,
+ "Sheet": 5,
+ "Drawer": 7,
+ "Popup": 9,
+ "Tool": 11,
+ "ToolTip": 13,
+ "SplashScreen": 15,
+ "Desktop": 17,
+ "SubWindow": 18,
+ "ForeignWindow": 33,
+ "CoverWindow": 65,
+ "WindowType_Mask": 255,
+ "MSWindowsFixedSizeDialogHint": 256,
+ "MSWindowsOwnDC": 512,
+ "BypassWindowManagerHint": 1024,
+ "X11BypassWindowManagerHint": 1024,
+ "FramelessWindowHint": 2048,
+ "WindowTitleHint": 4096,
+ "WindowSystemMenuHint": 8192,
+ "WindowMinimizeButtonHint": 16384,
+ "WindowMaximizeButtonHint": 32768,
+ "WindowMinMaxButtonsHint": 49152,
+ "WindowContextHelpButtonHint": 65536,
+ "WindowShadeButtonHint": 131072,
+ "WindowStaysOnTopHint": 262144,
+ "WindowTransparentForInput": 524288,
+ "WindowOverridesSystemGestures": 1048576,
+ "WindowDoesNotAcceptFocus": 2097152,
+ "MaximizeUsingFullscreenGeometryHint": 4194304,
+ "CustomizeWindowHint": 33554432,
+ "WindowStaysOnBottomHint": 67108864,
+ "WindowCloseButtonHint": 134217728,
+ "MacWindowToolBarButtonHint": 268435456,
+ "BypassGraphicsProxyWidget": 536870912,
+ "NoDropShadowWindowHint": 1073741824,
+ "WindowFullscreenButtonHint": -2147483648
+ }
+ }
+ Enum {
+ name: "WindowFlags"
+ values: {
+ "Widget": 0,
+ "Window": 1,
+ "Dialog": 3,
+ "Sheet": 5,
+ "Drawer": 7,
+ "Popup": 9,
+ "Tool": 11,
+ "ToolTip": 13,
+ "SplashScreen": 15,
+ "Desktop": 17,
+ "SubWindow": 18,
+ "ForeignWindow": 33,
+ "CoverWindow": 65,
+ "WindowType_Mask": 255,
+ "MSWindowsFixedSizeDialogHint": 256,
+ "MSWindowsOwnDC": 512,
+ "BypassWindowManagerHint": 1024,
+ "X11BypassWindowManagerHint": 1024,
+ "FramelessWindowHint": 2048,
+ "WindowTitleHint": 4096,
+ "WindowSystemMenuHint": 8192,
+ "WindowMinimizeButtonHint": 16384,
+ "WindowMaximizeButtonHint": 32768,
+ "WindowMinMaxButtonsHint": 49152,
+ "WindowContextHelpButtonHint": 65536,
+ "WindowShadeButtonHint": 131072,
+ "WindowStaysOnTopHint": 262144,
+ "WindowTransparentForInput": 524288,
+ "WindowOverridesSystemGestures": 1048576,
+ "WindowDoesNotAcceptFocus": 2097152,
+ "MaximizeUsingFullscreenGeometryHint": 4194304,
+ "CustomizeWindowHint": 33554432,
+ "WindowStaysOnBottomHint": 67108864,
+ "WindowCloseButtonHint": 134217728,
+ "MacWindowToolBarButtonHint": 268435456,
+ "BypassGraphicsProxyWidget": 536870912,
+ "NoDropShadowWindowHint": 1073741824,
+ "WindowFullscreenButtonHint": -2147483648
+ }
+ }
+ Enum {
+ name: "WindowState"
+ values: {
+ "WindowNoState": 0,
+ "WindowMinimized": 1,
+ "WindowMaximized": 2,
+ "WindowFullScreen": 4,
+ "WindowActive": 8
+ }
+ }
+ Enum {
+ name: "WindowStates"
+ values: {
+ "WindowNoState": 0,
+ "WindowMinimized": 1,
+ "WindowMaximized": 2,
+ "WindowFullScreen": 4,
+ "WindowActive": 8
+ }
+ }
+ Enum {
+ name: "ApplicationState"
+ values: {
+ "ApplicationSuspended": 0,
+ "ApplicationHidden": 1,
+ "ApplicationInactive": 2,
+ "ApplicationActive": 4
+ }
+ }
+ Enum {
+ name: "ScreenOrientation"
+ values: {
+ "PrimaryOrientation": 0,
+ "PortraitOrientation": 1,
+ "LandscapeOrientation": 2,
+ "InvertedPortraitOrientation": 4,
+ "InvertedLandscapeOrientation": 8
+ }
+ }
+ Enum {
+ name: "ScreenOrientations"
+ values: {
+ "PrimaryOrientation": 0,
+ "PortraitOrientation": 1,
+ "LandscapeOrientation": 2,
+ "InvertedPortraitOrientation": 4,
+ "InvertedLandscapeOrientation": 8
+ }
+ }
+ Enum {
+ name: "WidgetAttribute"
+ values: {
+ "WA_Disabled": 0,
+ "WA_UnderMouse": 1,
+ "WA_MouseTracking": 2,
+ "WA_ContentsPropagated": 3,
+ "WA_OpaquePaintEvent": 4,
+ "WA_NoBackground": 4,
+ "WA_StaticContents": 5,
+ "WA_LaidOut": 7,
+ "WA_PaintOnScreen": 8,
+ "WA_NoSystemBackground": 9,
+ "WA_UpdatesDisabled": 10,
+ "WA_Mapped": 11,
+ "WA_MacNoClickThrough": 12,
+ "WA_InputMethodEnabled": 14,
+ "WA_WState_Visible": 15,
+ "WA_WState_Hidden": 16,
+ "WA_ForceDisabled": 32,
+ "WA_KeyCompression": 33,
+ "WA_PendingMoveEvent": 34,
+ "WA_PendingResizeEvent": 35,
+ "WA_SetPalette": 36,
+ "WA_SetFont": 37,
+ "WA_SetCursor": 38,
+ "WA_NoChildEventsFromChildren": 39,
+ "WA_WindowModified": 41,
+ "WA_Resized": 42,
+ "WA_Moved": 43,
+ "WA_PendingUpdate": 44,
+ "WA_InvalidSize": 45,
+ "WA_MacBrushedMetal": 46,
+ "WA_MacMetalStyle": 46,
+ "WA_CustomWhatsThis": 47,
+ "WA_LayoutOnEntireRect": 48,
+ "WA_OutsideWSRange": 49,
+ "WA_GrabbedShortcut": 50,
+ "WA_TransparentForMouseEvents": 51,
+ "WA_PaintUnclipped": 52,
+ "WA_SetWindowIcon": 53,
+ "WA_NoMouseReplay": 54,
+ "WA_DeleteOnClose": 55,
+ "WA_RightToLeft": 56,
+ "WA_SetLayoutDirection": 57,
+ "WA_NoChildEventsForParent": 58,
+ "WA_ForceUpdatesDisabled": 59,
+ "WA_WState_Created": 60,
+ "WA_WState_CompressKeys": 61,
+ "WA_WState_InPaintEvent": 62,
+ "WA_WState_Reparented": 63,
+ "WA_WState_ConfigPending": 64,
+ "WA_WState_Polished": 66,
+ "WA_WState_DND": 67,
+ "WA_WState_OwnSizePolicy": 68,
+ "WA_WState_ExplicitShowHide": 69,
+ "WA_ShowModal": 70,
+ "WA_MouseNoMask": 71,
+ "WA_GroupLeader": 72,
+ "WA_NoMousePropagation": 73,
+ "WA_Hover": 74,
+ "WA_InputMethodTransparent": 75,
+ "WA_QuitOnClose": 76,
+ "WA_KeyboardFocusChange": 77,
+ "WA_AcceptDrops": 78,
+ "WA_DropSiteRegistered": 79,
+ "WA_ForceAcceptDrops": 79,
+ "WA_WindowPropagation": 80,
+ "WA_NoX11EventCompression": 81,
+ "WA_TintedBackground": 82,
+ "WA_X11OpenGLOverlay": 83,
+ "WA_AlwaysShowToolTips": 84,
+ "WA_MacOpaqueSizeGrip": 85,
+ "WA_SetStyle": 86,
+ "WA_SetLocale": 87,
+ "WA_MacShowFocusRect": 88,
+ "WA_MacNormalSize": 89,
+ "WA_MacSmallSize": 90,
+ "WA_MacMiniSize": 91,
+ "WA_LayoutUsesWidgetRect": 92,
+ "WA_StyledBackground": 93,
+ "WA_MSWindowsUseDirect3D": 94,
+ "WA_CanHostQMdiSubWindowTitleBar": 95,
+ "WA_MacAlwaysShowToolWindow": 96,
+ "WA_StyleSheet": 97,
+ "WA_ShowWithoutActivating": 98,
+ "WA_X11BypassTransientForHint": 99,
+ "WA_NativeWindow": 100,
+ "WA_DontCreateNativeAncestors": 101,
+ "WA_MacVariableSize": 102,
+ "WA_DontShowOnScreen": 103,
+ "WA_X11NetWmWindowTypeDesktop": 104,
+ "WA_X11NetWmWindowTypeDock": 105,
+ "WA_X11NetWmWindowTypeToolBar": 106,
+ "WA_X11NetWmWindowTypeMenu": 107,
+ "WA_X11NetWmWindowTypeUtility": 108,
+ "WA_X11NetWmWindowTypeSplash": 109,
+ "WA_X11NetWmWindowTypeDialog": 110,
+ "WA_X11NetWmWindowTypeDropDownMenu": 111,
+ "WA_X11NetWmWindowTypePopupMenu": 112,
+ "WA_X11NetWmWindowTypeToolTip": 113,
+ "WA_X11NetWmWindowTypeNotification": 114,
+ "WA_X11NetWmWindowTypeCombo": 115,
+ "WA_X11NetWmWindowTypeDND": 116,
+ "WA_MacFrameworkScaled": 117,
+ "WA_SetWindowModality": 118,
+ "WA_WState_WindowOpacitySet": 119,
+ "WA_TranslucentBackground": 120,
+ "WA_AcceptTouchEvents": 121,
+ "WA_WState_AcceptedTouchBeginEvent": 122,
+ "WA_TouchPadAcceptSingleTouchEvents": 123,
+ "WA_X11DoNotAcceptFocus": 126,
+ "WA_MacNoShadow": 127,
+ "WA_AlwaysStackOnTop": 128,
+ "WA_TabletTracking": 129,
+ "WA_ContentsMarginsRespectsSafeArea": 130,
+ "WA_StyleSheetTarget": 131,
+ "WA_AttributeCount": 132
+ }
+ }
+ Enum {
+ name: "ApplicationAttribute"
+ values: {
+ "AA_ImmediateWidgetCreation": 0,
+ "AA_MSWindowsUseDirect3DByDefault": 1,
+ "AA_DontShowIconsInMenus": 2,
+ "AA_NativeWindows": 3,
+ "AA_DontCreateNativeWidgetSiblings": 4,
+ "AA_PluginApplication": 5,
+ "AA_MacPluginApplication": 5,
+ "AA_DontUseNativeMenuBar": 6,
+ "AA_MacDontSwapCtrlAndMeta": 7,
+ "AA_Use96Dpi": 8,
+ "AA_X11InitThreads": 10,
+ "AA_SynthesizeTouchForUnhandledMouseEvents": 11,
+ "AA_SynthesizeMouseForUnhandledTouchEvents": 12,
+ "AA_UseHighDpiPixmaps": 13,
+ "AA_ForceRasterWidgets": 14,
+ "AA_UseDesktopOpenGL": 15,
+ "AA_UseOpenGLES": 16,
+ "AA_UseSoftwareOpenGL": 17,
+ "AA_ShareOpenGLContexts": 18,
+ "AA_SetPalette": 19,
+ "AA_EnableHighDpiScaling": 20,
+ "AA_DisableHighDpiScaling": 21,
+ "AA_UseStyleSheetPropagationInWidgetStyles": 22,
+ "AA_DontUseNativeDialogs": 23,
+ "AA_SynthesizeMouseForUnhandledTabletEvents": 24,
+ "AA_CompressHighFrequencyEvents": 25,
+ "AA_DontCheckOpenGLContextThreadAffinity": 26,
+ "AA_DisableShaderDiskCache": 27,
+ "AA_DontShowShortcutsInContextMenus": 28,
+ "AA_CompressTabletEvents": 29,
+ "AA_DisableWindowContextHelpButton": 30,
+ "AA_DisableSessionManager": 31,
+ "AA_AttributeCount": 32
+ }
+ }
+ Enum {
+ name: "ImageConversionFlags"
+ values: {
+ "ColorMode_Mask": 3,
+ "AutoColor": 0,
+ "ColorOnly": 3,
+ "MonoOnly": 2,
+ "AlphaDither_Mask": 12,
+ "ThresholdAlphaDither": 0,
+ "OrderedAlphaDither": 4,
+ "DiffuseAlphaDither": 8,
+ "NoAlpha": 12,
+ "Dither_Mask": 48,
+ "DiffuseDither": 0,
+ "OrderedDither": 16,
+ "ThresholdDither": 32,
+ "DitherMode_Mask": 192,
+ "AutoDither": 0,
+ "PreferDither": 64,
+ "AvoidDither": 128,
+ "NoOpaqueDetection": 256,
+ "NoFormatConversion": 512
+ }
+ }
+ Enum {
+ name: "BGMode"
+ values: {
+ "TransparentMode": 0,
+ "OpaqueMode": 1
+ }
+ }
+ }
+ Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" }
+}
diff --git a/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml
new file mode 100644
index 0000000000..a478a587df
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml
@@ -0,0 +1,5 @@
+import QtQml
+QtObject {
+ property Component c: MyItem {}
+ property QtObject o: c.createObject()
+}
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index e586604862..75bf580947 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -75,6 +75,8 @@ private slots:
void contextObjectHierarchy();
void destroyContextProperty();
+ void numericContextProperty();
+
private:
QQmlEngine engine;
};
@@ -1004,6 +1006,14 @@ void tst_qqmlcontext::destroyContextProperty()
// TODO: Or are we?
}
+void tst_qqmlcontext::numericContextProperty()
+{
+ QQmlEngine engine;
+ auto context = engine.rootContext();
+ context->setContextProperty(QLatin1String("11"), 42);
+ QCOMPARE(context->contextProperty(QLatin1String("11")).toInt(), 42);
+}
+
QTEST_MAIN(tst_qqmlcontext)
#include "tst_qqmlcontext.moc"
diff --git a/tests/auto/qml/qqmlecmascript/CMakeLists.txt b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
index 70f010c082..bb1040db16 100644
--- a/tests/auto/qml/qqmlecmascript/CMakeLists.txt
+++ b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
@@ -21,6 +21,7 @@ qt_internal_add_test(tst_qqmlecmascript
Qt::Network
Qt::QmlPrivate
Qt::QuickTestUtilsPrivate
+ Qt::QuickPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml
new file mode 100644
index 0000000000..7f1b5b0317
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: root.invokableObject.method_QObject(Component)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml
new file mode 100644
index 0000000000..1904740b26
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml
@@ -0,0 +1,9 @@
+import QtQml
+import QtQml as NS
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: root.invokableObject.method_QObject(NS)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml
new file mode 100644
index 0000000000..d3a151efe3
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ objectName: "redRectangle"
+ id: redRectangle
+
+ property bool b: false
+ function toggle() { b = !b }
+ width: b ? 600 : 500
+
+ Item {
+ id: blueRectangle
+ objectName: "blueRectangle"
+ // width: b ? (100 + redRectangle.width / 2) : 25
+ width: b ? redRectangle.width : 25
+ }
+
+ property int blueRectangleWidth: blueRectangle.width
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
new file mode 100644
index 0000000000..490fec2dc8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property var val: undefined
+ property var observes: width
+ width: val
+ implicitWidth: 200
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
new file mode 100644
index 0000000000..2933d9b4d5
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Item {
+ height: undefined
+ implicitHeight: 30
+ property int steps: 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 500
+ }
+ }
+
+ onHeightChanged: ++steps
+
+ Component.onCompleted: {
+ height = Qt.binding(() => implicitHeight);
+ implicitHeight = 60;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index f47c7132a2..e976ec46a3 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -52,6 +52,8 @@
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmlvaluetypeproxybinding_p.h>
#include <QtCore/private/qproperty_p.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
@@ -312,6 +314,7 @@ private slots:
void bindingBoundFunctions();
void qpropertyAndQtBinding();
void qpropertyBindingReplacement();
+ void qpropertyBindingNoQPropertyCapture();
void deleteRootObjectInCreation();
void onDestruction();
void onDestructionViaGC();
@@ -393,6 +396,8 @@ private slots:
void qpropertyBindingHandlesUndefinedCorrectly();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly();
+ void qpropertyBindingRestoresObserverAfterReset();
+ void qpropertyBindingObserverCorrectlyLinkedAfterReset();
void hugeRegexpQuantifiers();
void singletonTypeWrapperLookup();
void getThisObject();
@@ -3071,6 +3076,26 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->actuals().count(), 1);
QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 13);
+ QCOMPARE(o->actuals().count(), 1);
+ QCOMPARE(o->actuals().at(0).value<QObject *>()->metaObject()->className(), "QQmlComponentAttached");
+ }
+
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs2.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments
+ }
+
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
@@ -7640,6 +7665,28 @@ void tst_qqmlecmascript::qpropertyBindingReplacement()
QCOMPARE(root->objectName(), u"overwritten"_qs);
}
+void tst_qqmlecmascript::qpropertyBindingNoQPropertyCapture()
+{
+
+ QQmlEngine engine;
+ QQmlComponent comp(&engine, testFileUrl("qpropertyBindingNoQPropertyCapture.qml"));
+ std::unique_ptr<QObject> root(comp.create());
+ QVERIFY2(root, qPrintable(comp.errorString()));
+ auto redRectangle = root.get();
+
+ QQmlProperty blueRectangleWidth(redRectangle, "blueRectangleWidth", &engine);
+
+ auto toggle = [&](){
+ QMetaObject::invokeMethod(root.get(), "toggle");
+ };
+
+ QCOMPARE(blueRectangleWidth.read().toInt(), 25);
+ toggle();
+ QCOMPARE(blueRectangleWidth.read().toInt(), 600);
+ toggle();
+ QCOMPARE(blueRectangleWidth.read().toInt(), 25);
+}
+
void tst_qqmlecmascript::deleteRootObjectInCreation()
{
QQmlEngine engine;
@@ -9195,6 +9242,32 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly()
QCOMPARE(root->property("value2").toInt(), 2);
}
+void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("height").toDouble(), 60.0);
+ QVERIFY(o->property("steps").toInt() > 3);
+}
+
+void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("width"), 200);
+ auto item = qobject_cast<QQuickItem *>(o.get());
+ auto itemPriv = QQuickItemPrivate::get(item);
+ QBindingStorage *storage = qGetBindingStorage(itemPriv);
+ QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) };
+ QCOMPARE(ptr.observerCount(), 1);
+}
+
void tst_qqmlecmascript::hugeRegexpQuantifiers()
{
QJSEngine engine;
diff --git a/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml
new file mode 100644
index 0000000000..83dbd566f3
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml
@@ -0,0 +1,6 @@
+import QtQml
+import 'file://./MyModuleName' as MyModuleName
+
+QtObject {
+ objectName: MyModuleName.Font.exampleVar
+}
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index 7b6c33bcc9..35b035ae9c 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -61,6 +61,7 @@ private slots:
void preferResourcePath();
void invalidFileImport_data();
void invalidFileImport();
+ void invalidImportUrl();
};
void tst_QQmlImport::cleanup()
@@ -136,6 +137,18 @@ void tst_QQmlImport::invalidFileImport()
"but not absolute paths or resource paths.").arg(import)));
}
+void tst_QQmlImport::invalidImportUrl()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("fileDotSlashImport.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY(component.isError());
+ QCOMPARE(
+ component.errorString(),
+ url.toString() + QLatin1String(
+ ":2 Cannot resolve URL for import \"file://./MyModuleName\"\n"));
+}
+
void tst_QQmlImport::testDesignerSupported()
{
QQuickView *window = new QQuickView();
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 92e24bc332..46be3766ec 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -125,6 +125,8 @@ void registerTypes()
qmlRegisterTypesAndRevisions<Large>("Test", 1);
qmlRegisterTypesAndRevisions<Foo>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<Counter>("Test", 1);
}
QVariant myCustomVariantTypeConverter(const QString &data)
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index e43424c9ca..f06c60f46d 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1866,4 +1866,48 @@ private:
void registerTypes();
+class CounterAttachedBaseType: public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ Q_PROPERTY (int value READ value NOTIFY valueChanged)
+
+public:
+ CounterAttachedBaseType(QObject *parent = nullptr) : QObject(parent) {}
+
+ int value() { return m_value; }
+ Q_SIGNAL void valueChanged();
+
+protected:
+ int m_value = 98;
+};
+
+
+class CounterAttachedType: public CounterAttachedBaseType
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ CounterAttachedType(QObject *parent = nullptr) : CounterAttachedBaseType(parent) {}
+
+ Q_INVOKABLE void increase() {
+ ++m_value;
+ Q_EMIT valueChanged();
+ }
+};
+
+class Counter : public QObject
+{
+ Q_OBJECT
+ QML_ATTACHED(CounterAttachedBaseType)
+ QML_ELEMENT
+
+public:
+ static CounterAttachedBaseType *qmlAttachedProperties(QObject *o)
+ {
+ return new CounterAttachedType(o);
+ }
+};
+
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 047df2f6d0..0d26772c6c 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -388,6 +388,8 @@ private slots:
void bindingAliasToComponentUrl();
void signalInlineComponentArg();
+ void callMethodOfAttachedDerived();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -6669,6 +6671,27 @@ void tst_qqmllanguage::signalInlineComponentArg()
}
}
+void tst_qqmllanguage::callMethodOfAttachedDerived()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(R"(
+ import QtQml
+ import Test
+
+ QtObject {
+ Component.onCompleted: Counter.increase()
+ property int v: Counter.value
+ }
+ )", QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("v").toInt(), 99);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml
new file mode 100644
index 0000000000..bfa832c1c8
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml
@@ -0,0 +1,50 @@
+import QtQml
+
+QtObject {
+ id: root
+ objectName: column.text
+
+ property Component c: Component {
+ id: comp
+ QtObject { }
+ }
+
+ property QtObject rectItem: null
+
+ property bool running: false
+
+ property Timer t: Timer {
+ id: column
+ interval: 200
+ running: root.running
+ repeat: true
+
+ property string text: {
+ let item = root.rectItem
+ let result = rectItem ? rectItem.objectName : "Create Object"
+ return result
+ }
+
+ onTriggered: {
+ let rectItem = root.rectItem
+
+ // If rectItem exists destory it.
+ if (rectItem) {
+ rectItem.destroy()
+ return
+ }
+
+ // Otherwise create a new object
+ let newRectItem = comp.createObject(column, {})
+
+
+ // Setting the objectName before setting root.rectItem seems to work.
+ // newRectItem.width = 1200
+ root.rectItem = newRectItem
+
+ // But setting the objectName after setting root.rectItem seems to
+ // cause the issue.
+ newRectItem.objectName = "1300"
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 7ea9b4183e..896150b33e 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -206,6 +206,8 @@ private slots:
void bindToNonQObjectTarget();
+ void invalidateQPropertyChangeTriggers();
+
private:
QQmlEngine engine;
};
@@ -2376,6 +2378,33 @@ void tst_qqmlproperty::bindToNonQObjectTarget()
QVERIFY(!o.isNull());
}
+void tst_qqmlproperty::invalidateQPropertyChangeTriggers()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("invalidateQPropertyChangeTriggers.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QStringList names;
+ QObject::connect(root.data(), &QObject::objectNameChanged, [&](const QString &name) {
+ if (names.length() == 10)
+ root->setProperty("running", false);
+ else
+ names.append(name);
+ });
+
+ root->setProperty("running", true);
+ QTRY_VERIFY(!root->property("running").toBool());
+
+ QCOMPARE(names, (QStringList {
+ u""_qs, u"1300"_qs, u"Create Object"_qs,
+ u""_qs, u"1300"_qs, u"Create Object"_qs,
+ u""_qs, u"1300"_qs, u"Create Object"_qs,
+ u""_qs
+ }));
+}
+
QTEST_MAIN(tst_qqmlproperty)
#include "tst_qqmlproperty.moc"
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
index 4bf1b58b9a..d2a270279b 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
@@ -15,4 +15,6 @@ android
android
[touchAndDragHandlerOnFlickable]
android
-
+# QTBUG-113226
+[pinchHandlerOnFlickable]
+*
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml
new file mode 100644
index 0000000000..a4867d502c
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+Flickable {
+ id: root
+ width: 800
+ height: 480
+ contentWidth: 1000
+ contentHeight: 600
+
+ Rectangle {
+ id: pinchable
+ objectName: "pinchable"
+ border.color: "black"
+ color: pinch.active ? "salmon" : "peachpuff"
+ x: 100
+ y: 100
+ width: 200
+ height: 200
+ radius: 80
+ PinchHandler {
+ id: pinch
+ }
+ PointHandler {
+ id: p1
+ target: Rectangle {
+ parent: pinchable
+ color: "green"
+ visible: p1.active
+ x: p1.point.position.x - width / 2
+ y: p1.point.position.y - height / 2
+ width: 9; height: width; radius: width / 2
+ }
+ }
+ PointHandler {
+ id: p0
+ target: Rectangle {
+ parent: pinchable
+ color: "red"
+ visible: p0.active
+ x: p0.point.position.x - width / 2
+ y: p0.point.position.y - height / 2
+ width: 9; height: width; radius: width / 2
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
index 5925965c55..0bf6b3fdcc 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
@@ -80,6 +80,7 @@ private slots:
void touchDragSliderAndFlickable();
void touchAndDragHandlerOnFlickable_data();
void touchAndDragHandlerOnFlickable();
+ void pinchHandlerOnFlickable();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -807,6 +808,77 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable()
touchSeq.release(1, p1, window).commit();
}
+void tst_FlickableInterop::pinchHandlerOnFlickable()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickable.qml")));
+ QQuickFlickable *flickable = qmlobject_cast<QQuickFlickable*>(window.rootObject());
+ QVERIFY(flickable);
+ QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPointerHandler*>();
+ QVERIFY(pinchHandler);
+ QQuickItem *pinchable = pinchHandler->target();
+ QVERIFY(pinchable);
+
+ QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted);
+ QSignalSpy grabChangedSpy(touchDevice, &QPointingDevice::grabChanged);
+
+ QObject *grabber = nullptr;
+ connect(touchDevice, &QPointingDevice::grabChanged,
+ [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) {
+ if (transition == QPointingDevice::GrabTransition::GrabExclusive)
+ grabber = g;
+ });
+
+ QPoint p0 = pinchable->mapToScene({50, 100}).toPoint();
+ QPoint p1 = pinchable->mapToScene({150, 100}).toPoint();
+ QTest::QTouchEventSequence touch = QTest::touchEvent(&window, touchDevice);
+
+ touch.press(0, p0, &window).press(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ int activeStep = -1;
+ int grabTransitionCount = 0;
+ // drag two fingers down: PinchHandler moves the item; Flickable doesn't grab, because there are 2 points
+ for (int i = 0; i < 4; ++i) {
+ p0 += QPoint(0, dragThreshold);
+ p1 += QPoint(0, dragThreshold);
+ touch.move(0, p0, &window).move(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ if (pinchHandler->active() && activeStep < 0) {
+ qCDebug(lcPointerTests) << "pinch began at step" << i;
+ activeStep = i;
+ QCOMPARE(grabber, pinchHandler);
+ grabTransitionCount = grabChangedSpy.count();
+ }
+ }
+ QVERIFY(pinchHandler->active());
+ QCOMPARE(grabChangedSpy.count(), grabTransitionCount);
+ QCOMPARE(grabber, pinchHandler);
+ qreal scale = pinchable->scale();
+ QCOMPARE(scale, 1);
+ qreal rot = pinchable->rotation();
+ QCOMPARE(rot, 0);
+ // start expanding and rotating
+ for (int i = 0; i < 4; ++i) {
+ p0 += QPoint(-5, 10);
+ p1 += QPoint(5, -10);
+ touch.move(0, p0, &window).move(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ QVERIFY(pinchHandler->active());
+ // PinchHandler keeps grab: no more transitions
+ QCOMPARE(grabChangedSpy.count(), grabTransitionCount);
+ QCOMPARE(grabber, pinchHandler);
+ QVERIFY(pinchable->scale() > scale);
+ scale = pinchable->scale();
+ QVERIFY(pinchable->rotation() < rot);
+ rot = pinchable->rotation();
+ }
+ touch.release(0, p0, &window).release(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ QTRY_COMPARE(pinchHandler->active(), false);
+ QCOMPARE(flickMoveSpy.count(), 0); // Flickable never moved
+}
+
QTEST_MAIN(tst_FlickableInterop)
#include "tst_flickableinterop.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml
new file mode 100644
index 0000000000..7bc3907c8c
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml
@@ -0,0 +1,36 @@
+import QtQuick
+import Qt.test 1.0
+
+Item {
+ width: 200
+ height: 200
+
+ Rectangle {
+ id: circle
+ y: 0
+ width: 100
+ height: width
+ radius: width/2
+ color: "#3e1"
+ clip: true
+
+ // Rectangle contains() is not affected by its 'radius' property
+ containmentMask: QtObject {
+ property alias radius: circle.radius
+ function contains(point: point) : bool {
+ return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2)
+ }
+ }
+ EventHandler {
+ objectName: "circle eventHandler"
+ }
+ Rectangle {
+ width: circle.width/2
+ height: width
+ color: "red"
+ EventHandler {
+ objectName: "eventHandler"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
index 9cab8f8ed6..51c94ea8c8 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
@@ -252,6 +252,7 @@ private slots:
void handlerInWindow();
void dynamicCreationInWindow();
void cppConstruction();
+ void clip();
protected:
bool eventFilter(QObject *, QEvent *event) override
@@ -734,6 +735,63 @@ void tst_PointerHandlers::cppConstruction()
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
}
+void tst_PointerHandlers::clip()
+{
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "clip.qml");
+ QQuickView * window = windowPtr.data();
+ QVERIFY(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler");
+ EventHandler *circleHandler = window->contentItem()->findChild<EventHandler*>("circle eventHandler");
+
+ QCOMPARE(handler->pressEventCount, 0);
+ QCOMPARE(circleHandler->pressEventCount, 0);
+ QCOMPARE(handler->releaseEventCount, 0);
+ QCOMPARE(circleHandler->releaseEventCount, 0);
+
+ const QPoint rectPt = QPoint(1, 1);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectPt);
+ QCOMPARE(handler->pressEventCount, 1);
+ QCOMPARE(circleHandler->pressEventCount, 0);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectPt);
+ QCOMPARE(handler->releaseEventCount, 1);
+ QCOMPARE(circleHandler->releaseEventCount, 0);
+
+
+ handler->pressEventCount = 0;
+ circleHandler->pressEventCount = 0;
+ handler->releaseEventCount = 0;
+ circleHandler->releaseEventCount = 0;
+
+ const QPoint rectAndCirclePt = QPoint(49 ,49);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
+ QCOMPARE(handler->pressEventCount, 1);
+ QCOMPARE(circleHandler->pressEventCount, 1);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
+ QCOMPARE(handler->releaseEventCount, 1);
+ QCOMPARE(circleHandler->releaseEventCount, 1);
+
+
+ handler->pressEventCount = 0;
+ circleHandler->pressEventCount = 0;
+ handler->releaseEventCount = 0;
+ circleHandler->releaseEventCount = 0;
+
+ const QPoint circlePt = QPoint(51 ,51);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, circlePt);
+ QCOMPARE(handler->pressEventCount, 0);
+ QCOMPARE(circleHandler->pressEventCount, 1);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, circlePt);
+ QCOMPARE(handler->releaseEventCount, 0);
+ QCOMPARE(circleHandler->releaseEventCount, 1);
+}
+
QTEST_MAIN(tst_PointerHandlers)
#include "tst_qquickpointerhandler.moc"
diff --git a/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml
new file mode 100644
index 0000000000..e31caa1905
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml
@@ -0,0 +1,29 @@
+import QtQuick
+
+Item {
+ id: root
+
+ property list<Translate> targets
+ property alias animTargets: animation.targets
+
+ Component {
+ id: trComponent
+ Translate {}
+ }
+
+ Component.onCompleted: {
+ const target = trComponent.createObject(this);
+ targets.push(target);
+ target.destroy();
+ // give event loop some time to actually stop the animation and destroy the target
+ Qt.callLater(animation.start);
+ }
+
+ NumberAnimation {
+ id: animation
+ targets: root.targets
+ property: "x"
+ running: false
+ to: 100
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 313f478820..30b934eeec 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -118,6 +118,7 @@ private slots:
void alwaysRunToEndInSequentialAnimationBug();
void cleanupWhenRenderThreadStops();
void infiniteLoopsWithoutFrom();
+ void targetsDeletedNotRemoved();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
for (int ii = 0; ii < 5; ++ii) { \
@@ -2060,6 +2061,26 @@ void tst_qquickanimations::infiniteLoopsWithoutFrom()
animation->stop();
}
+void tst_qquickanimations::targetsDeletedNotRemoved()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("targetsDeletedWithoutRemoval.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj.get(), qPrintable(component.errorString()));
+ {
+ QQmlListReference ref(obj.get(), "targets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QTRY_COMPARE(ref.at(0), nullptr);
+ }
+ {
+ QQmlListReference ref(obj.get(), "animTargets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QCOMPARE(ref.at(0), nullptr);
+ }
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquickgridview/data/qtbug92998.qml b/tests/auto/quick/qquickgridview/data/qtbug92998.qml
new file mode 100644
index 0000000000..d7db5fb843
--- /dev/null
+++ b/tests/auto/quick/qquickgridview/data/qtbug92998.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.12
+import QtQuick.Window 2.12
+
+Item {
+ width: 450
+ height: 650
+ GridView {
+ objectName: "gridview"
+ id: gridView
+ width: 450
+ height: 650
+ layoutDirection: Qt.RightToLeft
+ property int cells: calcCells(width)
+ cellWidth: width / cells
+ cellHeight: cellWidth
+
+ delegate: Component {
+ Item {
+ width: gridView.cellWidth
+ height: gridView.cellHeight
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ color: "green"
+ }
+ }
+ }
+ model: [
+ { number: "1" },
+ { number: "2" },
+ { number: "3" },
+ { number: "4" },
+ { number: "5" },
+ { number: "6" },
+ { number: "7" },
+ { number: "8" },
+ { number: "9" },
+ { number: "10" },
+ { number: "11" },
+ { number: "12" },
+ { number: "13" },
+ { number: "14" },
+ { number: "15" },
+ { number: "16" }];
+ function calcCells(w) {
+ var rw = 120;
+ var c = Math.max(1, Math.round(w / rw));
+ return c;
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 7e319fb28a..9532aa546c 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -215,6 +215,7 @@ private slots:
void positionViewAtBeginningAfterResizingCells();
void QTBUG_48870_fastModelUpdates();
void QTBUG_86255();
+ void resizeDynamicCellWidthRtL();
void keyNavigationEnabled();
void releaseItems();
@@ -6839,6 +6840,25 @@ void tst_QQuickGridView::QTBUG_86255()
QTRY_COMPARE(view->isFlicking(), false);
}
+void tst_QQuickGridView::resizeDynamicCellWidthRtL()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QTRY_VERIFY(window);
+ window->setSource(testFileUrl("qtbug92998.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "gridview");
+ QTRY_VERIFY(gridview != nullptr);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ gridview->setWidth(460);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ QTRY_COMPARE(gridview->contentX(), 0.f);
+ gridview->setWidth(360);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ QTRY_COMPARE(gridview->contentX(), 0.f);
+}
+
void tst_QQuickGridView::releaseItems()
{
QScopedPointer<QQuickView> view(createView());
diff --git a/tests/auto/quick/qquicklistview2/data/fetchMore.qml b/tests/auto/quick/qquicklistview2/data/fetchMore.qml
new file mode 100644
index 0000000000..4ce53e4d28
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/fetchMore.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import org.qtproject.Test
+
+ListView {
+ id: listView
+ width: 300
+ height: 150
+ flickDeceleration: 10000
+
+ model: FetchMoreModel
+ delegate: Text {
+ height: 50
+ text: model.display
+ }
+
+ Text {
+ anchors.right: parent.right
+ text: "count " + listView.count
+ color: listView.moving ? "red" : "blue"
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index 4c6f9c70e2..a7f64caaf0 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -66,6 +66,9 @@ private slots:
void isCurrentItem_DelegateModel();
void isCurrentItem_NoRegressionWithDelegateModelGroups();
+ void fetchMore_data();
+ void fetchMore();
+
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -260,29 +263,31 @@ void tst_QQuickListView2::tapDelegateDuringFlicking_data()
{
QTest::addColumn<QByteArray>("qmlFile");
QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior");
+ QTest::addColumn<bool>("expectCanceled");
QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << false;
QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << true;
QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << false;
QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << true;
QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << false;
QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << true;
QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << false;
QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << true;
}
void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
{
QFETCH(QByteArray, qmlFile);
QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
+ QFETCH(bool, expectCanceled);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData())));
@@ -295,7 +300,16 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
QVERIFY(listView->isFlicking()); // we want to test the case when it's still moving while we tap
// @y = 400 we pressed the 4th delegate; started flicking, and the press was canceled
QCOMPARE(listView->property("pressedDelegates").toList().first(), 4);
- QCOMPARE(listView->property("canceledDelegates").toList().first(), 4);
+ // At first glance one would expect MouseArea and Button would be consistent about this;
+ // but in fact, before ListView takes over the grab via filtering,
+ // Button.pressed transitions to false because QQuickAbstractButtonPrivate::handleMove
+ // sees that the touchpoint has strayed outside its bounds, but it does NOT emit the canceled signal
+ if (expectCanceled) {
+ const QVariantList canceledDelegates = listView->property("canceledDelegates").toList();
+ QCOMPARE(canceledDelegates.size(), 1);
+ QCOMPARE(canceledDelegates.first(), 4);
+ }
+ QCOMPARE(listView->property("releasedDelegates").toList().size(), 0);
// press a delegate during flicking (at y > 501 + 100, so likely delegate 6)
QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 100});
@@ -318,7 +332,7 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
QVERIFY(lastPressed > 5);
QCOMPARE(releasedDelegates.last(), lastPressed);
QCOMPARE(tappedDelegates.last(), lastPressed);
- QCOMPARE(canceledDelegates.count(), 1); // only the first press was canceled, not the second
+ QCOMPARE(canceledDelegates.size(), expectCanceled ? 1 : 0); // only the first press was canceled, not the second
}
void tst_QQuickListView2::flickDuringFlicking_data()
@@ -464,6 +478,81 @@ void tst_QQuickListView2::isCurrentItem_NoRegressionWithDelegateModelGroups()
QCOMPARE(item3->property("isCurrent").toBool(), false);
}
+class TestFetchMoreModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ QVariant data(const QModelIndex& index, int role) const override
+ {
+ if (role == Qt::DisplayRole)
+ return QString::number(index.row());
+ return {};
+ }
+
+ int columnCount(const QModelIndex&) const override { return 1; }
+
+ int rowCount(const QModelIndex& parent) const override
+ {
+ return parent.isValid() ? 0 : m_lines;
+ }
+
+ QModelIndex parent(const QModelIndex&) const override { return {}; }
+
+ bool canFetchMore(const QModelIndex &) const override { return true; }
+
+ void fetchMore(const QModelIndex & parent) override
+ {
+ if (Q_UNLIKELY(parent.isValid()))
+ return;
+ beginInsertRows(parent, m_lines, m_lines);
+ m_lines++;
+ endInsertRows();
+ }
+
+ int m_lines = 3;
+};
+
+void tst_QQuickListView2::fetchMore_data()
+{
+ QTest::addColumn<bool>("reuseItems");
+ QTest::addColumn<int>("cacheBuffer");
+
+ QTest::newRow("no reuseItems, default buffer") << false << -1;
+ QTest::newRow("reuseItems, default buffer") << true << -1;
+ QTest::newRow("no reuseItems, no buffer") << false << 0;
+ QTest::newRow("reuseItems, no buffer") << true << 0;
+ QTest::newRow("no reuseItems, buffer 100 px") << false << 100;
+ QTest::newRow("reuseItems, buffer 100 px") << true << 100;
+}
+
+void tst_QQuickListView2::fetchMore() // QTBUG-95107
+{
+ QFETCH(bool, reuseItems);
+ QFETCH(int, cacheBuffer);
+
+ TestFetchMoreModel model;
+ qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "FetchMoreModel", &model);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("fetchMore.qml")));
+ auto *listView = qobject_cast<QQuickListView*>(window.rootObject());
+ QVERIFY(listView);
+ listView->setReuseItems(reuseItems);
+ if (cacheBuffer >= 0)
+ listView->setCacheBuffer(cacheBuffer);
+
+ for (int i = 0; i < 3; ++i) {
+ const int rowCount = listView->count();
+ if (lcTests().isDebugEnabled()) QTest::qWait(1000);
+ listView->flick(0, -5000);
+ QTRY_VERIFY(!listView->isMoving());
+ qCDebug(lcTests) << "after flick: contentY" << listView->contentY()
+ << "rows" << rowCount << "->" << listView->count();
+ QVERIFY(listView->count() > rowCount);
+ QVERIFY(model.m_lines >= listView->count()); // fetchMore() was called
+ }
+}
+
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
index 32733613b3..fada2b3370 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
@@ -43,8 +43,8 @@ Rectangle {
TouchPoint { id: point2; objectName: "point2" }
]
- onCanceled: root.cancelCount = touchPoints.length
- onTouchUpdated: root.touchCount = touchPoints.length
+ onCanceled: (touchPoints) => root.cancelCount = touchPoints.length
+ onTouchUpdated: (touchPoints) => root.touchCount = touchPoints.length
Text {
text: "①"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index 299241b768..3e6a7fce16 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -577,6 +577,10 @@ void tst_QQuickMultiPointTouchArea::nested()
void tst_QQuickMultiPointTouchArea::inFlickable()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("On Android, QPointingDevicePrivate::queryPointById() sometimes fails to find a point 'in play' (QTBUG-117079)");
+#endif
+
QScopedPointer<QQuickView> window(createAndShowView("inFlickable.qml"));
QVERIFY(window->rootObject() != nullptr);
@@ -595,7 +599,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
QPoint p1(20,100);
QPoint p2(40,100);
- // moving one point vertically
+ // moving one point vertically: flickable gets the grab
QTest::touchEvent(window.data(), device).press(0, p1);
QQuickTouchUtils::flush(window.data());
@@ -619,7 +623,8 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
QTRY_VERIFY(!flickable->isMoving());
- // moving two points vertically
+ // moving two points vertically: MPTAs handle them, Flickable ignores multi-touch.
+ // The stray mouse events simulate OS-level synth-from-touch, and should not interfere.
p1 = QPoint(20,100);
QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2);
QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
@@ -641,11 +646,11 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY());
}
- QVERIFY(flickable->contentY() < 0);
- QCOMPARE(point11->pressed(), false);
- QCOMPARE(point12->pressed(), false);
- QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 2);
- QCOMPARE(window->rootObject()->property("touchCount").toInt(), 0);
+ QCOMPARE(flickable->contentY(), 0);
+ QCOMPARE(point11->pressed(), true);
+ QCOMPARE(point12->pressed(), true);
+ QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 0);
+ QCOMPARE(window->rootObject()->property("touchCount").toInt(), 2);
QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2);
QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
@@ -680,10 +685,8 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY());
}
- QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue);
QCOMPARE(flickable->contentY(), qreal(0));
QCOMPARE(point11->pressed(), true);
- QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue);
QCOMPARE(point12->pressed(), true);
QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2);
@@ -694,12 +697,16 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
// test that dragging out of a Flickable containing a MPTA doesn't harm Flickable's state.
void tst_QQuickMultiPointTouchArea::inFlickable2()
{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> window(createAndShowView("inFlickable2.qml"));
QVERIFY(window->rootObject() != nullptr);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable != nullptr);
+ QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta);
+
QQuickTouchPoint *point11 = window->rootObject()->findChild<QQuickTouchPoint*>("point1");
QVERIFY(point11);
@@ -712,25 +719,12 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
QQuickTouchUtils::flush(window.data());
QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(dragThreshold, 0);
+ QTest::touchEvent(window.data(), device).move(0, p1);
+ QQuickTouchUtils::flush(window.data());
+ QTest::mouseMove(window.data(), p1);
+ }
QVERIFY(!flickable->isMoving());
QVERIFY(point11->pressed());
@@ -743,27 +737,21 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
QTRY_VERIFY(!flickable->isMoving());
// Check that we can still move the Flickable
+ QSignalSpy gestureStartedSpy(mpta, &QQuickMultiPointTouchArea::gestureStarted);
p1 = QPoint(50,100);
QTest::touchEvent(window.data(), device).press(0, p1);
QQuickTouchUtils::flush(window.data());
QCOMPARE(point11->pressed(), true);
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(0, dragThreshold);
+ QTest::touchEvent(window.data(), device).move(0, p1);
+ QQuickTouchUtils::flush(window.data());
+ // QTBUG-113653: gestureStarted is emitted when touch delta exceeds drag threshold,
+ // regardless of the filtering Flickable parent
+ QCOMPARE(gestureStartedSpy.size(), i > 0 ? 1 : 0);
+ }
QVERIFY(flickable->contentY() < 0);
QVERIFY(flickable->isMoving());
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 773ccdac99..992709b865 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -445,6 +445,9 @@ public:
, touchDevice(QTest::createTouchDevice())
, touchDeviceWithVelocity(QTest::createTouchDevice(QInputDevice::DeviceType::TouchScreen,
QInputDevice::Capability::Position | QPointingDevice::Capability::Velocity))
+ , tabletStylusDevice(QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType::Stylus,
+ QPointingDevice::PointerType::Pen,
+ QPointingDeviceUniqueId::fromNumericId(1234567890)))
{
QQuickWindow::setDefaultAlphaBuffer(true);
}
@@ -566,8 +569,9 @@ private slots:
void visibleVsVisibility();
private:
- QPointingDevice *touchDevice;
- QPointingDevice *touchDeviceWithVelocity;
+ QPointingDevice *touchDevice; // TODO make const after fixing QTBUG-107864
+ const QPointingDevice *touchDeviceWithVelocity;
+ const QPointingDevice *tabletStylusDevice;
};
#if QT_CONFIG(opengl)
@@ -3637,16 +3641,16 @@ void tst_qquickwindow::cleanupGrabsOnRelease()
void tst_qquickwindow::subclassWithPointerEventVirtualOverrides_data()
{
- QTest::addColumn<QPointingDevice::DeviceType>("deviceType");
+ QTest::addColumn<const QPointingDevice *>("device");
- QTest::newRow("mouse click") << QPointingDevice::DeviceType::Mouse;
- QTest::newRow("touch tap") << QPointingDevice::DeviceType::TouchScreen;
- QTest::newRow("stylus tap") << QPointingDevice::DeviceType::Stylus;
+ QTest::newRow("mouse click") << QPointingDevice::primaryPointingDevice();
+ QTest::newRow("touch tap") << static_cast<const QPointingDevice*>(touchDevice); // TODO QTBUG-107864
+ QTest::newRow("stylus tap") << tabletStylusDevice;
}
void tst_qquickwindow::subclassWithPointerEventVirtualOverrides() // QTBUG-97859
{
- QFETCH(QPointingDevice::DeviceType, deviceType);
+ QFETCH(const QPointingDevice *, device);
PointerRecordingWindow window;
window.resize(250, 250);
@@ -3654,32 +3658,23 @@ void tst_qquickwindow::subclassWithPointerEventVirtualOverrides() // QTBUG-97859
window.setTitle(QTest::currentTestFunction());
window.show();
QVERIFY(QTest::qWaitForWindowActive(&window));
- const qint64 stylusId = 1234567890;
-
const QPoint pos(120, 120);
- switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {
+
+ QQuickTest::pointerPress(device, &window, 0, pos);
+ QQuickTest::pointerRelease(device, &window, 0, pos);
+
+ switch (device->type()) {
case QPointingDevice::DeviceType::Mouse:
- QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, pos);
- QTRY_COMPARE(window.m_mouseEvents.count(), 3); // separate move before press
- QCOMPARE(window.m_events.count(), 3);
+ QTRY_COMPARE(window.m_mouseEvents.size(), 3); // separate move before press
+ QCOMPARE(window.m_events.size(), 3);
break;
case QPointingDevice::DeviceType::TouchScreen:
- QTest::touchEvent(&window, touchDevice).press(0, pos, &window);
- QTest::touchEvent(&window, touchDevice).release(0, pos, &window);
- QTRY_COMPARE(window.m_touchEvents.count(), 2);
- QCOMPARE(window.m_events.count(), 2);
+ QTRY_COMPARE(window.m_touchEvents.size(), 2);
+ QCOMPARE(window.m_events.size(), 2);
break;
case QPointingDevice::DeviceType::Stylus:
- // press (pressure is 0.8)
- QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos),
- int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
- Qt::LeftButton, 0.8, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
- // release (pressure is 0)
- QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos),
- int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
- Qt::NoButton, 0, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
- QTRY_COMPARE(window.m_tabletEvents.count(), 2);
- QVERIFY(window.m_events.count() >= window.m_tabletEvents.count()); // tablet + synth-mouse events
+ QTRY_COMPARE(window.m_tabletEvents.size(), 2);
+ QVERIFY(window.m_events.size() >= window.m_tabletEvents.size()); // tablet + synth-mouse events
break;
default:
break;
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
index 66dde3e26c..c80ceddcc3 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -738,7 +738,7 @@ void tst_TouchMouse::touchButtonOnFlickable()
QTRY_COMPARE(eventItem2->touchUngrabCount, 1);
qCDebug(lcTests) << "expected delivered events: press(touch) move(touch)" << eventItem2->eventList;
- QCOMPARE(eventItem2->eventList.size(), 2);
+ QCOMPARE(eventItem2->eventList.size(), 3);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QCOMPARE(grabMonitor.exclusiveGrabber, flickable);
// both EventItem and Flickable handled the actual touch, so synth-mouse doesn't happen
diff --git a/tests/auto/quickcontrols2/CMakeLists.txt b/tests/auto/quickcontrols2/CMakeLists.txt
index 903732bf79..4004fbecdf 100644
--- a/tests/auto/quickcontrols2/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(controls)
endif()
add_subdirectory(cursor)
add_subdirectory(customization)
+add_subdirectory(deferred)
add_subdirectory(designer)
if(NOT ANDROID) # QTBUG-100258
add_subdirectory(focus)
diff --git a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml
index 254c876f08..61dbecd660 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml
@@ -691,7 +691,10 @@ TestCase {
id: translucentPages
SwipeView {
spacing: 10
- padding: 10
+ leftPadding: 10
+ topPadding: 10
+ rightPadding: 10
+ bottomPadding: 10
Text { text: "page 0" }
Text { text: "page 1"; font.pointSize: 16 }
Text { text: "page 2"; font.pointSize: 24 }
@@ -702,18 +705,33 @@ TestCase {
function test_initialPositions() { // QTBUG-102487
const control = createTemporaryObject(translucentPages, testCase, {width: 320, height: 200})
verify(control)
+ compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.spacing, 10)
+
compare(control.orientation, Qt.Horizontal)
- for (var i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
const page = control.itemAt(i)
- // control.contentItem.width + control.spacing == 310; except Imagine style has contentItem.width == 320
- compare(page.x, i * 310)
+ compare(page.x, i * (control.contentItem.width + control.spacing))
compare(page.y, 0)
+ compare(page.width, control.contentItem.width)
+ compare(page.height, control.contentItem.height)
}
control.orientation = Qt.Vertical
- for (var i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
const page = control.itemAt(i)
compare(page.y, i * (control.contentItem.height + control.spacing))
compare(page.x, 0)
+ compare(page.width, control.contentItem.width)
+ compare(page.height, control.contentItem.height)
}
+
+ // QTBUG-115468: add a page after startup and check that that works too.
+ control.orientation = Qt.Horizontal
+ let page4 = page.createObject(control, { text: "page 4", "font.pointSize": 40 })
+ control.insertItem(control.count, page4)
+ compare(page4.x, (control.count - 1) * 310)
+ compare(page4.y, 0)
+ compare(page4.width, control.contentItem.width)
+ compare(page4.height, control.contentItem.height)
}
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
index fff6fa7933..0fc2e87b8a 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
@@ -1278,4 +1278,46 @@ TestCase {
tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: false});
compare(tumbler.currentIndex, 4);
}
+
+ // QTBUG-109995
+ Component {
+ id: flickTumbler
+ Flickable {
+ width: 50
+ height: 200
+ interactive: true
+ contentHeight: 400
+ property alias tumblerItem: noWrapTumbler
+ Tumbler {
+ id: noWrapTumbler
+ anchors.fill: parent
+ model: 20
+ wrap: false
+ }
+ }
+ }
+
+ function test_flick() {
+ let control = createTemporaryObject(flickTumbler, testCase)
+ verify(control)
+
+ let tumbler = control.tumblerItem
+ compare(tumbler.count, 20)
+ compare(tumbler.wrap, false)
+
+ let touch = touchEvent(tumbler)
+ let tumblerView = findView(tumbler)
+ let delegateHeight = tumblerView.children[0].children[0].height
+
+ // Move delegates through touch operation and check the current index
+ touch.press(0, tumblerView, control.width / 2, control.height / 2).commit()
+ // Move slowly, otherwise its considered as flick which cause current index
+ // to be varied according to its velocity
+ var scrollOffset = control.height / 2
+ for (; scrollOffset > delegateHeight / 2; scrollOffset-=5) {
+ touch.move(0, tumblerView, control.width / 2, scrollOffset).commit()
+ }
+ touch.release(0, tumblerView, control.width / 2, scrollOffset).commit()
+ tryCompare(tumblerView, "currentIndex", 2)
+ }
}
diff --git a/tests/auto/quickcontrols2/deferred/CMakeLists.txt b/tests/auto/quickcontrols2/deferred/CMakeLists.txt
new file mode 100644
index 0000000000..900732ef4e
--- /dev/null
+++ b/tests/auto/quickcontrols2/deferred/CMakeLists.txt
@@ -0,0 +1,25 @@
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickdeferred
+ SOURCES
+ tst_qquickdeferred.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::QmlPrivate
+ Qt::QuickTemplates2Private
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qquickdeferred CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\":/data\\\"
+)
+
+qt_internal_extend_target(tst_qquickdeferred CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
+)
diff --git a/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml b/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml
new file mode 100644
index 0000000000..9f6a174cf4
--- /dev/null
+++ b/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml
@@ -0,0 +1,15 @@
+import test
+import QtQuick
+
+Item {
+ id: root
+ property bool toggle: true
+ property int counter: 0
+
+ x: toggle ? 100 : tester.objectProperty.x
+
+ DeferredPropertyTester {
+ id: tester
+ objectProperty: Item { x: { console.log("hi"); return 300; } }
+ }
+}
diff --git a/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp b/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp
new file mode 100644
index 0000000000..4d8096aa46
--- /dev/null
+++ b/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtTest/qtest.h>
+#include <QQmlEngine>
+#include <QtQuick/qquickitem.h>
+#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+
+class DeferredPropertyTester : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickItem *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged)
+ Q_CLASSINFO("DeferredPropertyNames", "objectProperty")
+
+public:
+ DeferredPropertyTester() {}
+
+
+ QQuickItem *objectProperty() {
+
+ if (!m_object.wasExecuted()) {
+ quickBeginDeferred(this, "objectProperty", m_object);
+ quickCompleteDeferred(this, "objectProperty", m_object);
+ }
+
+ return m_object;
+ }
+ void setObjectProperty(QQuickItem *obj) {
+ if (m_object == obj)
+ return;
+ m_object = obj;
+ if (!m_object.isExecuting()) // first read
+ emit objectChanged();
+ }
+
+signals:
+ void objectChanged();
+
+private:
+ QQuickDeferredPointer<QQuickItem> m_object = nullptr;
+};
+
+class tst_qquickdeferred : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qquickdeferred() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
+private slots:
+ void noSpuriousBinding();
+};
+
+
+
+void tst_qquickdeferred::noSpuriousBinding() {
+ qmlRegisterType<DeferredPropertyTester>("test", 1, 0, "DeferredPropertyTester");
+
+ QQmlEngine engine;
+ QQmlComponent comp(&engine, testFileUrl("noSpuriousBinding.qml"));
+ std::unique_ptr<QObject> root(comp.create());
+ QVERIFY2(root, qPrintable(comp.errorString()));
+ root->setProperty("toggle", false);
+}
+
+QTEST_MAIN(tst_qquickdeferred)
+
+#include "tst_qquickdeferred.moc"
diff --git a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
index 9e37541137..0176c2db35 100644
--- a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
@@ -84,7 +84,9 @@ private slots:
void menuSeparator();
void repeater();
void order();
+#if QT_CONFIG(cursor)
void popup();
+#endif
void actions();
#if QT_CONFIG(shortcut)
void actionShortcuts();
@@ -819,8 +821,15 @@ void tst_QQuickMenu::order()
}
}
+#if QT_CONFIG(cursor)
void tst_QQuickMenu::popup()
{
+#if defined(Q_OS_ANDROID)
+ QSKIP("Setting cursor position is not supported on Android");
+#endif
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland")))
+ QSKIP("Setting cursor position is not supported on Wayland");
+
// Try moving the cursor from the current position
// Skip if it fails since the test relies on moving the cursor
const QPoint point = QCursor::pos() + QPoint(1, 1);
@@ -851,8 +860,6 @@ void tst_QQuickMenu::popup()
QQuickItem *button = window->property("button").value<QQuickItem *>();
QVERIFY(button);
- // Android does not support settings cursor position
-#if QT_CONFIG(cursor) && !defined(Q_OS_ANDROID)
QPoint oldCursorPos = QCursor::pos();
QPoint cursorPos = window->mapToGlobal(QPoint(11, 22));
QCursor::setPos(cursorPos);
@@ -981,8 +988,8 @@ void tst_QQuickMenu::popup()
QCursor::setPos(oldCursorPos);
QTRY_COMPARE(QCursor::pos(), oldCursorPos);
-#endif
}
+#endif // QT_CONFIG(cursor)
void tst_QQuickMenu::actions()
{
diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
index d2cfda9375..a587c085a4 100644
--- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
@@ -44,6 +44,7 @@
#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquickpalette_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#include <QtQuickTemplates2/private/qquickcombobox_p.h>
@@ -123,6 +124,7 @@ private slots:
private:
static bool hasWindowActivation();
+ QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
tst_QQuickPopup::tst_QQuickPopup()
@@ -246,7 +248,6 @@ void tst_QQuickPopup::overlay()
QFETCH(bool, modal);
QFETCH(bool, dim);
- QScopedPointer<QPointingDevice> device(QTest::createTouchDevice());
QQuickControlsApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
@@ -337,13 +338,13 @@ void tst_QQuickPopup::overlay()
QVERIFY(overlay->isVisible());
- QTest::touchEvent(window, device.data()).press(0, QPoint(1, 1));
+ QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
- QTest::touchEvent(window, device.data()).release(0, QPoint(1, 1));
+ QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
@@ -358,28 +359,28 @@ void tst_QQuickPopup::overlay()
QVERIFY(overlay->isVisible());
QVERIFY(!button->isPressed());
- QTest::touchEvent(window, device.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint());
+ QTest::touchEvent(window, touchScreen.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
- QTest::touchEvent(window, device.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
+ QTest::touchEvent(window, touchScreen.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
- QTest::touchEvent(window, device.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1);
+ QTest::touchEvent(window, touchScreen.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
- QTest::touchEvent(window, device.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
+ QTest::touchEvent(window, touchScreen.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
@@ -484,21 +485,21 @@ void tst_QQuickPopup::closePolicy_data()
QTest::addColumn<QString>("source");
QTest::addColumn<QQuickPopup::ClosePolicy>("closePolicy");
- QTest::newRow("Window:NoAutoClose") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
- QTest::newRow("Window:CloseOnPressOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
- QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
- QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
- QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
- QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
- QTest::newRow("Window:CloseOnEscape") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
-
- QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
- QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
- QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
- QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
- QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
- QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
- QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
+ QTest::newRow("Window:NoAutoClose") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
+ QTest::newRow("Window:CloseOnPressOutside") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
+ QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
+ QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
+ QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+ QTest::newRow("Window:CloseOnEscape") << "window.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
+
+ QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
+ QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
+ QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
+ QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+ QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml" << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
}
void tst_QQuickPopup::closePolicy()
@@ -534,56 +535,64 @@ void tst_QQuickPopup::closePolicy()
// wait for dimmer
QTest::qWait(50);
- // press outside popup and its parent
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
- QTRY_VERIFY(!popup->isVisible());
- else
- QVERIFY(popup->isOpened());
+ for (int i = 0; i < 2; ++i) {
+ // press outside popup and its parent
+ QTest::touchEvent(window, touchScreen.data()).press(0, {1, 1});
+ QQuickTouchUtils::flush(window);
+ if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
+ QTRY_VERIFY(!popup->isVisible());
+ else
+ QVERIFY(popup->isOpened());
- popup->open();
- QVERIFY(popup->isVisible());
- QTRY_VERIFY(popup->isOpened());
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
- // release outside popup and its parent
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
- QTRY_VERIFY(!popup->isVisible());
- else
- QVERIFY(popup->isOpened());
+ // release outside popup and its parent
+ QTest::touchEvent(window, touchScreen.data()).release(0, {1, 1});
+ QQuickTouchUtils::flush(window);
+ if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
+ QTRY_VERIFY(!popup->isVisible());
+ else
+ QVERIFY(popup->isOpened());
- popup->open();
- QVERIFY(popup->isVisible());
- QTRY_VERIFY(popup->isOpened());
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
- // press outside popup but inside its parent
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
- QTRY_VERIFY(!popup->isVisible());
- else
- QVERIFY(popup->isOpened());
+ // press outside popup but inside its parent
+ QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(button->x() + 1, button->y() + 1));
+ QQuickTouchUtils::flush(window);
+ if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
+ QTRY_VERIFY(!popup->isVisible());
+ else
+ QVERIFY(popup->isOpened());
- popup->open();
- QVERIFY(popup->isVisible());
- QTRY_VERIFY(popup->isOpened());
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
- // release outside popup but inside its parent
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
- QTRY_VERIFY(!popup->isVisible());
- else
- QVERIFY(popup->isOpened());
+ // release outside popup but inside its parent
+ QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(button->x() + 1, button->y() + 1));
+ QQuickTouchUtils::flush(window);
+ if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
+ QTRY_VERIFY(!popup->isVisible());
+ else
+ QVERIFY(popup->isOpened());
- popup->open();
- QVERIFY(popup->isVisible());
- QTRY_VERIFY(popup->isOpened());
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
- // press inside and release outside
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + popup->x() + 1,
- button->y() + popup->y() + 1));
- QVERIFY(popup->isOpened());
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- QVERIFY(popup->isOpened());
+ // press inside and release outside
+ QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(button->x() + popup->x() + 1,
+ button->y() + popup->y() + 1));
+ QQuickTouchUtils::flush(window);
+ QVERIFY(popup->isOpened());
+ QTest::touchEvent(window, touchScreen.data()).release(0, {1, 1});
+ QQuickTouchUtils::flush(window);
+ QVERIFY(popup->isOpened());
+ }
// escape
QTest::keyClick(window, Qt::Key_Escape);