aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2024-01-22 22:38:27 +0200
committerTarja Sundqvist <tarja.sundqvist@qt.io>2024-02-20 10:05:13 +0000
commit302ab20d46280e11042f3896460c55d8b8146e41 (patch)
tree3cef87d6ca24d2e37a3dc8c4af4506e510903a78
parent02277e3753613d9e19bbb36367c7d2b1d13d7545 (diff)
parenta7df6331b29e44ed364fcd7297c4e1bc6ce2167c (diff)
Merge remote-tracking branch 'origin/tqtc/lts-6.2.8' into tqtc/lts-6.2-opensource
Conflicts solved in a file: dependencies.yaml Change-Id: Ib4083daa41a689b937d2aeb522e93e3aab0be1c4
-rw-r--r--.cmake.conf2
-rw-r--r--.qmake.conf2
-rw-r--r--dependencies.yaml8
-rw-r--r--examples/quick/pointerhandlers/components/FlashAnimation.qml8
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpgbin0 -> 104984 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webpbin0 -> 48514 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpgbin0 -> 81973 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webpbin0 -> 424394 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webpbin0 -> 185554 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webpbin0 -> 620084 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webpbin0 -> 999018 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webpbin0 -> 286790 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webpbin0 -> 155468 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc184
-rw-r--r--examples/quick/pointerhandlers/map.qml2
-rw-r--r--examples/quick/scenegraph/graph/noisynode.cpp1
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/labs/animation/qquickboundaryrule.cpp4
-rw-r--r--src/particles/qquickimageparticle.cpp7
-rw-r--r--src/particles/qquickimageparticle_p.h2
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp1
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc25
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc69
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc6
-rw-r--r--src/qml/jit/qv4baselinejit.cpp4
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h2
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp11
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp6
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp10
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/qml/qqml.cpp7
-rw-r--r--src/qml/qml/qqmlbinding.cpp56
-rw-r--r--src/qml/qml/qqmlengine.cpp15
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp23
-rw-r--r--src/qml/qml/qqmlimport.cpp60
-rw-r--r--src/qml/qml/qqmlmetatype.cpp4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp24
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp4
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp51
-rw-r--r--src/qml/types/qqmlconnections.cpp14
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h2
-rw-r--r--src/qmldom/qqmldomreformatter.cpp62
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp137
-rw-r--r--src/qmlmodels/qqmllistmodel_p_p.h45
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.cpp28
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webpbin0 -> 53154 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webpbin0 -> 27426 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webpbin0 -> 51008 bytes
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandler.qml5
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml68
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml83
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml101
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml83
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml98
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml98
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml98
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc5
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc27
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp10
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp25
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp9
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp12
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp2
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp18
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp126
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp2
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp105
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp2
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp6
-rw-r--r--src/quick/items/qquickflickable.cpp8
-rw-r--r--src/quick/items/qquickflickable_p_p.h1
-rw-r--r--src/quick/items/qquickflipable.cpp54
-rw-r--r--src/quick/items/qquickimage.cpp2
-rw-r--r--src/quick/items/qquickimagebase.cpp4
-rw-r--r--src/quick/items/qquickitem.cpp8
-rw-r--r--src/quick/items/qquickitemsmodule.cpp134
-rw-r--r--src/quick/items/qquickitemview.cpp3
-rw-r--r--src/quick/items/qquicklistview.cpp3
-rw-r--r--src/quick/items/qquickloader.cpp2
-rw-r--r--src/quick/items/qquickrectangle.cpp9
-rw-r--r--src/quick/items/qquickrendercontrol.cpp4
-rw-r--r--src/quick/items/qquickshadereffect.cpp2
-rw-r--r--src/quick/items/qquickspriteengine.cpp1
-rw-r--r--src/quick/items/qquicktableview.cpp35
-rw-r--r--src/quick/items/qquicktableview_p_p.h9
-rw-r--r--src/quick/items/qquicktext.cpp1
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp5
-rw-r--r--src/quick/items/qquickwindow.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp6
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp4
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp4
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp12
-rw-r--r--src/quick/util/qquickanimatorjob.cpp5
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp7
-rw-r--r--src/quick/util/qquicktransition.cpp5
-rw-r--r--src/quickcontrols2/doc/snippets/qtquickcontrols2-swipedelegate.qml3
-rw-r--r--src/quickcontrols2/doc/src/includes/container-currentindex.qdocinc9
-rw-r--r--src/quickcontrols2/doc/src/includes/qquickimaginestyle.qdocinc2
-rw-r--r--src/quickcontrols2/imagine/impl/qquickninepatchimage.cpp44
-rw-r--r--src/quickcontrolstestutils/qtest_quickcontrols_p.h4
-rw-r--r--src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm2
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp4
-rw-r--r--src/quickshapes/qquickshapesoftwarerenderer.cpp6
-rw-r--r--src/quicktemplates2/qquickdrawer.cpp16
-rw-r--r--src/quicktemplates2/qquickheaderview.cpp2
-rw-r--r--src/quicktemplates2/qquickpopup.cpp2
-rw-r--r--src/quicktemplates2/qquickpopuppositioner.cpp8
-rw-r--r--src/quicktemplates2/qquicksplitview.cpp77
-rw-r--r--src/quicktemplates2/qquickswipeview.cpp2
-rw-r--r--src/quicktemplates2/qquicktabbar.cpp2
-rw-r--r--src/quickwidgets/qquickwidget.cpp19
-rw-r--r--tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp80
-rw-r--r--tests/auto/qml/debugger/qv4debugger/CMakeLists.txt1
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp4
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp108
-rw-r--r--tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml37
-rw-r--r--tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml45
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp3
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp7
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h12
-rw-r--r--tests/auto/qml/qqmlconnections/data/invalidTarget.qml31
-rw-r--r--tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml25
-rw-r--r--tests/auto/qml/qqmlecmascript/data/readOnlyBindable.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp1
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h45
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp95
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml13
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir5
-rw-r--r--tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp19
-rw-r--r--tests/auto/qml/qqmllanguage/data/MyRectangle.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.19.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp10
-rw-r--r--tests/auto/qml/qqmllistmodel/data/protectQObjectFromGC.qml21
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp22
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp20
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_arc.qml22
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml9
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_composite.qml7
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml8
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_image.qml25
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_line.qml9
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_path.qml16
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml5
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml11
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_state.qml16
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml5
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_text.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_transform.qml6
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp8
-rw-r--r--tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml32
-rw-r--r--tests/auto/quick/qquickflipable/tst_qquickflipable.cpp28
-rw-r--r--tests/auto/quick/qquicklistview2/data/qtbug86744.qml25
-rw-r--r--tests/auto/quick/qquicklistview2/data/qtbug98315.qml98
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp41
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp21
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_splitview.qml22
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml13
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp41
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/mirroredCombobox.qml26
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/rotatedCombobox.qml26
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp151
-rw-r--r--tests/auto/quickwidgets/qquickwidget/CMakeLists.txt1
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/button.qml27
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/mouse.qml2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp98
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf1
178 files changed, 3436 insertions, 473 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 07d90b8adc..e62fae5ef9 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,2 @@
-set(QT_REPO_MODULE_VERSION "6.2.7")
+set(QT_REPO_MODULE_VERSION "6.2.8")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "")
diff --git a/.qmake.conf b/.qmake.conf
index 1a33c704d2..3745dab334 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.7
+MODULE_VERSION = 6.2.8
diff --git a/dependencies.yaml b/dependencies.yaml
index 481d9f48ba..d3f1ef8260 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,13 +1,13 @@
dependencies:
../tqtc-qtbase:
- ref: 694575a59b5370afc494fbf700eee8db1d1ec091
+ ref: 67934c103800bae50c2ec1977758d40fa8e4e507
required: true
../tqtc-qtimageformats:
- ref: 607e0da419b604b971ce01f51bc94920c61191f7
+ ref: c5e59f6ab90be0f9e31488c676b2e0ebef7a99a0
required: false
../tqtc-qtshadertools:
- ref: 5de9eb91f047e8001ac24f0bdf5ffc72adb37236
+ ref: f9868e2b39e539b1bb6917006b789ab7b5fec5eb
required: false
../tqtc-qtsvg:
- ref: 42fb62219b3d41481f4065d222f227614f41fbe8
+ ref: 40235be466095382836e9b544dc921077c47b417
required: false
diff --git a/examples/quick/pointerhandlers/components/FlashAnimation.qml b/examples/quick/pointerhandlers/components/FlashAnimation.qml
index b0dd7836ae..ac41b2c1d6 100644
--- a/examples/quick/pointerhandlers/components/FlashAnimation.qml
+++ b/examples/quick/pointerhandlers/components/FlashAnimation.qml
@@ -53,15 +53,9 @@ import QtQuick
SequentialAnimation {
id: tapFlash
running: false
+ loops: 3
PropertyAction { value: false }
PauseAnimation { duration: 100 }
PropertyAction { value: true }
PauseAnimation { duration: 100 }
- PropertyAction { value: false }
- PauseAnimation { duration: 100 }
- PropertyAction { value: true }
- PauseAnimation { duration: 100 }
- PropertyAction { value: false }
- PauseAnimation { duration: 100 }
- PropertyAction { value: true }
}
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg
new file mode 100644
index 0000000000..5149665a07
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp
new file mode 100644
index 0000000000..4b8794971e
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg
new file mode 100644
index 0000000000..c9d167b1e7
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp
new file mode 100644
index 0000000000..b96b0cf90a
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp
new file mode 100644
index 0000000000..f263458d12
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp
new file mode 100644
index 0000000000..1d13805c64
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp
new file mode 100644
index 0000000000..89f61251ab
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp
new file mode 100644
index 0000000000..1a0ac33d0c
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp
new file mode 100644
index 0000000000..162e97ea15
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc b/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc
new file mode 100644
index 0000000000..7d19d47219
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \example pointerhandlers
+ \title Pointer Handlers Example
+ \ingroup qtquickexamples
+ \meta tags {quick, input handlers}
+ \brief Shows how to use \l {Qt Quick Input Handlers} in interactive components.
+
+ \l {Qt Quick Controls} contains pre-built components that are sufficient
+ for many kinds of user interfaces; but you may sometimes find a need to
+ write such components "from scratch". In that case, you will need to make
+ substantial use of Pointer Handlers. This is a collection of small examples
+ that show how to implement some common and less-common interaction patterns.
+
+ If you build the example as an executable, you can choose pages from a
+ top-level ListView. The \c TouchpointFeedbackSprite and \c MouseFeedbackSprite
+ components show how to use PointHandler to provide feedback about what the
+ user is doing with the pointing devices: these components are always
+ visible throughout the example, and do not interfere with interaction
+ anywhere else. These components are handy for reuse if you ever need to do
+ a video or live demonstration of a user interface.
+
+ Alternatively, some of the individual QML files can be run on the command
+ line with the \l {QML Runtime Tool}{qml tool}, as a quick reminder to see
+ how a particular kind of component can be built.
+
+ \section1 TapHandler
+
+ \image pointerhandlers-example-taphandler.webp
+
+ The "tap" page shows how to detect touchscreen taps, long-presses, and
+ mouse clicks with different buttons. You can also try out the different
+ \l {TapHandler::}{gesturePolicy} values.
+
+ \section2 Multi-tap Buttons
+
+ On this page are three custom \c Button components, with different
+ \l {TapHandler::}{gesturePolicy} values: one button requires you
+ to keep your finger or mouse within the button during the whole time
+ from press to release; one will execute even if you drag outside
+ the button and then drag back in before releasing; and one will
+ exit its pressed state if you drag a little past the
+ \l {QStyleHints::startDragDistance}{system-wide drag threshold}.
+ If you try this example on a touchscreen, you should be able to interact
+ with all three buttons at the same time with different fingers.
+
+ \image pointerhandlers-example-multibutton.webp
+
+ \section1 PointHandler
+
+ On the "single point handler" page, PointHandler provides feedback from
+ some of the properties that can be found in \l SinglePointHandler::point
+ and \l handlerPoint::device. If you have built the example as
+ an executable, you will see this feedback at the same time as the feedback
+ from the PointHandlers in the main pointerhandlers.qml file.
+
+ \image pointerhandlers-example-pointhandler.webp
+
+ The "tablet canvas" page uses PointHandler to uniquely detect different
+ \l {QPointingDevice::PointerType}{types of pointing devices}, so that if
+ you have a drawing tablet connected to your computer, you can draw on the
+ Canvas item with different types of stylus: pen, airbrush or marker. You
+ can "erase" strokes with the stylus eraser (done here by drawing on top
+ with the background color). Several \l {HoverHandler}{HoverHandlers}
+ provide feedback about which type of stylus or eraser is detected.
+
+ \section1 HoverHandler
+
+ The "hover sidebar" page shows how to detect when the mouse is hovering a
+ button component and its container at the same time. The propagation can be
+ disabled by setting the \l HoverHandler::blocking property. You can try out
+ all combinations of HoverHandler and MouseArea here, to compare how they
+ handle hover detection. And one more HoverHandler is used to show the
+ current mouse position in scene coordinates.
+
+ You can also verify that the HoverHandler on the animated "platform"
+ is hovered whenever it slides under the mouse cursor.
+
+ \section1 DragHandler
+
+ The "joystick" page simply has a \l DragHandler dragging an \l Image.
+ A \l State with \l AnchorChanges unlocks the \l anchors so that dragging is
+ possible; and an \l AnchorAnimation animates the knob's return to the
+ center position when released.
+
+ \image pointerhandlers-example-joystick.jpg
+
+ The "fling animation" page demonstrates one use of the
+ DragHandler::centroid::velocity property, which simply makes the value from
+ QEventPoint::velocity() available in QML. The \c MomentumAnimation component
+ that is used in this example shows one way to simulate phyics (momentum and
+ friction) without resorting to heavier solutions.
+
+ \image pointerhandlers-example-fling.webp
+
+ \section1 PinchHandler
+
+ The "pinch" page demonstrates multiple PinchHandlers: some require two
+ fingers and some require three, to perform the usual scaling, rotation and
+ translation, with constraints managed via PinchHandler's axis min and max
+ properties. One of the PinchHandler instances is used to manipulate
+ different \l Rectangle properties rather than position, scale and rotation.
+ Various PinchHandler properties are bound in various ways. You should be
+ able to manipulate each instance uniquely. Some items also have
+ DragHandlers and TapHandlers.
+
+ \image pointerhandlers-example-pinchhandler.webp
+
+ \section1 Interoperability
+
+ \section2 Sliders
+
+ The "mixer" page demonstrates the use of multiple handlers inside a
+ ListView delegate. You can interact with multiple \c Slider components
+ simultaneously, and you can flick the ListView sideways.
+
+ \list
+ \li A DragHandler allows you to start dragging anywhere along the "track"
+ where the slider knob can slide. Because the default \l{DragHandler::}{snapMode}
+ is \c DragHandler.SnapAuto, the knob will \e snap into a position centered
+ under the mouse or touchpoint after you have dragged past the
+ \l {QStyleHints::startDragDistance}{system-wide drag threshold}.
+
+ \li A WheelHandler directly adjusts the \c y property of the knob \l Image.
+
+ \li A BoundaryRule prevents either the DragHandler or the WheelHandler from
+ dragging the knob too far.
+
+ \li A TapHandler provides one more gesture than a typical \c Slider component
+ would have: you can tap on the knob.
+ \endlist
+
+ \image pointerhandlers-example-mixer.webp
+
+ \section2 Map
+
+ The "map" page demonstrates dragging, transformation and re-scaling an
+ \l {Qt SVG}{SVG} \l Image. You should be able to zoom into a particular
+ location on the map; and if the zoom level is changed substantially,
+ \l Image::sourceSize is changed to request the SVG to be re-rendered at a
+ different resolution. Dragging vertically with two fingers activates a
+ DragHandler that manipulates a \l Rotation transform to tilt the map.
+
+ \image pointerhandlers-example-map.webp
+
+ \section2 Fake Flickable
+
+ The "fake Flickable" page contains an attempt to reproduce much of the
+ functionality of \l Flickable using discrete pointer handlers and
+ animations. Flickable is a complex component, but here you can see one way
+ of separating the individual behaviors that it provides, in case you would
+ like to have only part of the functionality without the complexity.
+
+ There's also a slide-out "drawer" component holding a few more
+ pointer-handler-powered controls.
+
+ \image pointerhandlers-example-fakeflickable.jpg
+*/
diff --git a/examples/quick/pointerhandlers/map.qml b/examples/quick/pointerhandlers/map.qml
index f54319f8aa..1a25a3b4ca 100644
--- a/examples/quick/pointerhandlers/map.qml
+++ b/examples/quick/pointerhandlers/map.qml
@@ -120,8 +120,6 @@ Item {
target: map
minimumScale: 0.1
maximumScale: 10
- xAxis.enabled: false
- yAxis.enabled: false
onActiveChanged: if (!active) reRenderIfNecessary()
grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started
}
diff --git a/examples/quick/scenegraph/graph/noisynode.cpp b/examples/quick/scenegraph/graph/noisynode.cpp
index f8b72bfe67..b9a9b6b1ae 100644
--- a/examples/quick/scenegraph/graph/noisynode.cpp
+++ b/examples/quick/scenegraph/graph/noisynode.cpp
@@ -151,6 +151,7 @@ void NoisyShader::updateSampledImage(RenderState &state, int binding, QSGTexture
Q_UNUSED(binding);
NoisyMaterial *mat = static_cast<NoisyMaterial *>(newMaterial);
+ mat->state.texture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
*texture = mat->state.texture;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1f82b16a62..3c6a28072f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,7 +31,9 @@ if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Test)
if(QT_FEATURE_testlib AND TARGET Qt::Test)
add_subdirectory(qmltest)
- add_subdirectory(quicktestutils)
+ if(QT_FEATURE_network)
+ add_subdirectory(quicktestutils)
+ endif()
endif()
if(QT_FEATURE_quick_particles)
@@ -48,7 +50,7 @@ if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
add_subdirectory(quickdialogs2)
add_subdirectory(quicknativestyle)
- if(QT_FEATURE_testlib AND TARGET Qt::Test AND TARGET Qt::QuickControls2)
+ if(QT_FEATURE_testlib AND QT_FEATURE_network AND TARGET Qt::Test AND TARGET Qt::QuickControls2)
add_subdirectory(quickcontrolstestutils)
endif()
else()
diff --git a/src/labs/animation/qquickboundaryrule.cpp b/src/labs/animation/qquickboundaryrule.cpp
index 161909b078..0b2d2e2856 100644
--- a/src/labs/animation/qquickboundaryrule.cpp
+++ b/src/labs/animation/qquickboundaryrule.cpp
@@ -146,7 +146,7 @@ void QQuickBoundaryReturnJob::updateState(QAbstractAnimationJob::State newState,
Note that a property cannot have more than one assigned BoundaryRule.
- \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior example}, {Qt QML}
+ \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior example}, {Qt QML}, {Pointer Handlers Example}
*/
QQuickBoundaryRule::QQuickBoundaryRule(QObject *parent)
@@ -419,7 +419,7 @@ bool QQuickBoundaryRule::returnToBounds()
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::easing
+ \qmlproperty enumeration QtQuick::BoundaryRule::easing
This property holds the easing curve to be applied in overshoot mode
(whenever the \l minimum or \l maximum constraint is violated, while
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 884edc3390..750c0446c8 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -766,6 +766,7 @@ void QQuickImageParticle::sceneGraphInvalidated()
m_material = nullptr;
delete m_outgoingNode;
m_outgoingNode = nullptr;
+ m_apiChecked = false;
}
void QQuickImageParticle::setImage(const QUrl &image)
@@ -1074,6 +1075,12 @@ void QQuickImageParticle::reset()
update();
}
+
+void QQuickImageParticle::invalidateSceneGraph()
+{
+ reset();
+}
+
void QQuickImageParticle::createEngine()
{
if (m_spriteEngine)
diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h
index 19000f6c9b..03a96b7901 100644
--- a/src/particles/qquickimageparticle_p.h
+++ b/src/particles/qquickimageparticle_p.h
@@ -395,6 +395,8 @@ private Q_SLOTS:
void spritesUpdate(qreal time = 0 );
void mainThreadFetchImageData();
void finishBuildParticleNodes(QSGNode **n);
+ void invalidateSceneGraph();
+
private:
struct ImageData {
QUrl source;
diff --git a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
index ab5cfb48b8..615d6e8726 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
@@ -420,6 +420,7 @@ QImage::Format qVGImageFormatToQImageFormat(VGImageFormat format)
break;
case VG_sL_8:
qImageFormat = QImage::Format_Grayscale8;
+ break;
default:
qImageFormat = QImage::Format_ARGB32;
break;
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index a66aa27561..b81a0c80ea 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -59,6 +59,7 @@ qt_add_qml_module(
[RESOURCES ...]
[OUTPUT_TARGETS out_targets_var]
[DESIGNER_SUPPORTED]
+ [NO_PLUGIN]
[NO_PLUGIN_OPTIONAL]
[NO_CREATE_PLUGIN_TARGET]
[NO_GENERATE_PLUGIN_SOURCE]
@@ -324,8 +325,12 @@ been created. When \c NO_CREATE_PLUGIN_TARGET is given, \c PLUGIN_TARGET must
also be provided to explicitly name the plugin target.
Every QML module must define a \c URI. It should be specified in dotted URI
-notation, such as \c{QtQuick.Layouts}. It must not contain anything other than
-alphanumeric or dot characters. Other QML modules may use this name in
+notation, such as \c{QtQuick.Layouts}. Each segment must be a well-formed
+ECMAScript Identifier Name. This means, for example, the segments
+must not start with a number and they must not contain \e{-} (minus)
+characters. As the \c URI will be translated into directory names, you
+should restrict it to alphanumeric characters of the latin alphabet,
+underscores, and dots. Other QML modules may use this name in
\l{qtqml-syntax-imports.html}{import statements} to import the module. The
\c URI will be used in the \c module line of the generated
\l{Module Definition qmldir Files}{qmldir} file. The \c URI is also used to
@@ -412,7 +417,21 @@ plugin class, the \c NO_GENERATE_PLUGIN_SOURCE option should be given. Where no
\c CLASS_NAME is provided, it defaults to the \c URI with dots replaced by
underscores, then \c Plugin appended. Unless the QML module has no plugin, the
class name will be recorded as a \c classname line in the generated
-\l{Module Definition qmldir Files}{qmldir} file.
+\l{Module Definition qmldir Files}{qmldir} file. You need to add any C++ files
+with custom plugin code to the plugin target. Since the plugin then likely
+contains functionality that goes beyond simply loading the backing library, you
+will probably want to add \l{NO_PLUGIN_OPTIONAL}, too. Otherwise the QML engine
+may skip loading the plugin if it detects that the backing library is already
+linked.
+
+\target NO_PLUGIN
+If the \c NO_PLUGIN keyword is given, then no plugin will be built. This
+keyword is thus incompatible with all the options that customize the plugin
+target, in particular \l{NO_GENERATE_PLUGIN_SOURCE}, \l{NO_PLUGIN_OPTIONAL},
+\l{PLUGIN_TARGET}, \l{NO_CREATE_PLUGIN_TARGET}, and \l{CLASS_NAME}. If you do
+not provide a plugin for your module, it will only be fully usable if its
+backing library has been linked into the executable. It is generally hard to
+guarantee that a linker preserves the linkage to a library it considers unused.
\target NO_PLUGIN_OPTIONAL
If the \c NO_PLUGIN_OPTIONAL keyword is given, then the plugin is recorded in
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index 40e0627e6d..d4fe08e37d 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -97,15 +97,18 @@ Types to QML} explains, the properties, methods and signals of any
QObject-derived class are accessible from QML code.
To register a QObject-derived class as an instantiable QML object type, add
-\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration and
+\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration. You
+also need to make adjustments in the build system. For qmake, add
\c {CONFIG += qmltypes}, a \c {QML_IMPORT_NAME}, and a
-\c QML_IMPORT_MAJOR_VERSION to your project file. This will register the class
-into the type namespace under the given major version, using either the class
-name or an explicitly given name as QML type name. The minor version(s) will
-be derived from any revisions attached to properties, methods, or signals. The
-default minor version is \c 0. You can explicitly restrict the type to be
-available only from specific minor versions by adding the
-\c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
+\c QML_IMPORT_MAJOR_VERSION to your project file. For CMake, the file containing
+the class should be part of a target set-up with
+\l{qt_add_qml_module}{qt_add_qml_module()}.
+This will register the class into the type namespace under the given major version,
+using either the class name or an explicitly given name as QML type name. The
+minor version(s) will be derived from any revisions attached to properties,
+methods, or signals. The default minor version is \c 0. You can explicitly
+restrict the type to be available only from specific minor versions by adding
+the \c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
import suitable versions of the namespace in order to use the type.
For example, suppose there is a \c Message class with \c author and
@@ -127,26 +130,52 @@ This type can be registered by adding an appropriate type namespace and version
number to the project file. For example, to make the type available in the
\c com.mycompany.messaging namespace with version 1.0:
-\code
-CONFIG += qmltypes
-QML_IMPORT_NAME = com.mycompany.messaging
-QML_IMPORT_MAJOR_VERSION = 1
-\endcode
+\if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{selected}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section3 Using CMake
+\endif
+ \badcode
+ qt_add_qml_module(messaging
+ URI com.mycompany.messaging
+ VERSION 1.0
+ SOURCES
+ message.cpp message.h
+ )
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+\else
+ \section3 Using QMake
+\endif
+ \code
+ CONFIG += qmltypes
+ QML_IMPORT_NAME = com.mycompany.messaging
+ QML_IMPORT_MAJOR_VERSION = 1
+ \endcode
+
+ If the header the class is declared in is not accessible from your project's
+ include path, you may have to amend the include path so that the generated
+ registration code can be compiled.
+
+ \code
+ INCLUDEPATH += com/mycompany/messaging
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+\endif
-If the header the class is declared in is not accessible from your project's
-include path, you may have to amend the include path so that the generated
-registration code can be compiled:
-\code
-INCLUDEPATH += com/mycompany/messaging
-\endcode
The type can be used in an \l{qtqml-syntax-basics.html#object-declarations}
{object declaration} from QML, and its properties can be read and written to,
as per the example below:
\qml
-import com.mycompany.messaging 1.0
+import com.mycompany.messaging
Message {
author: "Amelie"
diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
index 7054378b07..493b031b60 100644
--- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
@@ -44,6 +44,12 @@ Identified modules must be installed into the
\l{qtqml-syntax-imports.html#qml-import-path}{import path} in order to be found
by the QML engine.
+Syntactically, each dot-separated segment of the URI must be a well-formed
+ECMAScript Identifier Name. This means, for example, the segments must not start
+with a number and they must not contain \e{-} (minus) characters. As the URI
+will be translated into directory names, you should restrict it to alphanumeric
+characters of the latin alphabet, underscores, and dots.
+
\section1 Locally Installed Identified Modules
A directory of QML and/or C++ files can be shared as an identified module if it
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index b21acf93c7..ced4c3e057 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -554,6 +554,8 @@ void BaselineJIT::generate_ThrowException()
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore);
as->gotoCatchException();
+
+ // LOAD_ACC(); <- not needed here since it would be unreachable.
}
void BaselineJIT::generate_GetException() { as->getException(); }
@@ -561,9 +563,11 @@ void BaselineJIT::generate_SetException() { as->setException(); }
void BaselineJIT::generate_CreateCallContext()
{
+ STORE_ACC();
as->prepareCallWithArgCount(1);
as->passCppFrameAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore);
+ LOAD_ACC();
}
void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 1873c41261..4000deaeab 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -72,7 +72,7 @@ struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object {
void init(size_t length);
void init(const QByteArray& array);
void destroy();
- std::aligned_storage_t<sizeof(QArrayDataPointer<char>), alignof(QArrayDataPointer<char>)> d;
+ struct { alignas(QArrayDataPointer<char>) unsigned char data[sizeof(QArrayDataPointer<char>)]; } d;
const QArrayDataPointer<char> &data() const { return *reinterpret_cast<const QArrayDataPointer<char> *>(&d); }
QArrayDataPointer<char> &data() { return *reinterpret_cast<QArrayDataPointer<char> *>(&d); }
bool isShared;
diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index f3e2b445d1..6cff8d3c9b 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -65,6 +65,11 @@ public:
s_staticUnits.insert(file, staticUnit);
}
+ void remove(const QString &file)
+ {
+ s_staticUnits.remove(file);
+ }
+
private:
QMutexLocker<QMutex> m_lock;
@@ -105,4 +110,10 @@ CompiledData::Unit *CompilationUnitMapper::get(
return data;
}
+void CompilationUnitMapper::invalidate(const QString &cacheFilePath)
+{
+ StaticUnitCache cache;
+ cache.remove(cacheFilePath);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 10ad0a6ede..c6773fdd0e 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -69,6 +69,7 @@ public:
CompiledData::Unit *get(
const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ static void invalidate(const QString &cacheFilePath);
private:
CompiledData::Unit *open(
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index f47ce4f825..c047298aba 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1530,6 +1530,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
if (typeHint == QMetaType::Bool)
return QVariant(value.toBoolean());
+ if (typeHint == QMetaType::Double)
+ return QVariant(value.toNumber());
+
+ if (typeHint == QMetaType::Float)
+ return QVariant(float(value.toNumber()));
+
if (typeHint == QMetaType::QJsonValue)
return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index bac5104fde..7fe3862172 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -857,8 +857,14 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
[&unitUrl, errorString](const char *data, quint32 size) {
- return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
- size, errorString);
+ const QString cachePath = localCacheFilePath(unitUrl);
+ if (CompiledData::SaveableUnitPointer::writeDataToFile(
+ cachePath, data, size, errorString)) {
+ CompilationUnitMapper::invalidate(cachePath);
+ return true;
+ }
+
+ return false;
});
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index c897c63024..8be952571a 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1992,7 +1992,7 @@ bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, c
qvariantPtr->convert(callMetaType);
} else {
QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
- if (!mo.isNull()) {
+ if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
QObject *obj = QQmlMetaType::toQObject(v);
if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index fddb97e087..1b737ee308 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1488,7 +1488,12 @@ void AOTCompiledContext::initGetEnumLookup(
{
Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- Q_ASSERT(metaObject);
+ if (!metaObject) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of undefined")
+ .arg(QString::fromUtf8(enumValue)));
+ return;
+ }
const int enumIndex = metaObject->indexOfEnumerator(enumerator);
const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
l->qmlEnumValueLookup.encodedEnumValue = value;
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 3dabaca09f..0b5081d491 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -691,14 +691,34 @@ bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
valueType ? valueType->coreIndex() : -1);
}
-bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
+static const QQmlPropertyData *getObjectPropertyData(QObject *object, int coreIndex)
{
- m_target = object;
+ QQmlData *data = QQmlData::get(object, true);
+ if (!data)
+ return nullptr;
+ if (!data->propertyCache) {
+ data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
+ if (!data->propertyCache)
+ return nullptr;
+ data->propertyCache->addref();
+ }
+ const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
+ Q_ASSERT(propertyData);
+ return propertyData;
+}
- if (!object) {
+bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
+{
+ auto invalidate = [this]() {
+ m_target = nullptr;
m_targetIndex = QQmlPropertyIndex();
return false;
- }
+ };
+
+ if (!object)
+ return invalidate();
+
+ m_target = object;
for (bool isAlias = coreIsAlias; isAlias;) {
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
@@ -706,21 +726,25 @@ bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, in
int aValueTypeIndex;
if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
// can't resolve id (yet)
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
+ return invalidate();
}
- if (valueTypeIndex == -1)
- valueTypeIndex = aValueTypeIndex;
- QQmlData *data = QQmlData::get(object, false);
- if (!data || !data->propertyCache) {
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
+ const QQmlPropertyData *propertyData = getObjectPropertyData(object, coreIndex);
+ if (!propertyData)
+ return invalidate();
+ if (aValueTypeIndex != -1) {
+ if (propertyData->propType().flags().testFlag(QMetaType::PointerToQObject)) {
+ // deep alias
+ propertyData->readProperty(object, &object);
+ coreIndex = aValueTypeIndex;
+ valueTypeIndex = -1;
+ propertyData = getObjectPropertyData(object, coreIndex);
+ if (!propertyData)
+ return invalidate();
+ } else {
+ valueTypeIndex = aValueTypeIndex;
+ }
}
- QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
- Q_ASSERT(propertyData);
m_target = object;
isAlias = propertyData->isAlias();
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 3a2f678419..ff8e2de238 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1769,7 +1769,7 @@ void QQmlEnginePrivate::cleanupScarceResources()
The newly added \a path will be first in the importPathList().
- \sa setImportPathList(), {QML Modules}
+ \sa setImportPathList(), {QML Modules}, {QML Import Path}
*/
void QQmlEngine::addImportPath(const QString& path)
{
@@ -1787,9 +1787,8 @@ void QQmlEngine::addImportPath(const QString& path)
provided by that module. A \c qmldir file is required for defining the
type version mapping and possibly QML extensions plugins.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -1803,9 +1802,11 @@ QStringList QQmlEngine::importPathList() const
Sets \a paths as the list of directories where the engine searches for
installed modules in a URL-based directory structure.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
+
+ \warning Calling setImportPathList does not preserve the default
+ import paths.
\sa importPathList(), addImportPath()
*/
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 22846a84dc..c2fc291d48 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -43,6 +43,21 @@
QT_BEGIN_NAMESPACE
/*!
+ \since 5.0
+ \inmodule QtQml
+ \class QQmlExtensionPlugin
+ \brief The QQmlExtensionPlugin class provides an abstract base for custom QML extension plugins
+ with custom type registration functions.
+
+ \ingroup plugins
+
+ \note If you need to write a plugin manually (which is rare) you should always use
+ \l{QQmlEngineExtensionPlugin}. QQmlExtensionPlugin only provides the registerTypes() and
+ unregisterTypes() functions in addition. You should not use them, but rather declare your
+ types with \l{QML_ELEMENT} and friends and have the build system take care of the registration.
+*/
+
+/*!
\since 5.14
\inmodule QtQml
\class QQmlEngineExtensionPlugin
@@ -61,7 +76,6 @@ QT_BEGIN_NAMESPACE
/*!
\fn void QQmlExtensionPlugin::registerTypes(const char *uri)
- \internal
Registers the QML types in the given \a uri. Subclasses should implement
this to call qmlRegisterType() for all types which are provided by the extension
@@ -127,9 +141,10 @@ void QQmlExtensionPlugin::unregisterTypes()
}
/*!
- \internal
-*/
-
+ Initializes the extension from the \a uri using the \a engine. Here an application
+ plugin might, for example, expose some data or objects to QML,
+ as context properties on the engine's root context.
+ */
void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_UNUSED(engine);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index e35df5b907..d04d6751cd 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1169,6 +1169,26 @@ QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDataba
return stableRelativePath;
}
+/* removes all file selector occurrences in path
+ firstPlus is the position of the initial '+' in the path
+ which we always have as we check for '+' to decide whether
+ we need to do some work at all
+*/
+static QString pathWithoutFileSelectors(QString path, // we want a copy of path
+ qsizetype firstPlus)
+{
+ do {
+ Q_ASSERT(path.at(firstPlus) == u'+');
+ const auto eos = path.size();
+ qsizetype terminatingSlashPos = firstPlus + 1;
+ while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
+ ++terminatingSlashPos;
+ path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
+ firstPlus = path.indexOf(u'+', firstPlus);
+ } while (firstPlus != -1);
+ return path;
+}
+
/*!
\internal
@@ -1215,10 +1235,42 @@ QTypeRevision QQmlImportsPrivate::matchingQmldirVersion(
typedef QQmlDirComponents::const_iterator ConstIterator;
const QQmlDirComponents &components = qmldir.components();
+ QMultiHash<QString, ConstIterator> baseFileName2ConflictingComponents;
+
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
+ // ugly heuristic to deal with file selectors
+ const auto comp2PotentialFileSelectorPos = cit2->fileName.indexOf(u'+');
+ const bool comp2MightHaveFileSelector = comp2PotentialFileSelectorPos != -1;
+ /* If we detect conflicting paths, we check if they agree when we remove anything looking like a
+ file selector.
+ We need to create copies of the filenames, otherwise QString::replace would modify the
+ existing file-names
+ */
+ QString compFileName1 = cit->fileName;
+ QString compFileName2 = cit2->fileName;
+ if (auto fileSelectorPos1 = compFileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
+ // existing entry was file selector entry, fix it up
+ // it could also be the case that _both_ are using file selectors
+ QString baseName = comp2MightHaveFileSelector ? pathWithoutFileSelectors(compFileName2,
+ comp2PotentialFileSelectorPos)
+ : compFileName2;
+ if (pathWithoutFileSelectors(compFileName1, fileSelectorPos1) == baseName) {
+ baseFileName2ConflictingComponents.insert(baseName, cit);
+ baseFileName2ConflictingComponents.insert(baseName, cit2);
+ continue;
+ }
+ // fall through to error case
+ } else if (comp2MightHaveFileSelector) {
+ // new entry contains file selector (and we now that cit did not)
+ if (pathWithoutFileSelectors(compFileName2, comp2PotentialFileSelectorPos) == compFileName1) {
+ baseFileName2ConflictingComponents.insert(compFileName1, cit2);
+ continue;
+ }
+ // fall through to error case
+ }
// This entry clashes with a predecessor
QQmlError error;
error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
@@ -1232,6 +1284,14 @@ QTypeRevision QQmlImportsPrivate::matchingQmldirVersion(
addVersion(cit->version);
}
+ // ensure that all components point to the actual base URL, and let the file selectors resolve them correctly during URL resolution
+ for (auto keyIt = baseFileName2ConflictingComponents.keyBegin(); keyIt != baseFileName2ConflictingComponents.keyEnd(); ++keyIt) {
+ const QString& baseFileName = *keyIt;
+ const auto conflictingComponents = baseFileName2ConflictingComponents.values(baseFileName);
+ for (ConstIterator component: conflictingComponents)
+ component->fileName = baseFileName;
+ }
+
typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
const QQmlDirScripts &scripts = qmldir.scripts();
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index a61cf4adea..fdf74f4873 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -337,6 +337,10 @@ void QQmlMetaType::clearTypeRegistrations()
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->undeletableTypes.clear();
+
+ for (auto it = data->propertyCaches.begin(), end = data->propertyCaches.end(); it != end; ++it)
+ (*it)->release();
+ data->propertyCaches.clear();
}
int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function)
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 69b0755e2d..0a96bb0af8 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -1475,16 +1475,24 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
void *argv[] = { &bindable };
// allow interception
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
- bindable.setBinding(qmlBinding);
+ const bool success = bindable.setBinding(qmlBinding);
+
+ // Only pop_front after setting the binding as the bindings are refcounted.
sharedState->allQPropertyBindings.pop_front();
- if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
- auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
- auto jsExpression = qmlBindingPriv->jsExpression();
- const bool canRemove = !qmlBinding.error().hasError() && !qmlBindingPriv->hasDependencies()
- && !jsExpression->hasUnresolvedNames();
- if (canRemove)
- bindable.takeBinding();
+
+ // If the binding was actually not set, it's deleted now.
+ if (success) {
+ if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
+ auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
+ auto jsExpression = qmlBindingPriv->jsExpression();
+ const bool canRemove = !qmlBinding.error().hasError()
+ && !qmlBindingPriv->hasDependencies()
+ && !jsExpression->hasUnresolvedNames();
+ if (canRemove)
+ bindable.takeBinding();
+ }
}
+
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return false;
}
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index b03b8e937a..2ebba1eb72 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -51,8 +51,8 @@
#include <QtCore/qdebug.h>
#include <QtCore/QCryptographicHash>
+#include <QtCore/private/qtools_p.h>
-#include <ctype.h> // for toupper
#include <limits.h>
#include <algorithm>
@@ -527,7 +527,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QVarLengthArray<char, 128> str(length+3);
str[0] = 'o';
str[1] = 'n';
- str[2] = toupper(rawName[0]);
+ str[2] = QtMiscUtils::toAsciiUpper(rawName[0]);
if (length > 1)
memcpy(&str[3], &rawName[1], length - 1);
str[length + 2] = '\0';
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 837199904e..46dc108b9e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -375,26 +375,10 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
}
/*!
- \qmlmethod matrix4x4 Qt::matrix4x4(real m11, real m12, real m13, real m14, real m21, real m22, real m23, real m24, real m31, real m32, real m33, real m34, real m41, real m42, real m43, real m44)
-
- Returns a matrix4x4 with the specified values.
-
- The arguments correspond to their positions in the matrix:
-
- \table
- \row \li \a m11 \li \a m12 \li \a m13 \li \a m14
- \row \li \a m21 \li \a m22 \li \a m23 \li \a m24
- \row \li \a m31 \li \a m32 \li \a m33 \li \a m34
- \row \li \a m41 \li \a m42 \li \a m43 \li \a m44
- \endtable
+ \qmlmethod matrix4x4 Qt::matrix4x4()
- Alternatively, the function may be called with a single argument
- where that argument is a JavaScript array which contains the sixteen
- matrix values.
-
- Finally, the function may be called with no arguments and the resulting
- matrix will be the identity matrix.
-*/
+ Returns an identity matrix4x4.
+ */
QVariant QtObject::matrix4x4() const
{
QVariant variant;
@@ -402,6 +386,21 @@ QVariant QtObject::matrix4x4() const
return variant;
}
+/*!
+ \qmlmethod matrix4x4 Qt::matrix4x4(var values)
+
+ Returns a matrix4x4 with the specified \a values. \a values is expected to
+ be a JavaScript array with 16 entries.
+
+ The array indices correspond to positions in the matrix as follows:
+
+ \table
+ \row \li 0 \li 1 \li 2 \li 3
+ \row \li 4 \li 5 \li 6 \li 7
+ \row \li 8 \li 9 \li 10 \li 11
+ \row \li 12 \li 13 \li 14 \li 15
+ \endtable
+*/
QVariant QtObject::matrix4x4(const QJSValue &value) const
{
if (value.isObject()) {
@@ -415,6 +414,20 @@ QVariant QtObject::matrix4x4(const QJSValue &value) const
return QVariant();
}
+/*!
+ \qmlmethod matrix4x4 Qt::matrix4x4(real m11, real m12, real m13, real m14, real m21, real m22, real m23, real m24, real m31, real m32, real m33, real m34, real m41, real m42, real m43, real m44)
+
+ Returns a matrix4x4 with the specified values.
+
+ The arguments correspond to their positions in the matrix:
+
+ \table
+ \row \li \a m11 \li \a m12 \li \a m13 \li \a m14
+ \row \li \a m21 \li \a m22 \li \a m23 \li \a m24
+ \row \li \a m31 \li \a m32 \li \a m33 \li \a m34
+ \row \li \a m41 \li \a m42 \li \a m43 \li \a m44
+ \endtable
+*/
QVariant QtObject::matrix4x4(double m11, double m12, double m13, double m14,
double m21, double m22, double m23, double m24,
double m31, double m32, double m33, double m34,
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index f13a39946d..70e048b0ea 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -60,15 +60,13 @@ Q_LOGGING_CATEGORY(lcQmlConnections, "qt.qml.connections")
class QQmlConnectionsPrivate : public QObjectPrivate
{
public:
- QQmlConnectionsPrivate() : target(nullptr), enabled(true), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
-
QList<QQmlBoundSignal*> boundsignals;
- QObject *target;
+ QQmlGuard<QObject> target;
- bool enabled;
- bool targetSet;
- bool ignoreUnknownSignals;
- bool componentcomplete;
+ bool enabled = true;
+ bool targetSet = false;
+ bool ignoreUnknownSignals = false;
+ bool componentcomplete = true;
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding *> bindings;
@@ -159,7 +157,7 @@ QQmlConnections::~QQmlConnections()
QObject *QQmlConnections::target() const
{
Q_D(const QQmlConnections);
- return d->targetSet ? d->target : parent();
+ return d->targetSet ? d->target.data() : parent();
}
class QQmlBoundSignalDeleter : public QObject
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index 5ccbe91045..2d63a860c3 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -63,7 +63,7 @@ public:
\param location: The location where an error occurred.
*/
IssueLocationWithContext(QStringView code, const QQmlJS::SourceLocation &location) {
- int before = qMax(0,code.lastIndexOf(QLatin1Char('\n'), location.offset));
+ quint32 before = qMax(0, code.lastIndexOf(QLatin1Char('\n'), location.offset));
if (before != 0) before++;
diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp
index d627af12ef..ee48a71c29 100644
--- a/src/qmldom/qqmldomreformatter.cpp
+++ b/src/qmldom/qqmldomreformatter.cpp
@@ -972,7 +972,11 @@ protected:
// to check
bool visit(TypeExpression *) override { return true; }
- bool visit(SuperLiteral *) override { return true; }
+ bool visit(SuperLiteral *) override
+ {
+ out("super");
+ return true;
+ }
bool visit(PatternProperty *) override { return true; }
bool visit(ComputedPropertyName *) override
{
@@ -989,7 +993,61 @@ protected:
}
bool visit(YieldExpression *) override { return true; }
bool visit(ClassExpression *) override { return true; }
- bool visit(ClassDeclaration *) override { return true; }
+
+ // Return false because we want to omit default function calls in accept0 implementation.
+ bool visit(ClassDeclaration *ast) override
+ {
+ preVisit(ast);
+ out(ast->classToken);
+ out(" ");
+ out(ast->name);
+ if (ast->heritage) {
+ out(" extends ");
+ accept(ast->heritage);
+ }
+ out(" {");
+ int baseIndent = lw.increaseIndent();
+ for (ClassElementList *it = ast->elements; it; it = it->next) {
+ PatternProperty *property = it->property;
+ lw.newline();
+ preVisit(property);
+ if (it->isStatic)
+ out("static ");
+ if (property->type == PatternProperty::Getter)
+ out("get ");
+ else if (property->type == PatternProperty::Setter)
+ out("set ");
+ FunctionExpression *f = AST::cast<FunctionExpression *>(property->initializer);
+ const bool scoped = f->lbraceToken.length != 0;
+ out(f->functionToken);
+ out(f->lparenToken);
+ accept(f->formals);
+ out(f->rparenToken);
+ out(f->lbraceToken);
+ if (scoped)
+ ++expressionDepth;
+ if (f->body) {
+ if (f->body->next || scoped) {
+ lnAcceptIndented(f->body);
+ lw.newline();
+ } else {
+ baseIndent = lw.increaseIndent(1);
+ accept(f->body);
+ lw.decreaseIndent(1, baseIndent);
+ }
+ }
+ if (scoped)
+ --expressionDepth;
+ out(f->rbraceToken);
+ lw.newline();
+ postVisit(property);
+ }
+ lw.decreaseIndent(1, baseIndent);
+ out("}");
+ postVisit(ast);
+ return false;
+ }
+
bool visit(ClassElementList *) override { return true; }
bool visit(Program *) override { return true; }
bool visit(NameSpaceImport *) override { return true; }
diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index 039098a80e..c060aaec97 100644
--- a/src/qmlmodels/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -129,8 +129,28 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data
const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
{
- const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(ListElement::GuardedQObjectPointer), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QUrl), sizeof(QJSValue) };
- const int dataAlignments[] = { alignof(StringOrTranslation), alignof(double), alignof(bool), alignof(ListModel *), alignof(QObject *), alignof(QVariantMap), alignof(QDateTime), alignof(QUrl), alignof(QJSValue) };
+ const int dataSizes[] = {
+ sizeof(StringOrTranslation),
+ sizeof(double),
+ sizeof(bool),
+ sizeof(ListModel *),
+ sizeof(QV4::PersistentValue),
+ sizeof(QVariantMap),
+ sizeof(QDateTime),
+ sizeof(QUrl),
+ sizeof(QJSValue)
+ };
+ const int dataAlignments[] = {
+ alignof(StringOrTranslation),
+ alignof(double),
+ alignof(bool),
+ alignof(ListModel *),
+ alignof(QV4::PersistentValue),
+ alignof(QVariantMap),
+ alignof(QDateTime),
+ alignof(QUrl),
+ alignof(QJSValue)
+ };
Role *r = new Role;
r->name = key;
@@ -634,10 +654,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
roleIndex = e->setFunctionProperty(r, jsv);
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
- QObject *o = wrapper->object();
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
if (role.type == ListLayout::Role::QObject)
- roleIndex = e->setQObjectProperty(role, o);
+ roleIndex = e->setQObjectProperty(role, wrapper);
} else if (QVariant maybeUrl = o->engine()->toVariant(o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
@@ -725,10 +744,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
}
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
- QObject *o = wrapper->object();
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
if (r.type == ListLayout::Role::QObject)
- e->setQObjectPropertyFast(r, o);
+ e->setQObjectPropertyFast(r, wrapper);
} else {
QVariant maybeUrl = o->engine()->toVariant(o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
@@ -852,12 +870,11 @@ StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role
return s;
}
-QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
+QV4::QObjectWrapper *ListElement::getQObjectProperty(const ListLayout::Role &role)
{
char *mem = getPropertyMemory(role);
- GuardedQObjectPointer *o
- = reinterpret_cast<GuardedQObjectPointer *>(mem);
- return o->data();
+ QV4::PersistentValue *g = reinterpret_cast<QV4::PersistentValue *>(mem);
+ return g->as<QV4::QObjectWrapper>();
}
QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
@@ -904,13 +921,13 @@ QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role)
return f;
}
-ListElement::GuardedQObjectPointer *
+QV4::PersistentValue *
ListElement::getGuardProperty(const ListLayout::Role &role)
{
char *mem = getPropertyMemory(role);
bool existingGuard = false;
- for (size_t i = 0; i < sizeof(GuardedQObjectPointer);
+ for (size_t i = 0; i < sizeof(QV4::PersistentValue);
++i) {
if (mem[i] != 0) {
existingGuard = true;
@@ -918,12 +935,12 @@ ListElement::getGuardProperty(const ListLayout::Role &role)
}
}
- GuardedQObjectPointer *o = nullptr;
+ QV4::PersistentValue *g = nullptr;
if (existingGuard)
- o = reinterpret_cast<GuardedQObjectPointer *>(mem);
+ g = reinterpret_cast<QV4::PersistentValue *>(mem);
- return o;
+ return g;
}
ListModel *ListElement::getListProperty(const ListLayout::Role &role)
@@ -979,12 +996,8 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
break;
case ListLayout::Role::QObject:
{
- GuardedQObjectPointer *guard =
- reinterpret_cast<GuardedQObjectPointer *>(
- mem);
- QObject *object = guard->data();
- if (object)
- data = QVariant::fromValue(object);
+ QV4::PersistentValue *guard = reinterpret_cast<QV4::PersistentValue *>(mem);
+ data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
}
break;
case ListLayout::Role::VariantMap:
@@ -1096,67 +1109,17 @@ int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
return roleIndex;
}
-static void
-restoreQObjectOwnership(ListElement::GuardedQObjectPointer *pointer)
-{
- if (QObject *o = pointer->data()) {
- QQmlData *data = QQmlData::get(o, false);
- Q_ASSERT(data);
-
- // Only restore the previous state if the object hasn't become explicitly
- // owned
- if (!data->explicitIndestructibleSet)
- data->indestructible = (pointer->tag() & ListElement::Indestructible);
- }
-}
-
-static void setQObjectOwnership(char *mem, QObject *o)
-{
- QQmlData *ddata = QQmlData::get(o, false);
- const int ownership = (!ddata || ddata->indestructible ? ListElement::Indestructible : 0)
- | (ddata && ddata->explicitIndestructibleSet ? ListElement::ExplicitlySet : 0);
-
- // If ddata didn't exist above, force its creation now
- if (!ddata)
- ddata = QQmlData::get(o, true);
-
- if (!ddata->explicitIndestructibleSet)
- ddata->indestructible = ownership != 0;
-
- new (mem) ListElement::GuardedQObjectPointer(
- o, static_cast<ListElement::ObjectIndestructible>(ownership));
-}
-
-int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
+int ListElement::setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o)
{
int roleIndex = -1;
if (role.type == ListLayout::Role::QObject) {
char *mem = getPropertyMemory(role);
- GuardedQObjectPointer *g =
- reinterpret_cast<GuardedQObjectPointer *>(mem);
- bool existingGuard = false;
- for (size_t i = 0; i < sizeof(GuardedQObjectPointer);
- ++i) {
- if (mem[i] != 0) {
- existingGuard = true;
- break;
- }
- }
- bool changed;
- if (existingGuard) {
- changed = g->data() != o;
- if (changed)
- restoreQObjectOwnership(g);
- g->~GuardedQObjectPointer();
- } else {
- changed = true;
- }
-
- setQObjectOwnership(mem, o);
-
- if (changed)
- roleIndex = role.index;
+ if (isMemoryUsed<QVariantMap>(mem))
+ reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
+ else
+ new (mem) QV4::PersistentValue(o->engine(), o);
+ roleIndex = role.index;
}
return roleIndex;
@@ -1289,11 +1252,10 @@ void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
*value = b;
}
-void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
+void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o)
{
char *mem = getPropertyMemory(role);
-
- setQObjectOwnership(mem, o);
+ new (mem) QV4::PersistentValue(o->engine(), o);
}
void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
@@ -1410,7 +1372,7 @@ QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElem
break;
case ListLayout::Role::QObject:
{
- QObject *object = src->getQObjectProperty(srcRole);
+ QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
roleIndex = target->setQObjectProperty(targetRole, object);
}
break;
@@ -1465,14 +1427,8 @@ void ListElement::destroy(ListLayout *layout)
break;
case ListLayout::Role::QObject:
{
- GuardedQObjectPointer *guard =
- getGuardProperty(r);
-
- if (guard) {
- restoreQObjectOwnership(guard);
-
- guard->~GuardedQObjectPointer();
- }
+ if (QV4::PersistentValue *guard = getGuardProperty(r))
+ guard->~PersistentValue();
}
break;
case ListLayout::Role::VariantMap:
@@ -1609,8 +1565,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
QV4::ScopedObject o(scope, d);
QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
if (role.type == ListLayout::Role::QObject && wrapper) {
- QObject *o = wrapper->object();
- roleIndex = setQObjectProperty(role, o);
+ roleIndex = setQObjectProperty(role, wrapper);
} else if (role.type == ListLayout::Role::VariantMap) {
roleIndex = setVariantMapProperty(role, o);
} else if (role.type == ListLayout::Role::Url) {
diff --git a/src/qmlmodels/qqmllistmodel_p_p.h b/src/qmlmodels/qqmllistmodel_p_p.h
index 2ec7ea04db..19cba980f4 100644
--- a/src/qmlmodels/qqmllistmodel_p_p.h
+++ b/src/qmlmodels/qqmllistmodel_p_p.h
@@ -281,43 +281,6 @@ public:
enum ObjectIndestructible { Indestructible = 1, ExplicitlySet = 2 };
enum { BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) };
- // This is a basic guarded QObject pointer, with tag. It cannot be copied or moved.
- class GuardedQObjectPointer
- {
- Q_DISABLE_COPY_MOVE(GuardedQObjectPointer)
-
- using RefCountData = QtSharedPointer::ExternalRefCountData;
- using Storage = QTaggedPointer<QObject, ObjectIndestructible>;
-
- public:
- GuardedQObjectPointer(QObject *o, ObjectIndestructible ownership)
- : storage(o, ownership)
- , refCount(o ? RefCountData::getAndRef(o) : nullptr)
- {}
-
- ~GuardedQObjectPointer()
- {
- if (refCount && !refCount->weakref.deref())
- delete refCount;
- }
-
- QObject *data() const
- {
- return (refCount == nullptr || refCount->strongref.loadRelaxed() == 0)
- ? nullptr
- : storage.data();
- }
-
- ObjectIndestructible tag() const
- {
- return storage.tag();
- }
-
- private:
- Storage storage;
- RefCountData *refCount = nullptr;
- };
-
ListElement();
ListElement(int existingUid);
~ListElement();
@@ -336,7 +299,7 @@ private:
int setDoubleProperty(const ListLayout::Role &role, double n);
int setBoolProperty(const ListLayout::Role &role, bool b);
int setListProperty(const ListLayout::Role &role, ListModel *m);
- int setQObjectProperty(const ListLayout::Role &role, QObject *o);
+ int setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o);
int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o);
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
@@ -347,7 +310,7 @@ private:
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
void setDoublePropertyFast(const ListLayout::Role &role, double n);
void setBoolPropertyFast(const ListLayout::Role &role, bool b);
- void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o);
+ void setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o);
void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o);
void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
@@ -359,8 +322,8 @@ private:
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
ListModel *getListProperty(const ListLayout::Role &role);
StringOrTranslation *getStringProperty(const ListLayout::Role &role);
- QObject *getQObjectProperty(const ListLayout::Role &role);
- GuardedQObjectPointer *getGuardProperty(const ListLayout::Role &role);
+ QV4::QObjectWrapper *getQObjectProperty(const ListLayout::Role &role);
+ QV4::PersistentValue *getGuardProperty(const ListLayout::Role &role);
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
QDateTime *getDateTimeProperty(const ListLayout::Role &role);
QUrl *getUrlProperty(const ListLayout::Role &role);
diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp
index bf6c0ca5ed..b58d4ac00a 100644
--- a/src/qmltyperegistrar/qmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp
@@ -261,11 +261,29 @@ void QmlTypesClassDescription::collect(
accessSemantics = QLatin1String("reference");
} else {
isCreatable = false;
- // If no classDef, we assume it's a value type defined by the foreign/extended trick.
- // Objects and namespaces always have metaobjects and therefore classDefs.
- accessSemantics = (!classDef || classDef->value(QLatin1String("gadget")).toBool())
- ? QLatin1String("value")
- : QLatin1String("none");
+
+ if (!classDef) {
+ if (elementName.isEmpty() || elementName[0].isLower()) {
+ // If no classDef, we generally assume it's a value type defined by the
+ // foreign/extended trick.
+ accessSemantics = QLatin1String("value");
+ } else {
+ // Objects and namespaces always have metaobjects and therefore classDefs.
+ // However, we may not be able to resolve the metaobject at compile time. See
+ // the "Invisible" test case. In that case, we must not assume anything about
+ // access semantics.
+ qWarning() << "Warning: Refusing to generate non-lowercase name"
+ << elementName << "for unknown foreign type";
+ elementName.clear();
+ // Make it completely inaccessible.
+ // We cannot get enums from anonymous types after all.
+ accessSemantics = QLatin1String("none");
+ }
+ } else if (classDef->value(QLatin1String("gadget")).toBool()) {
+ accessSemantics = QLatin1String("value");
+ } else {
+ accessSemantics = QLatin1String("none");
+ }
}
}
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
new file mode 100644
index 0000000000..3edab7d7a1
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
new file mode 100644
index 0000000000..05cb2f2276
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
new file mode 100644
index 0000000000..0455097159
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
Binary files differ
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
index 20be120120..138ad332d9 100644
--- a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.12
-import QtQuick.Window 2.2
+import QtQuick
Window {
width: 480
@@ -61,6 +60,7 @@ Window {
z: 10000
anchors.fill: parent
+ //![1]
PointHandler {
id: handler
acceptedDevices: PointerDevice.TouchScreen | PointerDevice.TouchPad
@@ -73,6 +73,7 @@ Window {
width: 20; height: width; radius: width / 2
}
}
+ //![1]
}
}
//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
new file mode 100644
index 0000000000..a884672b0d
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ color: handler.active ? "tomato" : "wheat"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+
+ PointHandler {
+ id: handler
+ acceptedButtons: Qt.MiddleButton | Qt.RightButton
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
new file mode 100644
index 0000000000..0229f3bb0a
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ id: feedbackPane
+ width: 480; height: 320
+
+ PointHandler {
+ id: control
+ acceptedModifiers: Qt.ControlModifier
+ cursorShape: Qt.PointingHandCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: control.active ? "indianred" : "khaki"
+ x: control.point.position.x - width / 2
+ y: control.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+ }
+
+ PointHandler {
+ id: shift
+ acceptedModifiers: Qt.ShiftModifier | Qt.MetaModifier
+ cursorShape: Qt.CrossCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: shift.active ? "darkslateblue" : "lightseagreen"
+ x: shift.point.position.x - width / 2
+ y: shift.point.position.y - height / 2
+ width: 30; height: width; radius: width / 2
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
new file mode 100644
index 0000000000..5bc98c1230
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+//![0]
+import QtQuick
+
+Canvas {
+ id: canvas
+ width: 800
+ height: 600
+ antialiasing: true
+ renderTarget: Canvas.FramebufferObject
+ property var points: []
+ onPaint: {
+ if (points.length < 2)
+ return
+ var ctx = canvas.getContext('2d');
+ ctx.save()
+ ctx.strokeStyle = stylusHandler.active ? "blue" : "white"
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(points[0].x, points[0].y)
+ for (var i = 1; i < points.length; i++)
+ ctx.lineTo(points[i].x, points[i].y)
+ ctx.lineWidth = 3
+ ctx.stroke()
+ points = points.slice(points.length - 2, 1)
+ ctx.restore()
+ }
+
+ PointHandler {
+ id: stylusHandler
+ acceptedPointerTypes: PointerDevice.Pen
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ PointHandler {
+ id: eraserHandler
+ acceptedPointerTypes: PointerDevice.Eraser
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ Rectangle {
+ width: 10; height: 10
+ color: stylusHandler.active ? "green" : eraserHandler.active ? "red" : "beige"
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
new file mode 100644
index 0000000000..49161f0abe
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ anchors.fill: handlingContainer
+ anchors.margins: -handler.margin
+ color: "beige"
+ }
+
+ Rectangle {
+ id: handlingContainer
+ width: 200; height: 200
+ anchors.centerIn: parent
+ border.color: "green"
+ color: handler.active ? "lightsteelblue" : "khaki"
+
+ Text {
+ text: "X"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ visible: handler.active
+ }
+
+ PointHandler {
+ id: handler
+ margin: 30
+ }
+ }
+
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
new file mode 100644
index 0000000000..a6b1673d4c
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
new file mode 100644
index 0000000000..f2e968599c
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.WithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
new file mode 100644
index 0000000000..993330a5ec
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.DragThreshold // the default
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+ Button { x: 30; y: 30 }
+}
+//![0]
diff --git a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index bf889a9066..7a05583a82 100644
--- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -41,6 +41,8 @@
behavioral concerns are better separated, and the behavior is built up by
finer-grained composition.
+ The \l {Pointer Handlers Example} demonstrates some use cases for these.
+
The pre-existing \l Keys attached property is similar in concept, so we
refer to the pointing-device-oriented handlers plus \c Keys together as the
set of Input Handlers. We expect to offer more attached-property use cases
@@ -94,7 +96,8 @@
PointHandler) can work only with passive grabs; others require exclusive
grabs; and others can "lurk" with passive grabs until they detect that a
gesture is being performed, and then make the transition from passive to
- exclusive grab.
+ exclusive grab. TapHandler's grabbing behavior is
+ \l {TapHandler::gesturePolicy}{configurable}.
When a grab transition is requested, \l PointerHandler::grabPermissions,
\l QQuickItem::keepMouseGrab() and \l QQuickItem::keepTouchGrab() control
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index 30c940e6ea..9aa404a612 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -52,11 +52,16 @@ options that align with the latest UI design trends. If these UI controls do not
satisfy your application's needs, only then it is recommended to create a
custom control.
+You can use the controls when you design UIs in Qt Design Studio. In addition,
+it provides timeline-based animations, visual effects, layouts, and a
+live-preview for prototyping applications.
\section2 Related Information
\list
\li \l{Qt Quick Controls}
+\li \l{Customizing Qt Quick Controls}
\li \l{Qt Quick}
+\li \l{Qt Design Studio Manual}
\endlist
\omit
@@ -117,7 +122,7 @@ All QML files listed under \c {QML_FILES} will automatically get compiled \l {Ah
\li \l{The Qt Resource System}
\endlist
-\section1 Separate UI from Logic
+\section1 Separate UI from Business Logic
One of the key goals that most application developers want to achieve is to
create a maintainable application. One of the ways to achieve this goal is
@@ -133,8 +138,8 @@ reasons why an application's UI should be written in QML:
\li JavaScript can easily be used in QML to respond to events.
\endlist
-Being a strongly typed language, C++ is best suited for an application's logic.
-Typically, such code performs tasks such as complex calculations
+Being a strongly typed language, C++ is best suited for an application's
+business logic. Typically, such code performs tasks such as complex calculations
or data processing, which are faster in C++ than QML.
Qt offers various approaches to integrate QML and C++ code in an application.
@@ -177,6 +182,22 @@ see \l {Choosing the Correct Integration Method Between C++ and QML}.
\li \l{Qt Quick Controls - Chat Tutorial}{Chat application tutorial}
\endlist
+\section1 Using Qt Design Studio
+
+Qt Design Studio uses UI files that have the filename extension \e {.ui.qml}
+to separate the visual parts of the UI from the UI logic you implement in
+\e {.qml} files. You should edit UI files only in the \uicontrol {2D} view in
+Qt Design Studio. If you use some other tool to add code that Qt Design Studio
+does not support, it displays error messages. Fix the errors to enable visual
+editing of the UI files again. Typically, you should move the unsupported code
+to a \e {.qml} file.
+
+\section2 Related Information
+
+\list
+ \li \l{Qt Design Studio: UI Files}
+\endlist
+
\section1 Using Qt Quick Layouts
Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout.
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index f150c7e6ac..8c9464ee55 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -88,7 +88,7 @@ Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
At this time, drag-and-drop is not yet supported.
- \sa Drag, MouseArea
+ \sa Drag, MouseArea, {Pointer Handlers Example}
*/
QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
@@ -131,13 +131,13 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDe
This property holds the snap mode.
- The snap mode configures snapping of the \l target item's center to the event point.
+ The snap mode configures snapping of the \l target item's center to the \l eventPoint.
Possible values:
\value DragHandler.SnapNever Never snap
- \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
- item \e and the \l target is a descendant of \l parentItem (default)
- \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAuto The \l target snaps if the \l eventPoint was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l {PointerHandler::}{parent} item (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the \l eventPoint was pressed outside of the \l target
\value DragHandler.SnapAlways Always snap
*/
QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index 65cc3d4937..52513f8273 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -49,7 +49,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
\inqmlmodule QtQuick
\brief An event point.
- A QML representation of a QEventPoint.
+ A handler-owned QML representation of a QEventPoint.
It's possible to make bindings to properties of a handler's current
\l {SinglePointHandler::point}{point} or
@@ -58,12 +58,12 @@ Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but after the point is released, or when the current point is
+ an \l eventPoint; but after the point is released, or when the current point is
being handled by a different handler, \c position.x and \c position.y are 0.
- \note This is practically identical to QtQuick::EventPoint; however an
- EventPoint is a long-lived QObject which is invalidated between gestures
- and reused for subsequent event deliveries. Continuous bindings to its
+ \note This is practically identical to \l eventPoint; however an eventPoint
+ is a short-lived copy of a long-lived Q_GADGET which is invalidated between
+ gestures and reused for subsequent event deliveries. Continuous bindings to its
properties are not possible, and an individual handler cannot rely on it
outside the period when that point is part of an active gesture which that
handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
@@ -186,7 +186,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
During a touch gesture, from the time that the first finger is pressed
until the last finger is released, each touchpoint will have a unique ID
number. Likewise, if input from multiple devices occurs (for example
- simultaneous mouse and touch presses), all the current event points from
+ simultaneous mouse and touch presses), all the current \l{eventPoint}{eventPoints} from
all the devices will have unique IDs.
\note Do not assume that id numbers start at zero or that they are
@@ -223,7 +223,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\qmlproperty QPointF QtQuick::HandlerPoint::position
\brief The position within the \c parent Item
- This is the position of the event point relative to the bounds of
+ This is the position of the \l eventPoint relative to the bounds of
the \l {PointerHandler::parent} {parent}.
*/
@@ -232,7 +232,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
\brief The position within the scene
- This is the position of the event point relative to the bounds of the Qt
+ This is the position of the \l eventPoint relative to the bounds of the Qt
Quick scene (typically the whole window).
*/
@@ -290,7 +290,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
This is a velocity vector pointing in the direction of movement, in logical
pixels per second. It has x and y components, at least one of which will be
nonzero when this point is in motion. It holds the average recent velocity:
- how fast and in which direction the event point has been moving recently.
+ how fast and in which direction the \l eventPoint has been moving recently.
\sa QtQuick::TouchPoint::velocity, QEventPoint::velocity
*/
@@ -345,6 +345,13 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\sa QtQuick::TouchPoint::ellipseDiameters, QEventPoint::ellipseDiameters
*/
+/*!
+ \readonly
+ \qmlproperty PointerDevice QtQuick::handlerPoint::device
+
+ This property holds the device that the point (and its event) came from.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickhandlerpoint_p.cpp"
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index 6e2b25a605..c5067881a6 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -67,7 +67,7 @@ Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
The \l cursorShape property allows changing the cursor whenever
\l hovered changes to \c true.
- \sa MouseArea, PointHandler
+ \sa MouseArea, PointHandler, {Pointer Handlers Example}
*/
QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
@@ -302,6 +302,13 @@ void QQuickHoverHandler::setHovered(bool hovered)
\sa Qt::CursorShape, QQuickItem::cursor()
*/
+/*!
+ \internal
+ \qmlproperty flags HoverHandler::dragThreshold
+
+ This property is not used in HoverHandler.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickhoverhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index c2d7df30fc..cb45509de0 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -92,7 +92,7 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
but if it's a disallowed number, it does not scale or rotate
its \l target, and the \l active property remains \c false.
- \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount()
+ \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount(), {Pointer Handlers Example}
*/
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
@@ -462,6 +462,13 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
}
/*!
+ \internal
+ \qmlproperty flags QtQuick::PinchHandler::acceptedButtons
+
+ This property is not used in PinchHandler.
+*/
+
+/*!
\readonly
\qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
@@ -508,6 +515,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
The translation of the gesture \l centroid. It is \c (0, 0) when the
gesture begins.
+
+ \note On some touchpads, such as on a \macos trackpad, native gestures do
+ not generate any translation values, and this property stays at \c (0, 0).
*/
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index e0f082b666..9fe214824f 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -175,7 +175,7 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes
By default, this property is set to
\l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
If you set it to an OR combination of device types, it will ignore events
- from non-matching events.
+ from non-matching \l {PointerDevice}{devices}.
For example, a control could be made to respond to mouse, touch, and stylus clicks
in some way, but delete itself if tapped with an eraser tool on a graphics tablet,
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 7921fae46d..ec3c972af3 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -92,7 +92,7 @@ QQuickPointerHandler::~QQuickPointerHandler()
\qmlproperty real PointerHandler::margin
The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which an event point can activate this handler. For example, on
+ item within which an \l eventPoint can activate this handler. For example, on
a PinchHandler where the \l {PointerHandler::target}{target} is also the
\c parent, it's useful to set this to a distance at least half the width
of a typical user's finger, so that if the \c parent has been scaled down
@@ -125,7 +125,7 @@ void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
\qmlproperty int PointerHandler::dragThreshold
\since 5.15
- The distance in pixels that the user must drag an event point in order to
+ The distance in pixels that the user must drag an \l eventPoint in order to
have it treated as a drag gesture.
The default value depends on the platform and screen resolution.
@@ -252,8 +252,8 @@ bool QQuickPointerHandler::isCursorShapeExplicitlySet() const
The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
The \a transition (verb) tells what happened.
- The \a point (object) is the point that was grabbed or ungrabbed.
- EventPoint has the sole responsibility to call this function.
+ The \a point (object) is the \l eventPoint that was grabbed or ungrabbed.
+ QQuickDeliveryAgent calls this function.
The Input Handler must react in whatever way is appropriate, and must
emit the relevant signals (for the benefit of QML code).
A subclass is allowed to override this virtual function, but must always
@@ -707,9 +707,9 @@ bool QQuickPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEv
\qmlproperty bool QtQuick::PointerHandler::active
This holds true whenever this Input Handler has taken sole responsibility
- for handing one or more EventPoints, by successfully taking an exclusive
- grab of those points. This means that it is keeping its properties
- up-to-date according to the movements of those Event Points and actively
+ for handing one or more \l {eventPoint}{eventPoints}, by successfully taking an
+ exclusive grab of those points. This means that it is keeping its properties
+ up-to-date according to the movements of those eventPoints and actively
manipulating its \l target (if any).
*/
void QQuickPointerHandler::setActive(bool active)
@@ -733,7 +733,7 @@ void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *)
The \l Item which is the scope of the handler; the Item in which it was declared.
The handler will handle events on behalf of this Item, which means a
- pointer event is relevant if at least one of its event points occurs within
+ pointer event is relevant if at least one of its \l {eventPoint}{eventPoints} occurs within
the Item's interior. Initially \l [QML] {target} {target()} is the same, but it
can be reassigned.
@@ -744,7 +744,7 @@ void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *)
*/
/*!
- \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point)
+ \qmlsignal QtQuick::PointerHandler::grabChanged(PointerDevice::GrabTransition transition, eventPoint point)
This signal is emitted when the grab has changed in some way which is
relevant to this handler.
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index f4a3a1bb2d..94d1f2f75c 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -102,7 +102,8 @@ QT_BEGIN_NAMESPACE
PointHandler will not automatically manipulate the \c target item in any way.
You need to use bindings to make it react to the \l point.
- \note On macOS, PointHandler does not react to the trackpad by default.
+ \note On macOS, PointHandler does not react to multiple fingers on the
+ trackpad by default, although it does react to a pressed point (mouse position).
That is because macOS can provide either native gesture recognition, or raw
touchpoints, but not both. We prefer to use the native gesture event in
PinchHandler, so we do not want to disable it by enabling touch. However
@@ -111,7 +112,7 @@ QT_BEGIN_NAMESPACE
want to react to all the touchpoints but do not require the smooth
native-gesture experience.
- \sa MultiPointTouchArea
+ \sa MultiPointTouchArea, HoverHandler, {Pointer Handlers Example}
*/
QQuickPointHandler::QQuickPointHandler(QQuickItem *parent)
@@ -164,6 +165,127 @@ QVector2D QQuickPointHandler::translation() const
return QVector2D(point().position() - point().pressPosition());
}
+/*!
+ \qmlproperty flags PointHandler::acceptedButtons
+
+ The mouse buttons that can activate this PointHandler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ in which other buttons are pressed or held.
+
+ \snippet pointerHandlers/pointHandlerAcceptedButtons.qml 0
+
+ \note On a touchscreen, there are no buttons, so this property does not
+ prevent PointHandler from reacting to touchpoints.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedDevices
+
+ The types of pointing devices that can activate this PointHandler.
+
+ By default, this property is set to
+ \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandler.qml 1
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedPointerTypes
+
+ The types of pointing instruments (finger, stylus, eraser, etc.)
+ that can activate this PointHandler.
+
+ By default, this property is set to
+ \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandlerCanvasDrawing.qml 0
+
+ The \l {Pointer Handlers Example} includes a more complex example for
+ drawing on a Canvas with a graphics tablet.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedModifiers
+
+ If this property is set, PointHandler requires the given keyboard modifiers
+ to be pressed in order to react to \l {PointerEvent}{PointerEvents}, and
+ otherwise ignores them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ then PointHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers, one of which is
+ enabled only if the required keyboard modifier is pressed:
+
+ \snippet pointerHandlers/pointHandlerAcceptedModifiers.qml 0
+
+ If you set \c acceptedModifiers to an OR combination of modifier keys,
+ it means \e all of those modifiers must be pressed to activate the handler.
+
+ The available modifiers are as follows:
+
+ \value NoModifier No modifier key is allowed.
+ \value ShiftModifier A Shift key on the keyboard must be pressed.
+ \value ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value AltModifier An Alt key on the keyboard must be pressed.
+ \value MetaModifier A Meta key on the keyboard must be pressed.
+ \value KeypadModifier A keypad button must be pressed.
+ \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument).
+ A Mode_switch key on the keyboard must be pressed.
+ \value KeyboardModifierMask The handler does not care which modifiers are pressed.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*!
+ \readonly
+ \qmlproperty bool PointHandler::active
+
+ This holds \c true whenever the constraints are satisfied and this
+ PointHandler is reacting. This means that it is keeping its properties
+ up-to-date according to the movements of the \l {eventPoint}{eventPoints}
+ that satisfy the constraints.
+*/
+
+/*!
+ \internal
+ \qmlproperty flags PointHandler::dragThreshold
+
+ This property is not used in PointHandler.
+*/
+
+/*!
+ \qmlproperty real PointHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an \l eventPoint can activate this handler.
+
+ The default value is \c 0.
+
+ \snippet pointerHandlers/pointHandlerMargin.qml 0
+*/
+
+/*!
+ \qmlproperty real PointHandler::target
+
+ A property that can conveniently hold an Item to be manipulated or to show
+ feedback. Unlike other \l {Qt Quick Input Handlers}{Pointer Handlers},
+ PointHandler does not do anything with the \c target on its own: you
+ usually need to create reactive bindings to properties such as
+ \l SinglePointHandler::point and \l PointHandler::active. If you declare
+ an Item instance here, you need to explicitly set its \l {Item::}{parent},
+ because PointHandler is not an Item.
+
+ By default, it is the same as the \l {PointerHandler::}{parent}, the Item
+ within which the handler is declared.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickpointhandler_p.cpp"
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index 6c60d56c8b..22b475637a 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -209,7 +209,7 @@ QQuickHandlerPoint QQuickSinglePointHandler::point() const
\readonly
\qmlproperty HandlerPoint QtQuick::SinglePointHandler::point
- The event point currently being handled. When no point is currently being
+ The \l eventPoint currently being handled. When no point is currently being
handled, this object is reset to default values (all coordinates are 0).
*/
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 316293ea06..3abb366676 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -84,7 +84,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
QStyleHints::touchDoubleTapDistance() with touch, and the time between
taps must not exceed QStyleHints::mouseDoubleClickInterval().
- \sa MouseArea
+ \sa MouseArea, {Pointer Handlers Example}
*/
QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
@@ -182,7 +182,7 @@ void QQuickTapHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point
/*!
\qmlproperty real QtQuick::TapHandler::longPressThreshold
- The time in seconds that an event point must be pressed in order to
+ The time in seconds that an \l eventPoint must be pressed in order to
trigger a long press gesture and emit the \l longPressed() signal.
If the point is released before this time limit, a tap can be detected
if the \l gesturePolicy constraint is satisfied. The default value is
@@ -229,34 +229,77 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
The \c gesturePolicy also affects grab behavior as described below.
- \value TapHandler.DragThreshold
- (the default value) The event point must not move significantly.
- If the mouse, finger or stylus moves past the system-wide drag
- threshold (QStyleHints::startDragDistance), the tap gesture is
- canceled, even if the button or finger is still pressed. This policy
- can be useful whenever TapHandler needs to cooperate with other
- input handlers (for example \l DragHandler) or event-handling Items
- (for example QtQuick Controls), because in this case TapHandler
- will not take the exclusive grab, but merely a
- \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
-
- \value TapHandler.WithinBounds
- If the event point leaves the bounds of the \c parent Item, the tap
- gesture is canceled. The TapHandler will take the
- \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
- press, but will release the grab as soon as the boundary constraint
- is no longer satisfied.
-
- \value TapHandler.ReleaseWithinBounds
- At the time of release (the mouse button is released or the finger
- is lifted), if the event point is outside the bounds of the
- \c parent Item, a tap gesture is not recognized. This corresponds to
- typical behavior for button widgets: you can cancel a click by
- dragging outside the button, and you can also change your mind by
- dragging back inside the button before release. Note that it's
- necessary for TapHandler to take the
- \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
- and retain it until release in order to detect this gesture.
+ \table
+ \header
+ \li Constant
+ \li Description
+ \row
+ \li \c TapHandler.DragThreshold
+ \image pointerHandlers/tapHandlerOverlappingButtons.webp
+ Grab on press: \e passive
+ \li (the default value) The \l eventPoint must not move significantly.
+ If the mouse, finger or stylus moves past the system-wide drag
+ threshold (QStyleHints::startDragDistance), the tap gesture is
+ canceled, even if the device or finger is still pressed. This policy
+ can be useful whenever TapHandler needs to cooperate with other
+ input handlers (for example \l DragHandler) or event-handling Items
+ (for example \l {Qt Quick Controls}), because in this case TapHandler
+ will not take the exclusive grab, but merely a
+ \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
+ That is, \c DragThreshold is especially useful to \e augment
+ existing behavior: it reacts to tap/click/long-press even when
+ another item or handler is already reacting, perhaps even in a
+ different layer of the UI. The following snippet shows one
+ TapHandler as used in one component; but if we stack up two
+ instances of the component, you will see the handlers in both of them
+ react simultaneously when a press occurs over both of them, because
+ the passive grab does not stop event propagation:
+ \quotefromfile pointerHandlers/tapHandlerOverlappingButtons.qml
+ \skipto Item
+ \printuntil component Button
+ \skipto TapHandler
+ \printuntil }
+ \skipuntil Text {
+ \skipuntil }
+ \printuntil Button
+ \printuntil Button
+ \printuntil }
+
+ \row
+ \li \c TapHandler.WithinBounds
+ \image pointerHandlers/tapHandlerButtonWithinBounds.webp
+ Grab on press: \e exclusive
+ \li If the \l eventPoint leaves the bounds of the \c parent Item, the tap
+ gesture is canceled. The TapHandler will take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
+ press, but will release the grab as soon as the boundary constraint
+ is no longer satisfied.
+ \snippet pointerHandlers/tapHandlerButtonWithinBounds.qml 1
+
+ \row
+ \li \c TapHandler.ReleaseWithinBounds
+ \image pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
+ Grab on press: \e exclusive
+ \li At the time of release (the mouse button is released or the finger
+ is lifted), if the \l eventPoint is outside the bounds of the
+ \c parent Item, a tap gesture is not recognized. This corresponds to
+ typical behavior for button widgets: you can cancel a click by
+ dragging outside the button, and you can also change your mind by
+ dragging back inside the button before release. Note that it's
+ necessary for TapHandler to take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
+ and retain it until release in order to detect this gesture.
+ \snippet pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml 1
+ \endtable
+
+ The \l {Pointer Handlers Example} demonstrates some use cases for these.
+
+ \note If you find that TapHandler is reacting in cases that conflict with
+ some other behavior, the first thing you should try is to think about which
+ \c gesturePolicy is appropriate. If you cannot fix it by changing \c gesturePolicy,
+ some cases are better served by adjusting \l {PointerHandler::}{grabPermissions},
+ either in this handler, or in another handler that should \e prevent TapHandler
+ from reacting.
*/
void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
{
@@ -273,7 +316,7 @@ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gestureP
Holds true whenever the mouse or touch point is pressed,
and any movement since the press is compliant with the current
- \l gesturePolicy. When the event point is released or the policy is
+ \l gesturePolicy. When the \l eventPoint is released or the policy is
violated, \e pressed will change to false.
*/
void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
index dd2e6134c0..f1e40ba35c 100644
--- a/src/quick/handlers/qquickwheelhandler.cpp
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -81,7 +81,7 @@ Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel")
WheelHandler handles only a rotating mouse wheel by default; this
can be changed by setting acceptedDevices.
- \sa MouseArea, Flickable
+ \sa MouseArea, Flickable, {Pointer Handlers Example}
*/
QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index e13c0f6dae..89fafbe44d 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -288,7 +288,7 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
QPainter instead of the more expensive and likely less performing
JavaScript and Context2D approach.
- \sa Context2D QQuickPaintedItem
+ \sa Context2D, QQuickPaintedItem, {Pointer Handlers Example}
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -652,6 +652,10 @@ void QQuickCanvasItem::invalidateSceneGraph()
d->textureProvider = nullptr;
delete d->nodeTexture;
d->nodeTexture = nullptr;
+
+ // As we can expect(/hope) that the SG will be "good again", we can requestPaint ( which does 'markDirty(canvasWindow);' )
+ // Otherwise this Canvas will be "blank" when SG comes back
+ requestPaint();
}
void QQuickCanvasItem::schedulePolish()
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 3c3ca3ea2b..e050561ac5 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -2257,11 +2257,9 @@ void QQuickFlickable::setContentWidth(qreal w)
d->contentItem->setWidth(w);
d->hData.markExtentsDirty();
// Make sure that we're entirely in view.
- if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->hData.dragging) {
- d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
+ if (!d->pressed && !d->hData.moving && !d->vData.moving) {
d->fixupMode = QQuickFlickablePrivate::Immediate;
d->fixupX();
- d->hData.contentPositionChangedExternallyDuringDrag = false;
} else if (!d->pressed && d->hData.fixingUp) {
d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
d->fixupX();
@@ -2288,11 +2286,9 @@ void QQuickFlickable::setContentHeight(qreal h)
d->contentItem->setHeight(h);
d->vData.markExtentsDirty();
// Make sure that we're entirely in view.
- if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->vData.dragging) {
- d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
+ if (!d->pressed && !d->hData.moving && !d->vData.moving) {
d->fixupMode = QQuickFlickablePrivate::Immediate;
d->fixupY();
- d->vData.contentPositionChangedExternallyDuringDrag = false;
} else if (!d->pressed && d->vData.fixingUp) {
d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
d->fixupY();
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 4658f7c7cb..c748bb9f43 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -120,6 +120,7 @@ public:
dragStartOffset = 0;
fixingUp = false;
inOvershoot = false;
+ contentPositionChangedExternallyDuringDrag = false;
}
void markExtentsDirty() {
diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp
index a338985622..87112c8b93 100644
--- a/src/quick/items/qquickflipable.cpp
+++ b/src/quick/items/qquickflipable.cpp
@@ -39,7 +39,7 @@
#include "qquickflipable_p.h"
#include "qquickitem_p.h"
-
+#include "qquicktranslate_p.h"
#include <QtQml/qqmlinfo.h>
@@ -237,9 +237,20 @@ void QQuickFlipable::updatePolish()
d->updateSide();
}
-// determination on the currently visible side of the flipable
-// has to be done on the complete scene transform to give
-// correct results.
+/*! \internal
+ Flipable must use the complete scene transform to correctly determine the
+ currently visible side.
+
+ It must also be independent of camera distance, in case the contents are
+ too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
+ whereas QQuickRotation::applyTo(QMatrix4x4*) calls
+ QMatrix4x4::projectedRotate() which by default assumes the camera distance
+ is 1024 virtual pixels. So for example if contents inside Flipable are to
+ be flipped around the y axis, and are wider than 1024*2, some of the
+ rendering goes behind the "camera". That's expected for rendering (since we
+ didn't provide API to change camera distance), but not ok for deciding when
+ to flip.
+*/
void QQuickFlipablePrivate::updateSide()
{
Q_Q(QQuickFlipable);
@@ -249,8 +260,39 @@ void QQuickFlipablePrivate::updateSide()
sideDirty = false;
- QTransform sceneTransform;
- itemToParentTransform(sceneTransform);
+ QMatrix4x4 sceneTransform;
+
+ const qreal tx = x.value();
+ const qreal ty = y.value();
+ if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
+ sceneTransform.translate(tx, ty);
+
+ for (const auto *transform : std::as_const(transforms)) {
+ if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
+ // rotation is a special case: we want to call rotate() instead of projectedRotate()
+ const auto angle = rot->angle();
+ const auto axis = rot->axis();
+ if (!(qFuzzyIsNull(angle) || axis.isNull())) {
+ sceneTransform.translate(rot->origin());
+ sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
+ sceneTransform.translate(-rot->origin());
+ }
+ } else {
+ transform->applyTo(&sceneTransform);
+ }
+ }
+
+ const bool hasRotation = !qFuzzyIsNull(rotation());
+ const bool hasScale = !qFuzzyCompare(scale(), 1);
+ if (hasScale || hasRotation) {
+ QPointF tp = computeTransformOrigin();
+ sceneTransform.translate(tp.x(), tp.y());
+ if (hasScale)
+ sceneTransform.scale(scale(), scale());
+ if (hasRotation)
+ sceneTransform.rotate(rotation(), 0, 0, 1);
+ sceneTransform.translate(-tp.x(), -tp.y());
+ }
QPointF p1(0, 0);
QPointF p2(1, 0);
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 54e6d27be7..4426c26cab 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -494,6 +494,8 @@ qreal QQuickImage::paintedHeight() const
\note \e {Changing this property dynamically causes the image source to be reloaded,
potentially even from the network, if it is not in the disk cache.}
+
+ \sa {Pointer Handlers Example}
*/
/*!
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 9ed3eef2a6..596cd7d63a 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -327,8 +327,10 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
d->devicePixelRatio = 1.0;
bool updatedDevicePixelRatio = false;
- if (d->sourcesize.isValid() || isScalableImageFormat(d->url))
+ if (d->sourcesize.isValid()
+ || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
+ }
if (!updatedDevicePixelRatio) {
// (possible) local file: loadUrl and d->devicePixelRatio will be modified if
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index d1bd9877d2..dfa88c0a04 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -8003,7 +8003,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
Returns \c true if this item contains \a point, which is in local coordinates;
returns \c false otherwise. This is the same check that is used for
hit-testing a QEventPoint during event delivery, and is affected by
- containmentMask() if it is set.
+ \l containmentMask if it is set.
*/
/*!
Returns \c true if this item contains \a point, which is in local coordinates;
@@ -8011,7 +8011,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
This function can be overridden in order to handle point collisions in items
with custom shapes. The default implementation checks whether the point is inside
- containmentMask() if it is set, or inside the bounding box otherwise.
+ \l containmentMask() if it is set, or inside the bounding box otherwise.
\note This method is used for hit-testing each QEventPoint during event
delivery, so the implementation should be kept as lightweight as possible.
@@ -8040,10 +8040,10 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains() method. Its main use is currently to determine
+ \l contains() method. Its main use is currently to determine
whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
- By default the \l contains method will return true for any point
+ By default the \c contains() method will return true for any point
within the Item's bounding box. \c containmentMask allows for
more fine-grained control. For example, if a custom C++
QQuickItem subclass with a specialized contains() method
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 144e71cc88..fbffde1c48 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -196,4 +196,138 @@ void QQuickItemsModule::defineModule()
qt_quickitems_defineModule();
}
+/*!
+ \qmltype PointerEvent
+ \instantiates QPointerEvent
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointerEvent.
+
+ PointerEvent is the QML name of the QPointerEvent class.
+*/
+
+/*!
+ \qmltype PointerDevice
+ \instantiates QPointingDevice
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDevice.
+
+ PointerDevice is the QML name of the QPointingDevice class.
+ It has the same properties and enums as \l QPointingDevice.
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::deviceType
+
+ This property tells the type of device that generated a PointerEvent.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QInputDevice::DeviceType, PointerDeviceHandler::acceptedDevices
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::pointerType
+
+ This property tells what is interacting with the PointerDevice.
+
+ There is some redundancy between this property and \l deviceType.
+ For example, if a touchscreen is used, then \c deviceType is
+ \c TouchScreen and \c pointerType is \c Finger. But on a graphics
+ tablet, it's often possible for both ends of the stylus to be used,
+ and programs need to distinguish them.
+ \l PointerDeviceHandler::acceptedDevices and
+ \l PointerDeviceHandler::acceptedPointerTypes can be used in combination
+ to filter the subset of events that a particular handler should react to.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen.
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QPointingDevice::PointerType, PointerDeviceHandler::acceptedPointerTypes
+*/
+
+/*!
+ \qmlproperty int PointerDevice::maximumPoints
+
+ This property tells the maximum number of simultaneous touch points
+ (fingers) that can be detected.
+*/
+
+/*!
+ \qmlproperty int PointerDevice::buttonCount
+
+ This property tells the maximum number of on-device buttons that can be
+ detected.
+*/
+
+/*!
+ \qmltype pointingDeviceUniqueId
+ \instantiates QPointingDeviceUniqueId
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDeviceUniqueId.
+
+ pointingDeviceUniqueId is the QML name of the QPointingDeviceUniqueId class.
+*/
+
+/*!
+ \qmlproperty qint64 pointingDeviceUniqueId::numericId
+
+ This property gives the numeric ID of the \l PointerDevice, if available;
+ otherwise it is \c -1.
+*/
+
+/*!
+ \qmlproperty pointingDeviceUniqueId PointerDevice::uniqueId
+
+ This property may provide a unique ID for the device, if available. For
+ example, a graphics tablet stylus device may have a unique serial number.
+
+ \sa eventPoint, QEventPoint::uniqueId()
+*/
+
+/*!
+ \qmlsignal PointerDevice::grabChanged(QtObject grabber, enumeration transition, PointerEvent event, eventPoint point)
+
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object, which may be either an \l Item or an
+ \l {Qt Quick Input Handlers}{Input Handler}.
+
+ Valid values for \a transition are:
+
+ \value GrabExclusive
+ The \a grabber has taken primary responsibility for handling the \a point.
+ \value UngrabExclusive
+ The \a grabber has given up its previous exclusive grab.
+ \value CancelGrabExclusive
+ The exclusive grab of \a grabber has been taken over or cancelled.
+ \value GrabPassive
+ The \a grabber has acquired a passive grab, to monitor the \a point.
+ \value UngrabPassive
+ The \a grabber has given up its previous passive grab.
+ \value CancelGrabPassive
+ The previous passive grab has terminated abnormally.
+
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted.
+
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(), QPointerEvent::removePassiveGrabber()
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index b00e827d51..faa03fa9ae 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1968,6 +1968,9 @@ void QQuickItemViewPrivate::layout()
transitioner->resetTargetLists();
}
+ if (!currentItem)
+ updateCurrent(currentIndex);
+
runDelayedRemoveTransition = false;
inLayout = false;
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 786d663155..58f031a9eb 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2741,7 +2741,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
\c section.delegate holds the delegate component for each section. The
default \l {QQuickItem::z}{stacking order} of section delegate instances
- is \c 2.
+ is \c 2. If you declare a \c required property named "section" in it,
+ that property will contain the section's title.
\c section.labelPositioning determines whether the current and/or
next section labels stick to the start/end of the view, and whether
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index ea1aa7b0c9..1f5c55cffe 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -804,7 +804,7 @@ void QQuickLoader::componentComplete()
{
Q_D(QQuickLoader);
QQuickItem::componentComplete();
- if (active()) {
+ if (active() && (status() != Ready)) {
if (d->loadingFromSource)
d->createComponent();
d->load();
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index ce570a743c..52c830c14a 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -578,8 +578,13 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (d->pen && d->pen->isValid()) {
rectangle->setPenColor(d->pen->color());
- rectangle->setPenWidth(d->pen->width());
- rectangle->setAligned(d->pen->pixelAligned());
+ qreal penWidth = d->pen->width();
+ if (d->pen->pixelAligned()) {
+ qreal dpr = window() ? window()->effectiveDevicePixelRatio() : 1.0;
+ penWidth = qRound(penWidth * dpr) / dpr; // Ensures integer width after dpr scaling
+ }
+ rectangle->setPenWidth(penWidth);
+ rectangle->setAligned(false); // width rounding already done, so the Node should not do it
} else {
rectangle->setPenWidth(0);
}
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index c87ea5b1f4..2d48a39be8 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -537,7 +537,7 @@ void QQuickRenderControlPrivate::maybeUpdate()
Reimplemented in subclasses to return the real window this render control
is rendering into.
- If \a offset in non-null, it is set to the offset of the control
+ If \a offset is non-null, it is set to the offset of the control
inside the window.
\note While not mandatory, reimplementing this function becomes essential for
@@ -549,7 +549,7 @@ void QQuickRenderControlPrivate::maybeUpdate()
/*!
Returns the real window that \a win is being rendered to, if any.
- If \a offset in non-null, it is set to the offset of the rendering
+ If \a offset is non-null, it is set to the offset of the rendering
inside its window.
*/
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index 6ef868884a..5c2f113fdc 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -1387,7 +1387,7 @@ bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
+ v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index e35c766ab6..ddc43c10ed 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -352,6 +352,7 @@ void QQuickSpriteEngine::startAssemblingImage()
return;
m_loaded = false;
m_errorsPrinted = false;
+ m_sprites.clear();
//This could also trigger the start of the image loading in Sprites, however that currently happens in Sprite::setSource
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 2fed5b1cfc..9f259166df 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -688,8 +688,6 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
-static const int kEdgeIndexNotSet = -2;
-static const int kEdgeIndexAtEnd = -3;
static const char* kRequiredProperty = "_qt_isrequiredpropery_selected";
@@ -2340,11 +2338,6 @@ void QQuickTableViewPrivate::processRebuildTable()
if (rebuildState == RebuildState::LayoutTable) {
layoutAfterLoadingInitialTable();
- if (!moveToNextRebuildState())
- return;
- }
-
- if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
loadAndUnloadVisibleEdges();
if (!moveToNextRebuildState())
return;
@@ -2364,6 +2357,12 @@ void QQuickTableViewPrivate::processRebuildTable()
return;
}
+ if (rebuildState == RebuildState::UpdateContentSize) {
+ updateContentSize();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
const bool preload = (rebuildOptions & RebuildOption::All
&& reusableFlag == QQmlTableInstanceModel::Reusable);
@@ -2607,23 +2606,31 @@ void QQuickTableViewPrivate::loadInitialTable()
loadAndUnloadVisibleEdges();
}
-void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+void QQuickTableViewPrivate::updateContentSize()
{
- clearEdgeSizeCache();
- relayoutTableItems();
- syncLoadedTableRectFromLoadedTable();
-
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) {
+ const bool allColumnsLoaded = atTableEnd(Qt::LeftEdge) && atTableEnd(Qt::RightEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded) {
updateAverageColumnWidth();
updateContentWidth();
}
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) {
+ const bool allRowsLoaded = atTableEnd(Qt::TopEdge) && atTableEnd(Qt::BottomEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded) {
updateAverageRowHeight();
updateContentHeight();
}
updateExtents();
+}
+
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+{
+ clearEdgeSizeCache();
+ relayoutTableItems();
+ syncLoadedTableRectFromLoadedTable();
+
+ updateContentSize();
+
adjustViewportXAccordingToAlignment();
adjustViewportYAccordingToAlignment();
}
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 6fd05c5508..9893cd97e7 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -71,6 +71,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
+static const int kEdgeIndexNotSet = -2;
+static const int kEdgeIndexAtEnd = -3;
class FxTableItem;
class QQuickTableSectionSizeProviderPrivate;
@@ -201,9 +203,9 @@ public:
LoadInitalTable,
VerifyTable,
LayoutTable,
- LoadAndUnloadAfterLayout,
CancelOvershootBottomRight,
CancelOvershootTopLeft,
+ UpdateContentSize,
PreloadColumns,
PreloadRows,
MovePreloadedItemsToPool,
@@ -400,6 +402,9 @@ public:
int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
+ inline bool atTableEnd(Qt::Edge edge) {
+ return nextVisibleEdgeIndexAroundLoadedTable(edge) == kEdgeIndexAtEnd;
+ }
bool allColumnsLoaded();
bool allRowsLoaded();
inline int edgeToArrayIndex(Qt::Edge edge);
@@ -441,6 +446,8 @@ public:
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
+ void updateContentSize();
+
QTypeRevision resolveImportVersion();
void createWrapperModel();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 2df40bd272..ec1416768e 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -2986,6 +2986,7 @@ void QQuickText::hoverLeaveEvent(QHoverEvent *event)
/*!
\qmlproperty int QtQuick::Text::renderTypeQuality
+ \since 6.0
Override the default rendering type quality for this component. This is a low-level
customization which can be ignored in most cases. It currently only has an effect
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index b3401200c8..763607f730 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -209,10 +209,7 @@ void QQuickTextNodeEngine::addTextDecorations(const QVarLengthArray<TextDecorati
{
QRectF &rect = textDecoration.rect;
- rect.setY(qRound(rect.y()
- + m_currentLine.ascent()
- + (m_currentLine.leadingIncluded() ? m_currentLine.leading() : qreal(0.0f))
- + offset));
+ rect.setY(qRound(rect.y() + m_currentLine.ascent() + offset));
rect.setHeight(thickness);
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 4922148b27..19b508cecb 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -3838,7 +3838,7 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
graphics API based on the platform and other conditions, set \a api to
QSGRendererInterface::Unknown.
- \since 5.8
+ \since 6.0
*/
void QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi api)
{
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index c8ecf49767..cf369f55a6 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -406,8 +406,10 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
Geometry objects are constructed by default with DrawTriangleStrip as
the drawing mode.
- The attribute structure is assumed to be POD and the geometry object
- assumes this will not go away. There is no memory management involved.
+ \note \a attributes and the \l Attribute objects referenced by it must
+ stay valid for the entire lifetime of the QSGGeometry.
+ QSGGeometry stores a reference to \a attributes and does not delete
+ the \l Attribute objects.
*/
QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
diff --git a/src/quick/scenegraph/coreapi/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
index 8ee1220b0d..b79999c3ea 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture.cpp
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -716,6 +716,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting OpenGL texture objects.
\since 6.0
*/
@@ -816,6 +817,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Direct3D 11 texture objects.
\since 6.0
*/
@@ -876,6 +878,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Metal texture objects.
\since 6.0
*/
@@ -923,6 +926,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Vulkan image objects.
\since 6.0
*/
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 3b3f4c4abb..df0592cad1 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -508,9 +508,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p,
bool supportsSubPixelPositions = fontD->fontEngine->supportsHorizontalSubPixelPositions();
for (int i=0; i<glyphIndexes.size(); ++i) {
QPointF glyphPosition = glyphPositions.at(i) + position;
+ QFixedPoint fixedPointPosition = fixedPointPositions.at(i);
+
QFixed subPixelPosition;
if (supportsSubPixelPositions)
- subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x() * glyphCacheScaleX));
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(fixedPointPosition.x.toReal() * glyphCacheScaleX));
QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i),
QFixedPoint(subPixelPosition, 0));
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index e5903077a5..5575731675 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -117,6 +117,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
@@ -125,6 +131,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
for (int idx : *dirtyIndices) {
const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index def4248118..bdb47653df 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -394,11 +394,12 @@ void QQuickTransformAnimatorJob::Helper::sync()
wasSynced = true;
}
+ // We update the node before checking on dirty, as the node might have changed without the animator running
+ node = d->itemNode();
+
if (dirty == 0)
return;
- node = d->itemNode();
-
if (dirty & QQuickItemPrivate::Position) {
dx = item->x();
dy = item->y();
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 2cf52347a2..b8167ebed3 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -2401,8 +2401,11 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve
if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
skipDelivery.append(filteringParent);
- for (auto point : filteringParentTouchEvent.points())
- event->setExclusiveGrabber(point, filteringParent);
+ for (auto point : filteringParentTouchEvent.points()) {
+ const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
+ if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
+ event->setExclusiveGrabber(point, filteringParent);
+ }
return true;
} else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) &&
!filteringParent->acceptTouchEvents()) {
diff --git a/src/quick/util/qquicktransition.cpp b/src/quick/util/qquicktransition.cpp
index 46c3f04bc8..64394ee285 100644
--- a/src/quick/util/qquicktransition.cpp
+++ b/src/quick/util/qquicktransition.cpp
@@ -82,6 +82,11 @@ QT_BEGIN_NAMESPACE
values can be set to restrict the animations to only be applied when changing
from one particular state to another.
+ Top-level animations within a transition are run in parallel. To run them
+ sequentially, define them within a SequentialAnimation:
+
+ \snippet qml/transition-reversible.qml sequential animations
+
To define multiple Transitions, specify \l Item::transitions as a list:
\snippet qml/transitions-list.qml list of transitions
diff --git a/src/quickcontrols2/doc/snippets/qtquickcontrols2-swipedelegate.qml b/src/quickcontrols2/doc/snippets/qtquickcontrols2-swipedelegate.qml
index f7e27978c5..866e711481 100644
--- a/src/quickcontrols2/doc/snippets/qtquickcontrols2-swipedelegate.qml
+++ b/src/quickcontrols2/doc/snippets/qtquickcontrols2-swipedelegate.qml
@@ -45,6 +45,7 @@ ListView {
required property string sender
required property string title
+ required property int index
ListView.onRemove: SequentialAnimation {
PropertyAction {
@@ -76,8 +77,6 @@ ListView {
SwipeDelegate.onClicked: listView.model.remove(index)
- required property int index
-
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
diff --git a/src/quickcontrols2/doc/src/includes/container-currentindex.qdocinc b/src/quickcontrols2/doc/src/includes/container-currentindex.qdocinc
new file mode 100644
index 0000000000..9fcf43e168
--- /dev/null
+++ b/src/quickcontrols2/doc/src/includes/container-currentindex.qdocinc
@@ -0,0 +1,9 @@
+//! [file]
+When \1 is paired with another container such as \l \2, it is
+necessary to make a two-way binding between the
+\l {Container::}{currentIndex} property
+of each control. To do this without breaking bindings, avoid setting
+\c currentIndex directly, and instead use
+\l {Container::}{setCurrentIndex()}, for example.
+See \l {Managing the Current Index} for more information.
+//! [file]
diff --git a/src/quickcontrols2/doc/src/includes/qquickimaginestyle.qdocinc b/src/quickcontrols2/doc/src/includes/qquickimaginestyle.qdocinc
index f6fe5a970a..36297e9080 100644
--- a/src/quickcontrols2/doc/src/includes/qquickimaginestyle.qdocinc
+++ b/src/quickcontrols2/doc/src/includes/qquickimaginestyle.qdocinc
@@ -53,5 +53,7 @@
\note Due to a technical limitation, the path should not be named
\e "imagine" if it is relative to the \c qtquickcontrols2.conf file.
+ \li \c QT_QUICK_CONTROLS_IMAGINE_SMOOTH
+ \li Set to \c 1 to enable smooth scaling for 9-patch images.
\endtable
//! [env]
diff --git a/src/quickcontrols2/imagine/impl/qquickninepatchimage.cpp b/src/quickcontrols2/imagine/impl/qquickninepatchimage.cpp
index 54547cf1f2..934eb887d6 100644
--- a/src/quickcontrols2/imagine/impl/qquickninepatchimage.cpp
+++ b/src/quickcontrols2/imagine/impl/qquickninepatchimage.cpp
@@ -87,6 +87,9 @@ QList<qreal> QQuickNinePatchData::coordsForSize(qreal size) const
return coords;
}
+/*
+ Adds the 0 index coordinate if appropriate, and the one at "size".
+*/
void QQuickNinePatchData::fill(const QList<qreal> &coords, qreal size)
{
data.clear();
@@ -207,6 +210,31 @@ public:
QQuickNinePatchData yDivs;
};
+/*
+ Examines each pixel in a horizontal or vertical (if offset is equal to the image's width)
+ line, storing the start and end index ("coordinate") of each 9-patch line.
+
+ For instance, in the 7x3 (9x5 actual size) 9-patch image below, which has no horizontal
+ stretchable area, it would return {}:
+
+ +-----+
+ | |
+ +-----+
+
+ If indices 3 to 5 were marked, it would return {2, 5}:
+
+ xxx
+ +-----+
+ | |
+ +-----+
+
+ If indices 3 and 5 were marked, it would store {0, 2, 3, 4, 5, 7}:
+
+ x x
+ +-----+
+ | |
+ +-----+
+*/
static QList<qreal> readCoords(const QRgb *data, int from, int count, int offset, QRgb color)
{
int p1 = -1;
@@ -215,12 +243,16 @@ static QList<qreal> readCoords(const QRgb *data, int from, int count, int offset
int p2 = from + i * offset;
if (data[p2] == color) {
// colored pixel
- if (p1 == -1)
+ if (p1 == -1) {
+ // This is the start of a 9-patch line.
p1 = i;
+ }
} else {
// empty pixel
if (p1 != -1) {
+ // This is the end of a 9-patch line; add the start and end indices as coordinates...
coords << p1 << i;
+ // ... and reset p1 so that we can search for the next one.
p1 = -1;
}
}
@@ -228,6 +260,12 @@ static QList<qreal> readCoords(const QRgb *data, int from, int count, int offset
return coords;
}
+/*
+ Called whenever a 9-patch image is set as the image's source.
+
+ Reads the 9-patch lines from the source image and sets the
+ inset and padding properties accordingly.
+*/
void QQuickNinePatchImagePrivate::updatePatches()
{
if (ninePatch.isNull())
@@ -332,6 +370,8 @@ void QQuickNinePatchImagePrivate::updateInsets(const QList<qreal> &horizontal, c
QQuickNinePatchImage::QQuickNinePatchImage(QQuickItem *parent)
: QQuickImage(*(new QQuickNinePatchImagePrivate), parent)
{
+ Q_D(QQuickNinePatchImage);
+ d->smooth = qEnvironmentVariableIntValue("QT_QUICK_CONTROLS_IMAGINE_SMOOTH");
}
qreal QQuickNinePatchImage::topPadding() const
@@ -465,6 +505,8 @@ QSGNode *QQuickNinePatchImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNode
QSGTexture *texture = window()->createTextureFromImage(image);
patchNode->initialize(texture, sz * d->devicePixelRatio, image.size(), d->xDivs, d->yDivs, d->devicePixelRatio);
+ auto patchNodeMaterial = static_cast<QSGTextureMaterial *>(patchNode->material());
+ patchNodeMaterial->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
return patchNode;
}
diff --git a/src/quickcontrolstestutils/qtest_quickcontrols_p.h b/src/quickcontrolstestutils/qtest_quickcontrols_p.h
index ca04ee03f4..1c1ae27808 100644
--- a/src/quickcontrolstestutils/qtest_quickcontrols_p.h
+++ b/src/quickcontrolstestutils/qtest_quickcontrols_p.h
@@ -55,7 +55,7 @@
#include <QtQuickControls2/qquickstyle.h>
#include <QtQuickControls2/private/qquickstyle_p.h>
-static QStringList testStyles()
+inline QStringList testStyles()
{
// It's not enough to check if the name is empty, because since Qt 6
// we set an appropriate style for the platform if no style was specified.
@@ -66,7 +66,7 @@ static QStringList testStyles()
return QStringList(QQuickStyle::name());
}
-static int runTests(QObject *testObject, int argc, char *argv[])
+inline int runTests(QObject *testObject, int argc, char *argv[])
{
int res = 0;
QTest::qInit(testObject, argc, argv);
diff --git a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
index d376726bdb..0e2f96183f 100644
--- a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
+++ b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
@@ -2953,7 +2953,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
[triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState];
// bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus);
bool viewHasFocus = false;
- [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight];
+ [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleEmphasized : NSBackgroundStyleNormal];
d->setupNSGraphicsContext(cg, NO);
diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index 01869c79ad..c44ef2d4e1 100644
--- a/src/quickshapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -145,8 +145,12 @@ void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path)
void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color)
{
ShapePathData &d(m_sp[index]);
+ const bool wasTransparent = d.strokeColor.a == 0;
d.strokeColor = colorToColor4ub(color);
+ const bool isTransparent = d.strokeColor.a == 0;
d.syncDirty |= DirtyColor;
+ if (wasTransparent && !isTransparent)
+ d.syncDirty |= DirtyStrokeGeom;
}
void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w)
diff --git a/src/quickshapes/qquickshapesoftwarerenderer.cpp b/src/quickshapes/qquickshapesoftwarerenderer.cpp
index dc9ccb340d..d68fc22388 100644
--- a/src/quickshapes/qquickshapesoftwarerenderer.cpp
+++ b/src/quickshapes/qquickshapesoftwarerenderer.cpp
@@ -180,10 +180,8 @@ void QQuickShapeSoftwareRenderer::endSync(bool)
void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node)
{
- if (m_node != node) {
- m_node = node;
- m_accDirty |= DirtyList;
- }
+ m_node = node;
+ m_accDirty |= DirtyList;
}
void QQuickShapeSoftwareRenderer::updateNode()
diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp
index ab1ac9f12c..1d89948b4d 100644
--- a/src/quicktemplates2/qquickdrawer.cpp
+++ b/src/quicktemplates2/qquickdrawer.cpp
@@ -488,6 +488,12 @@ bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulo
bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp)
{
+ auto cleanup = qScopeGuard([this] {
+ popupItem->setKeepMouseGrab(false);
+ popupItem->setKeepTouchGrab(false);
+ pressPoint = QPointF();
+ touchId = -1;
+ });
if (pressPoint.isNull())
return false;
if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) {
@@ -547,14 +553,8 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
}
}
- bool wasGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
- popupItem->setKeepMouseGrab(false);
- popupItem->setKeepTouchGrab(false);
-
- pressPoint = QPointF();
- touchId = -1;
-
- return wasGrabbed;
+ // the cleanup() lambda will run before return
+ return popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
}
void QQuickDrawerPrivate::handleUngrab()
diff --git a/src/quicktemplates2/qquickheaderview.cpp b/src/quicktemplates2/qquickheaderview.cpp
index 3569f4c516..94d5635ad1 100644
--- a/src/quicktemplates2/qquickheaderview.cpp
+++ b/src/quicktemplates2/qquickheaderview.cpp
@@ -439,7 +439,7 @@ void QHeaderDataProxyModel::connectToModel()
if (m_model.isNull())
return;
connect(m_model, &QAbstractItemModel::headerDataChanged,
- [this](Qt::Orientation orient, int first, int last) {
+ this, [this](Qt::Orientation orient, int first, int last) {
if (orient != orientation())
return;
if (orient == Qt::Horizontal) {
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index 265d5ba84a..a3492fcb54 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -445,7 +445,7 @@ bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event)
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->points()) {
- if (!acceptTouch(point))
+ if (event->type() != QEvent::TouchEnd && !acceptTouch(point))
return blockInput(item, point.position());
switch (point.state()) {
diff --git a/src/quicktemplates2/qquickpopuppositioner.cpp b/src/quicktemplates2/qquickpopuppositioner.cpp
index e247ea6556..bbdc3848cd 100644
--- a/src/quicktemplates2/qquickpopuppositioner.cpp
+++ b/src/quicktemplates2/qquickpopuppositioner.cpp
@@ -162,14 +162,18 @@ void QQuickPopupPositioner::reposition()
// if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right)
if (p->allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) {
- const QRectF flipped(m_parentItem->mapToScene(QPointF(m_parentItem->width() - p->x - rect.width(), p->y)), rect.size());
+ const QPointF newTopLeft(m_parentItem->width() - p->x - rect.width(), p->y);
+ const QRectF flipped(m_parentItem->mapToItem(popupItem->parentItem(), newTopLeft),
+ rect.size());
if (flipped.intersected(bounds).width() > rect.intersected(bounds).width())
rect.moveLeft(flipped.left());
}
// if the popup doesn't fit vertically inside the window, try flipping it around (above <-> below)
if (p->allowVerticalFlip && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) {
- const QRectF flipped(m_parentItem->mapToScene(QPointF(p->x, m_parentItem->height() - p->y - rect.height())), rect.size());
+ const QPointF newTopLeft(p->x, m_parentItem->height() - p->y - rect.height());
+ const QRectF flipped(m_parentItem->mapToItem(popupItem->parentItem(), newTopLeft),
+ rect.size());
if (flipped.intersected(bounds).height() > rect.intersected(bounds).height())
rect.moveTop(flipped.top());
}
diff --git a/src/quicktemplates2/qquicksplitview.cpp b/src/quicktemplates2/qquicksplitview.cpp
index 07c5fd2baa..24e42d8258 100644
--- a/src/quicktemplates2/qquicksplitview.cpp
+++ b/src/quicktemplates2/qquicksplitview.cpp
@@ -255,7 +255,7 @@ QT_BEGIN_NAMESPACE
*/
Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
-Q_LOGGING_CATEGORY(qlcQQuickSplitViewMouse, "qt.quick.controls.splitview.mouse")
+Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
void QQuickSplitViewPrivate::updateFillIndex()
@@ -930,7 +930,7 @@ void QQuickSplitViewPrivate::updateHandleVisibilities()
void QQuickSplitViewPrivate::updateHoveredHandle(QQuickItem *hoveredItem)
{
- qCDebug(qlcQQuickSplitViewMouse) << "updating hovered handle after" << hoveredItem << "was hovered";
+ qCDebug(qlcQQuickSplitViewPointer) << "updating hovered handle after" << hoveredItem << "was hovered";
const int oldHoveredHandleIndex = m_hoveredHandleIndex;
m_hoveredHandleIndex = m_handleItems.indexOf(hoveredItem);
@@ -943,16 +943,16 @@ void QQuickSplitViewPrivate::updateHoveredHandle(QQuickItem *hoveredItem)
QQuickSplitHandleAttached *oldHoveredHandleAttached = qobject_cast<QQuickSplitHandleAttached*>(
qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(oldHoveredHandle, true));
QQuickSplitHandleAttachedPrivate::get(oldHoveredHandleAttached)->setHovered(false);
- qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << oldHoveredHandleIndex << "is no longer hovered";
+ qCDebug(qlcQQuickSplitViewPointer) << "handle item at index" << oldHoveredHandleIndex << "is no longer hovered";
}
if (m_hoveredHandleIndex != -1) {
QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(hoveredItem, true));
QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(true);
- qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << m_hoveredHandleIndex << "is now hovered";
+ qCDebug(qlcQQuickSplitViewPointer) << "handle item at index" << m_hoveredHandleIndex << "is now hovered";
} else {
- qCDebug(qlcQQuickSplitViewMouse) << "either there is no hovered item or" << hoveredItem << "is not a handle";
+ qCDebug(qlcQQuickSplitViewPointer) << "either there is no hovered item or" << hoveredItem << "is not a handle";
}
}
@@ -1021,7 +1021,7 @@ void QQuickSplitViewPrivate::handlePress(const QPointF &point)
setResizing(true);
- qCDebug(qlcQQuickSplitViewMouse).nospace() << "handled press -"
+ qCDebug(qlcQQuickSplitViewPointer).nospace() << "handled press -"
<< " left/top index=" << m_pressedHandleIndex << ","
<< " size before press=" << m_leftOrTopItemSizeBeforePress << ","
<< " item=" << leftOrTopItem
@@ -1411,27 +1411,50 @@ void QQuickSplitView::hoverLeaveEvent(QHoverEvent *event)
bool QQuickSplitView::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
Q_D(QQuickSplitView);
- qCDebug(qlcQQuickSplitViewMouse) << "childMouseEventFilter called with" << item << event;
-
- if (event->type() == QEvent::MouseButtonPress) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- const QPointF point = mapFromItem(item, mouseEvent->position());
- d->handlePress(point);
-
- // Keep the mouse grab if this item belongs to the handle,
- // otherwise this event can be stolen e.g. Flickable if we're inside it.
- if (d->m_pressedHandleIndex != -1)
- item->setKeepMouseGrab(true);
- }
- else if (event->type() == QEvent::MouseButtonRelease) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- const QPointF point = mapFromItem(item, mouseEvent->position());
- d->handleRelease(point);
- }
- else if (event->type() == QEvent::MouseMove) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- const QPointF point = mapFromItem(item, mouseEvent->position());
- d->handleMove(point);
+ qCDebug(qlcQQuickSplitViewPointer) << "childMouseEventFilter called with" << item << event;
+
+ if (Q_LIKELY(event->isPointerEvent())) {
+ auto *pointerEvent = static_cast<QPointerEvent *>(event);
+ const auto &eventPoint = pointerEvent->points().first();
+ const QPointF point = mapFromItem(item, eventPoint.position());
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ d->handlePress(point);
+ // Keep the mouse grab if this item belongs to the handle,
+ // otherwise this event can be stolen e.g. Flickable if we're inside it.
+ if (d->m_pressedHandleIndex != -1)
+ item->setKeepMouseGrab(true);
+ break;
+ case QEvent::MouseButtonRelease:
+ d->handleRelease(point);
+ break;
+ case QEvent::MouseMove:
+ d->handleMove(point);
+ break;
+ case QEvent::TouchBegin:
+ if (pointerEvent->pointCount() == 1) {
+ d->handlePress(point);
+ // We filter the event on behalf of item, but we want the item
+ // to be the exclusive grabber so that we can continue to filter
+ // touch events for it.
+ if (d->m_pressedHandleIndex != -1) {
+ item->setKeepTouchGrab(true);
+ pointerEvent->setExclusiveGrabber(eventPoint, item);
+ }
+ }
+ break;
+ case QEvent::TouchEnd:
+ if (pointerEvent->pointCount() == 1)
+ d->handleRelease(point);
+ break;
+ case QEvent::TouchUpdate:
+ if (pointerEvent->pointCount() == 1)
+ d->handleMove(point);
+ break;
+ default:
+ break;
+ }
}
// If this event belongs to the handle, filter it. (d->m_pressedHandleIndex != -1) means that
diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp
index 970f44b147..3277ba524f 100644
--- a/src/quicktemplates2/qquickswipeview.cpp
+++ b/src/quicktemplates2/qquickswipeview.cpp
@@ -70,6 +70,8 @@ QT_BEGIN_NAMESPACE
\l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove}
pages dynamically at run time.
+ \include container-currentindex.qdocinc {file} {SwipeView} {TabBar}
+
It is generally not advisable to add excessive amounts of pages to a
SwipeView. However, when the amount of pages grows larger, or individual
pages are relatively complex, it may be desirable to free up resources by
diff --git a/src/quicktemplates2/qquicktabbar.cpp b/src/quicktemplates2/qquicktabbar.cpp
index 68b54125ed..0297324e01 100644
--- a/src/quicktemplates2/qquicktabbar.cpp
+++ b/src/quicktemplates2/qquicktabbar.cpp
@@ -68,6 +68,8 @@ QT_BEGIN_NAMESPACE
items dynamically at run time. The items can be accessed using
\l {Container::}{itemAt()} or \l {Container::}{contentChildren}.
+ \include container-currentindex.qdocinc {file} {TabBar} {SwipeView}
+
\section2 Resizing Tabs
By default, TabBar resizes its buttons to fit the width of the control.
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 6d27e09780..d316d12588 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -664,6 +664,7 @@ QQuickWidget::QQuickWidget(QWidget *parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
+ setAttribute(Qt::WA_AcceptTouchEvents);
d_func()->init();
}
@@ -1638,9 +1639,23 @@ bool QQuickWidget::event(QEvent *e)
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
- case QEvent::TouchCancel:
+ case QEvent::TouchCancel: {
// Touch events only have local and global positions, no need to map.
- return QCoreApplication::sendEvent(d->offscreenWindow, e);
+ bool res = QCoreApplication::sendEvent(d->offscreenWindow, e);
+ if (e->isAccepted() && e->type() == QEvent::TouchBegin) {
+ // If the TouchBegin got accepted, then make sure all points that have
+ // an exclusive grabber are also accepted so that the widget code for
+ // delivering touch events make this widget an implicit grabber of those
+ // points.
+ QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
+ auto deliveredPoints = pointerEvent->points();
+ for (auto &point : deliveredPoints) {
+ if (pointerEvent->exclusiveGrabber(point))
+ point.setAccepted(true);
+ }
+ }
+ return res;
+ }
case QEvent::FocusAboutToChange:
return QCoreApplication::sendEvent(d->offscreenWindow, e);
diff --git a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
index 6f48cb85bd..06c0e898d3 100644
--- a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
+++ b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
@@ -243,46 +243,50 @@ void tst_qquickimageparticle::test_deformed()
void tst_qquickimageparticle::test_tabled()
{
- QQuickView* view = createView(testFileUrl("tabled.qml"), 600);
- QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system");
- ensureAnimTime(600, system->m_animation);
+ #if defined(Q_OS_LINUX)
+ QSKIP("Crashing on Ubuntu 22.04: QTBUG-107707");
+ #else
+ QQuickView* view = createView(testFileUrl("tabled.qml"), 600);
+ QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system");
+ ensureAnimTime(600, system->m_animation);
- QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
- if (d->t == -1)
- continue; //Particle data unused
+ QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
+ if (d->t == -1)
+ continue; //Particle data unused
- QCOMPARE(d->x, 0.f);
- QCOMPARE(d->y, 0.f);
- QCOMPARE(d->vx, 0.f);
- QCOMPARE(d->vy, 0.f);
- QCOMPARE(d->ax, 0.f);
- QCOMPARE(d->ay, 0.f);
- QCOMPARE(d->lifeSpan, 0.5f);
- QCOMPARE(d->size, 32.f);
- QCOMPARE(d->endSize, 32.f);
- QVERIFY(myFuzzyLEQ(d->t, ((qreal)system->timeInt/1000.0)));
- QCOMPARE(d->color.r, (uchar)255);
- QCOMPARE(d->color.g, (uchar)255);
- QCOMPARE(d->color.b, (uchar)255);
- QCOMPARE(d->color.a, (uchar)255);
- QCOMPARE(d->xx, 1.0f);
- QCOMPARE(d->xy, 0.0f);
- QCOMPARE(d->yy, 1.0f);
- QCOMPARE(d->yx, 0.0f);
- QCOMPARE(d->rotation, 0.0f);
- QCOMPARE(d->rotationVelocity, 0.0f);
- QCOMPARE(d->autoRotate, (uchar)0);
- QCOMPARE(d->animX, 0.0f);
- QCOMPARE(d->animY, 0.0f);
- QCOMPARE(d->animWidth, 1.0f);
- QCOMPARE(d->animHeight, 1.0f);
- QCOMPARE(d->frameDuration, 1.0f);
- QCOMPARE(d->frameCount, 1.0f);
- QCOMPARE(d->animT, -1.0f);
- //TODO: This performance level doesn't alter particleData, but goes straight to shaders. Find something to test
- }
- delete view;
+ QCOMPARE(d->x, 0.f);
+ QCOMPARE(d->y, 0.f);
+ QCOMPARE(d->vx, 0.f);
+ QCOMPARE(d->vy, 0.f);
+ QCOMPARE(d->ax, 0.f);
+ QCOMPARE(d->ay, 0.f);
+ QCOMPARE(d->lifeSpan, 0.5f);
+ QCOMPARE(d->size, 32.f);
+ QCOMPARE(d->endSize, 32.f);
+ QVERIFY(myFuzzyLEQ(d->t, ((qreal)system->timeInt/1000.0)));
+ QCOMPARE(d->color.r, (uchar)255);
+ QCOMPARE(d->color.g, (uchar)255);
+ QCOMPARE(d->color.b, (uchar)255);
+ QCOMPARE(d->color.a, (uchar)255);
+ QCOMPARE(d->xx, 1.0f);
+ QCOMPARE(d->xy, 0.0f);
+ QCOMPARE(d->yy, 1.0f);
+ QCOMPARE(d->yx, 0.0f);
+ QCOMPARE(d->rotation, 0.0f);
+ QCOMPARE(d->rotationVelocity, 0.0f);
+ QCOMPARE(d->autoRotate, (uchar)0);
+ QCOMPARE(d->animX, 0.0f);
+ QCOMPARE(d->animY, 0.0f);
+ QCOMPARE(d->animWidth, 1.0f);
+ QCOMPARE(d->animHeight, 1.0f);
+ QCOMPARE(d->frameDuration, 1.0f);
+ QCOMPARE(d->frameCount, 1.0f);
+ QCOMPARE(d->animT, -1.0f);
+ //TODO: This performance level doesn't alter particleData, but goes straight to shaders. Find something to test
+ }
+ delete view;
+ #endif
}
diff --git a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
index e8fe26cb5a..92565f55c1 100644
--- a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
@@ -20,6 +20,7 @@ qt6_add_qml_module(testCppTypes
SOURCES
commontypes.h
)
+qt_autogen_tools_initial_setup(testCppTypesplugin)
qt_internal_add_test(tst_qv4debugger
SOURCES
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
index 24633d9c6d..9037ed0c92 100644
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
@@ -393,9 +393,7 @@ void Test262Runner::loadTestExpectations()
return;
}
- int line = 0;
while (!file.atEnd()) {
- ++line;
QByteArray line = file.readLine().trimmed();
if (line.startsWith('#') || line.isEmpty())
continue;
@@ -440,9 +438,7 @@ void Test262Runner::updateTestExpectations()
QTemporaryFile updatedExpectations;
updatedExpectations.open();
- int line = 0;
while (!file.atEnd()) {
- ++line;
QByteArray originalLine = file.readLine();
QByteArray line = originalLine.trimmed();
if (line.startsWith('#') || line.isEmpty()) {
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 3f429fec22..d84aaaeba3 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -65,6 +65,7 @@ private slots:
void cppRegisteredSingletonDependency();
void cacheModuleScripts();
void reuseStaticMappings();
+ void invalidateSaveLoadCache();
private:
QDir m_qmlCacheDirectory;
@@ -550,6 +551,7 @@ void tst_qmldiskcache::recompileAfterChange()
CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
QScopedPointer<TypeVersion2> obj(qobject_cast<TypeVersion2*>(component.create()));
QVERIFY(!obj.isNull());
+ qDebug() << obj->property("x");
QVERIFY(QFileInfo(testCompiler.cacheFilePath).lastModified() > initialCacheTimeStamp);
}
}
@@ -1030,6 +1032,112 @@ void tst_qmldiskcache::reuseStaticMappings()
QCOMPARE(testCompiler.unitData(), data1);
}
+class AParent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int x MEMBER x)
+public:
+ AParent(QObject *parent = nullptr) : QObject(parent) {}
+ int x = 25;
+};
+
+class BParent : public QObject
+{
+ Q_OBJECT
+
+ // Insert y before x, to change the property index of x
+ Q_PROPERTY(int y MEMBER y)
+
+ Q_PROPERTY(int x MEMBER x)
+public:
+ BParent(QObject *parent = nullptr) : QObject(parent) {}
+ int y = 13;
+ int x = 25;
+};
+
+static QString writeTempFile(
+ const QTemporaryDir &tempDir, const QString &fileName, const char *contents) {
+ QFile f(tempDir.path() + '/' + fileName);
+ const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ Q_ASSERT(ok);
+ f.write(contents);
+ return f.fileName();
+};
+
+void tst_qmldiskcache::invalidateSaveLoadCache()
+{
+ qmlRegisterType<AParent>("Base", 1, 0, "Parent");
+ QQmlEngine e;
+
+ // If you store a CU to a .qmlc file at run time, the .qmlc file will contain
+ // alias entries with the encodedMetaPropertyIndex pre-resolved. That's in
+ // contrast to .qmlc files generated ahead of time. Exploit that to cause
+ // a need to recompile the file.
+
+ QTemporaryDir tempDir;
+ const QString fileName = writeTempFile(
+ tempDir, QLatin1String("a.qml"),
+ "import Base\nParent { id: self; property alias z: self.x }");
+ const QUrl url = QUrl::fromLocalFile(fileName);
+ waitForFileSystem();
+
+ {
+ QQmlComponent a(&e, url);
+ QVERIFY2(a.isReady(), qPrintable(a.errorString()));
+ QScopedPointer<QObject> ao(a.create());
+ QVERIFY(!ao.isNull());
+ AParent *ap = qobject_cast<AParent *>(ao.data());
+ QCOMPARE(ap->property("z").toInt(), ap->x);
+ }
+
+ QString errorString;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> oldUnit
+ = QV4::ExecutableCompilationUnit::create();
+ QVERIFY2(oldUnit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ // Produce a checksum mismatch.
+ e.clearComponentCache();
+ qmlClearTypeRegistrations();
+ qmlRegisterType<BParent>("Base", 1, 0, "Parent");
+
+ {
+ QQmlComponent b(&e, url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Make it recompile again.
+ e.clearComponentCache();
+ {
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly | QIODevice::Append);
+ file.write(" ");
+ }
+ waitForFileSystem();
+
+ {
+ QQmlComponent b(&e, url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Verify that the mapped unit data is actually different now.
+ // The cache should have been invalidated after all.
+ // So, now we should be able to load a freshly written CU.
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
+ QVERIFY2(unit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ QVERIFY(unit->unitData() != oldUnit->unitData());
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"
diff --git a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml
new file mode 100644
index 0000000000..edbb12c6e6
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml
@@ -0,0 +1,37 @@
+import QtQuick
+
+Item {
+
+ function f() {
+ var count = 0;
+ class Person {
+ constructor(name){
+ this._name = name;
+ }
+ }
+ class Employee extends Person {
+ constructor(name, age){
+ super(name);
+ this._name = name;
+ this._age = age;
+ ++count;
+ }
+
+ doWork(){}
+
+ /* do we get the comment? */ get name(){
+ return this._name.toUpperCase();
+ }
+
+ set name(newName){
+ if (newName) {
+ this._name = newName;
+ }
+ }
+
+ static get count(){
+ return count;
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml
new file mode 100644
index 0000000000..268859d3cc
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml
@@ -0,0 +1,45 @@
+import QtQuick
+
+Item {
+
+function f() {
+
+var count = 0;
+
+class Person {
+ constructor(name) {
+ this._name = name
+ }
+}
+
+class Employee extends Person{
+
+
+ constructor(name, age) {
+ super(name);
+ this._name = name;
+ this._age = age;
+ ++count;
+ }
+
+ doWork() {
+
+ }
+
+ get /* do we get the comment? */ name() {
+ return this._name.toUpperCase();
+ }
+
+ set name(newName){
+ if(newName){
+ this._name = newName;
+ }
+ }
+
+ static get count() { return count;}
+}
+
+
+}
+
+} \ No newline at end of file
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 65b423b919..843c078f8c 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -252,6 +252,9 @@ void TestQmlformat::testFormat_data()
<< "emptyObject.formatted.qml" << QStringList {};
QTest::newRow("arrow functions") << "arrowFunctions.qml"
<< "arrowFunctions.formatted.qml" << QStringList {};
+ QTest::newRow("ecmaScriptClassInQml")
+ << "ecmaScriptClassInQml.qml"
+ << "ecmaScriptClassInQml.formatted.qml" << QStringList {};
}
void TestQmlformat::testFormat()
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index 9ab6c69b9c..02a0347ac2 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -67,3 +67,4 @@ qt_add_qml_module(tst-qmltyperegistrar-with-dashes
SOURCES
foo.cpp foo.h
)
+qt_autogen_tools_initial_setup(tst-qmltyperegistrar-with-dashesplugin)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index aeb46a8274..27f83ca22f 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -300,4 +300,11 @@ void tst_qmltyperegistrar::methodReturnType()
QVERIFY(qmltypesData.contains("type: \"QQmlComponent\""));
}
+void tst_qmltyperegistrar::omitInvisible()
+{
+ // If it cannot resolve the type a QML_FOREIGN refers to, it should not generate anything.
+ QVERIFY(qmltypesData.contains(
+ R"(Component { file: "tst_qmltyperegistrar.h"; name: "Invisible"; accessSemantics: "none" })"));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index 7cfce1c2be..1372c32d02 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -398,6 +398,17 @@ public:
Q_INVOKABLE QQmlComponent *createAThing(int) { return nullptr; }
};
+class Invisible : public QObject
+{
+};
+
+struct InvisibleForeign
+{
+ Q_GADGET
+ QML_FOREIGN(Invisible)
+ QML_NAMED_ELEMENT(Invisible)
+};
+
class tst_qmltyperegistrar : public QObject
{
Q_OBJECT
@@ -429,6 +440,7 @@ private slots:
void namespacesAndValueTypes();
void derivedFromForeignPrivate();
void methodReturnType();
+ void omitInvisible();
private:
QByteArray qmltypesData;
diff --git a/tests/auto/qml/qqmlconnections/data/invalidTarget.qml b/tests/auto/qml/qqmlconnections/data/invalidTarget.qml
new file mode 100644
index 0000000000..23599649ec
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/invalidTarget.qml
@@ -0,0 +1,31 @@
+import QtQml
+
+QtObject {
+ id: root
+ objectName: button.objectName
+
+ property QtObject b: QtObject {
+ objectName: "button"
+ id: button
+ signal clicked
+ }
+
+ property Connections c: Connections {
+ id: connections
+ target: null
+ function onClicked() { button.destroy(); }
+ }
+
+ property Timer t: Timer {
+ interval: 10
+ running: true
+ onTriggered: {
+ root.objectName = connections.target.objectName
+ }
+ }
+
+ Component.onCompleted: {
+ connections.target = button;
+ button.clicked()
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
index 1cdf458c0d..a28a8dd9dd 100644
--- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
+++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
@@ -78,6 +78,7 @@ private slots:
void noAcceleratedGlobalLookup();
void bindToPropertyWithUnderscoreChangeHandler();
+ void invalidTarget();
private:
QQmlEngine engine;
@@ -490,6 +491,25 @@ void tst_qqmlconnections::bindToPropertyWithUnderscoreChangeHandler()
QVERIFY(root->property("success").toBool());
}
+void tst_qqmlconnections::invalidTarget()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("invalidTarget.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> root {component.create()};
+ QVERIFY(root);
+ QCOMPARE(root->objectName(), QStringLiteral("button"));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(
+ url.toString()
+ + QLatin1String(":5:5: TypeError: Cannot read property 'objectName' of null")));
+ QTRY_VERIFY(root->objectName().isEmpty());
+}
+
QTEST_MAIN(tst_qqmlconnections)
#include "tst_qqmlconnections.moc"
diff --git a/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml b/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml
new file mode 100644
index 0000000000..fdf5f4ea11
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+ id: self
+ property font myFont
+ property int notMyFont
+
+ property var object
+
+ function callWithFont() {
+ object.method_gadget(myFont) // should be fine
+ }
+ function callWithInt() {
+ object.method_gadget(123)
+ }
+ function callWithInt2() {
+ object.method_gadget(notMyFont)
+ }
+ function callWithNull() {
+ object.method_gadget(null)
+ }
+ function callWithAllowedNull() {
+ object.method_qobject(null)
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/readOnlyBindable.qml b/tests/auto/qml/qqmlecmascript/data/readOnlyBindable.qml
new file mode 100644
index 0000000000..116036c9ff
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/readOnlyBindable.qml
@@ -0,0 +1,7 @@
+import Qt.test
+import QtQuick
+
+ReadOnlyBindable {
+ property int v: 12
+ x: v
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index 87d8ed0c1f..cd83702d3a 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -560,6 +560,7 @@ void registerTypes()
qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver");
qmlRegisterType<Sender>("Qt.test", 1,0, "Sender");
+ qmlRegisterTypesAndRevisions<ReadOnlyBindable>("Qt.test", 1);
}
#include "testtypes.moc"
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index acc066fddd..a3df73972e 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -833,12 +833,38 @@ public:
Q_INVOKABLE void method_unknown(NonRegisteredType) { invoke(28); }
+ Q_PROPERTY(QFont someFont READ someFont WRITE setSomeFont NOTIFY someFontChanged);
+ QFont someFont() { return m_someFont; }
+ void setSomeFont(const QFont &f)
+ {
+ if (f == m_someFont)
+ return;
+ m_someFont = f;
+ emit someFontChanged();
+ }
+ Q_INVOKABLE void method_gadget(QFont f)
+ {
+ invoke(40);
+ m_actuals << f;
+ }
+ Q_INVOKABLE void method_qobject(QObject *o)
+ {
+ invoke(41);
+ m_actuals << QVariant::fromValue(o);
+ }
+
private:
friend class MyInvokableBaseObject;
void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;}
int m_invoked;
bool m_invokedError;
QVariantList m_actuals;
+
+ QFont m_someFont;
+
+public:
+Q_SIGNALS:
+ void someFontChanged();
};
MyInvokableBaseObject::~MyInvokableBaseObject() {}
@@ -1847,6 +1873,25 @@ public slots:
int slot1(int i, int j, int k) {return i+j+k;}
};
+class ReadOnlyBindable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int x READ x WRITE setX BINDABLE bindableX)
+ Q_OBJECT_BINDABLE_PROPERTY(ReadOnlyBindable, int, _xProp)
+
+public:
+ ReadOnlyBindable(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ setX(7);
+ }
+
+ int x() const { return _xProp.value(); }
+ void setX(int x) { _xProp.setValue(x); }
+ QBindable<int> bindableX() const { return &_xProp; }
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 2d38bf0524..f47c7132a2 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -428,6 +428,9 @@ private slots:
void functionAsDefaultArgument();
void internalClassParentGc();
+ void methodTypeMismatch();
+
+ void doNotCrashOnReadOnlyBindable();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -3331,6 +3334,19 @@ void tst_qqmlecmascript::callQtInvokables()
QJSValue callback = qvariant_cast<QJSValue>(o->actuals().at(1));
QVERIFY(!callback.isNull());
QVERIFY(callback.isCallable());
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_gadget(object.someFont)",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 40);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant(o->someFont()));
+
+ o->reset();
+ QVERIFY(EVALUATE_ERROR("object.method_gadget(123)"));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), -1);
+ QCOMPARE(o->actuals(), QVariantList());
}
void tst_qqmlecmascript::resolveClashingProperties()
@@ -9978,6 +9994,85 @@ void tst_qqmlecmascript::internalClassParentGc()
QCOMPARE(root->objectName(), "3");
}
+void tst_qqmlecmascript::methodTypeMismatch()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("methodTypeMismatch.qml"));
+
+ QScopedPointer<MyInvokableObject> object(new MyInvokableObject());
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY2(o, qPrintable(component.errorString()));
+ o->setProperty("object", QVariant::fromValue(object.get()));
+
+ auto mo = o->metaObject();
+ QVERIFY(mo);
+
+ auto method = mo->method(mo->indexOfMethod("callWithFont()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals(), QVariantList() << QVariant(object->someFont()));
+
+ QRegularExpression argumentConversionErrorMatcher("Could not convert argument 0");
+ QRegularExpression argumentConversionErrorMatcher2(".*/methodTypeMismatch.qml");
+ QRegularExpression typeErrorMatcher(
+ ".*/methodTypeMismatch\\.qml:..: TypeError: Passing incompatible arguments to C\\+\\+ "
+ "functions from JavaScript is not allowed.");
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithInt()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(123) !!!
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithInt2()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(0) !!!
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithNull()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(nullptr) !!!
+
+ // make sure that null is still accepted by functions accepting, e.g., a QObject*!
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithAllowedNull()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals(), QVariantList() << QVariant::fromValue((QObject *)nullptr));
+}
+
+void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("readOnlyBindable.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "setBinding: Could not set binding via bindable interface. "
+ "The QBindable is read-only.");
+#endif
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("x").toInt(), 7);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml
new file mode 100644
index 0000000000..d6dd2c9b90
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import qmldirtest
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ property color color: mybutton.color
+
+ MyButton {
+ id: mybutton
+ }
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/MyButton.qml
new file mode 100644
index 0000000000..cc6eb967da
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "blue"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/MyButton.qml
new file mode 100644
index 0000000000..5bf632c48d
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "yellow"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/MyButton.qml
new file mode 100644
index 0000000000..32db428c4f
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "green"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir
new file mode 100644
index 0000000000..ac68d9097d
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir
@@ -0,0 +1,5 @@
+module qmldirtest
+MyButton 1.0 qml/MyButton.qml
+MyButton 1.0 qml/+linux/MyButton.qml
+MyButton 1.0 qml/+macos/MyButton.qml
+
diff --git a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
index 4c99bcf9c8..fe55d8b056 100644
--- a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
+++ b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
@@ -47,7 +47,7 @@ private slots:
void basicTest();
void basicTestCached();
void applicationEngineTest();
-
+ void qmldirCompatibility();
};
void tst_qqmlfileselector::basicTest()
@@ -95,6 +95,23 @@ void tst_qqmlfileselector::applicationEngineTest()
QCOMPARE(object->property("value").toString(), QString("selected"));
}
+void tst_qqmlfileselector::qmldirCompatibility()
+{
+ QQmlApplicationEngine engine;
+ engine.addImportPath(dataDirectory());
+ engine.load(testFileUrl("qmldirtest/main.qml"));
+ QVERIFY(!engine.rootObjects().isEmpty());
+ QObject *object = engine.rootObjects().at(0);
+ auto color = object->property("color").value<QColor>();
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+ QCOMPARE(color, QColorConstants::Svg::blue);
+#elif defined(Q_OS_DARWIN)
+ QCOMPARE(color, QColorConstants::Svg::yellow);
+#else
+ QCOMPARE(color, QColorConstants::Svg::green);
+#endif
+}
+
QTEST_MAIN(tst_qqmlfileselector)
#include "tst_qqmlfileselector.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/MyRectangle.qml b/tests/auto/qml/qqmllanguage/data/MyRectangle.qml
new file mode 100644
index 0000000000..4d5e7c6c8d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/MyRectangle.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ property alias rectangle1AnchorsleftMargin: rectangle1.anchors.leftMargin
+
+ Rectangle {
+ id: rectangle1
+ anchors.leftMargin: 250
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.19.qml b/tests/auto/qml/qqmllanguage/data/alias.19.qml
new file mode 100644
index 0000000000..a96c0c694d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.19.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Item {
+ id: myThing
+ width: 1920
+
+ MyRectangle {
+ rectangle1AnchorsleftMargin: myThing.width / 2
+ Component.onCompleted: myThing.height = rectangle1AnchorsleftMargin
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 06b2738c9e..047df2f6d0 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -2257,6 +2257,16 @@ void tst_qqmllanguage::aliasProperties()
QQmlComponent component(&engine, testFileUrl("alias.18.qml"));
VERIFY_ERRORS("alias.18.errors.txt");
}
+
+ // Binding on deep alias
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.19.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("height").toInt(), 960);
+ }
}
// QTBUG-13374 Test that alias properties and signals can coexist
diff --git a/tests/auto/qml/qqmllistmodel/data/protectQObjectFromGC.qml b/tests/auto/qml/qqmllistmodel/data/protectQObjectFromGC.qml
new file mode 100644
index 0000000000..43b375b681
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/protectQObjectFromGC.qml
@@ -0,0 +1,21 @@
+import QtQml
+import QtQml.Models
+
+ListModel {
+ id: filesModel
+ property Component testComponent: Component {
+ id: testComponent
+ QtObject {
+ required property string name
+ }
+ }
+ Component.onCompleted: {
+ filesModel.clear()
+ for(let i = 0; i < 10; i++) {
+ filesModel.append({
+ path: testComponent.createObject(null, { name: "" + i })
+ })
+ }
+ gc()
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 37b0f6f1fe..fbdf6d90f3 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -139,6 +139,7 @@ private slots:
void listElementWithTemplateString();
void destroyComponentObject();
void objectOwnershipFlip();
+ void protectQObjectFromGC();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -1882,7 +1883,7 @@ void tst_qqmllistmodel::destroyComponentObject()
Q_RETURN_ARG(QVariant, retVal));
QVERIFY(retVal.toBool());
QTRY_VERIFY(created.isNull());
- QTRY_VERIFY(list->get(0).property("obj").isUndefined());
+ QTRY_VERIFY(list->get(0).property("obj").isNull());
QCOMPARE(list->count(), 1);
}
@@ -1920,6 +1921,25 @@ void tst_qqmllistmodel::objectOwnershipFlip()
QCOMPARE(QJSEngine::objectOwnership(item.data()), QJSEngine::CppOwnership);
}
+void tst_qqmllistmodel::protectQObjectFromGC()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("protectQObjectFromGC.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QQmlListModel *listModel = qobject_cast<QQmlListModel *>(root.data());
+ QVERIFY(listModel);
+ QCOMPARE(listModel->count(), 10);
+
+ for (int i = 0; i < 10; ++i) {
+ QObject *element = qjsvalue_cast<QObject *>(listModel->get(i).property("path"));
+ QVERIFY(element);
+ QCOMPARE(element->property("name").toString(), QString::number(i));
+ }
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index be493484ee..f726855727 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -72,6 +72,8 @@ private slots:
void enumsInRecursiveImport_data();
void enumsInRecursiveImport();
+
+ void clearPropertyCaches();
};
class TestType : public QObject
@@ -734,6 +736,24 @@ void tst_qqmlmetatype::enumsInRecursiveImport()
QTRY_COMPARE(obj->property("color").toString(), QString("green"));
}
+void tst_qqmlmetatype::clearPropertyCaches()
+{
+ qmlClearTypeRegistrations();
+ qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "A");
+
+ QQmlRefPointer<QQmlPropertyCache> oldCache(
+ QQmlMetaType::propertyCache(&TestType::staticMetaObject));
+ QVERIFY(oldCache);
+
+ qmlClearTypeRegistrations();
+ qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "B");
+ QQmlRefPointer<QQmlPropertyCache> newCache(
+ QQmlMetaType::propertyCache(&TestType::staticMetaObject));
+ QVERIFY(newCache);
+
+ QVERIFY(oldCache.data() != newCache.data());
+}
+
QTEST_MAIN(tst_qqmlmetatype)
#include "tst_qqmlmetatype.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST
deleted file mode 100644
index 9cc6783375..0000000000
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST
+++ /dev/null
@@ -1,4 +0,0 @@
-# QTBUG-103072
-[gesturePolicyDragWithinBounds]
-android
-
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
index 33fffd4cb1..00d13f31f4 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_angle_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -21,6 +22,7 @@ CanvasTestCase {
}
function test_angle_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -36,6 +38,7 @@ CanvasTestCase {
}
function test_angle_3(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -53,6 +56,7 @@ CanvasTestCase {
}
function test_angle_4(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -71,6 +75,7 @@ CanvasTestCase {
}
function test_angle_5(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -90,6 +95,7 @@ CanvasTestCase {
function test_angle_6(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -110,6 +116,7 @@ CanvasTestCase {
function test_empty(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -125,6 +132,7 @@ CanvasTestCase {
}
function test_nonempty(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -141,6 +149,7 @@ CanvasTestCase {
}
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -199,6 +208,7 @@ CanvasTestCase {
}
function test_end(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -216,6 +226,7 @@ CanvasTestCase {
}
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -234,6 +245,7 @@ CanvasTestCase {
function test_scale_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -270,6 +282,7 @@ CanvasTestCase {
function test_scale_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -296,6 +309,7 @@ CanvasTestCase {
function test_selfintersect_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -316,6 +330,7 @@ CanvasTestCase {
function test_selfintersect_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -340,6 +355,7 @@ CanvasTestCase {
function test_shape_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -361,6 +377,7 @@ CanvasTestCase {
function test_shape_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -381,6 +398,7 @@ CanvasTestCase {
}
function test_shape_3(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -401,6 +419,7 @@ CanvasTestCase {
function test_shape_4(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -421,6 +440,7 @@ CanvasTestCase {
function test_shape_5(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -441,6 +461,7 @@ CanvasTestCase {
function test_twopie(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -488,6 +509,7 @@ CanvasTestCase {
function test_zero(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
index ef1b7a7b2a..81412d98c9 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
@@ -7,6 +7,7 @@ CanvasTestCase {
function test_coincide(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -44,6 +45,7 @@ CanvasTestCase {
}
function test_collinear(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -109,6 +111,7 @@ CanvasTestCase {
}
function test_subpath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -135,6 +138,7 @@ CanvasTestCase {
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -152,6 +156,7 @@ CanvasTestCase {
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -208,6 +213,7 @@ CanvasTestCase {
}
function test_scale(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -237,6 +243,7 @@ CanvasTestCase {
function test_shape(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -348,6 +355,7 @@ CanvasTestCase {
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -374,6 +382,7 @@ CanvasTestCase {
}
function test_zero(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
index 8a5a52cf1e..4ee24ad437 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_clearRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -17,6 +18,7 @@ CanvasTestCase {
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var composites = [ {compsite:"copy"},
{compsite:"destination-atop"},
@@ -47,6 +49,7 @@ CanvasTestCase {
function test_globalAlpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.globalAlpha, 1.0);
@@ -83,6 +86,7 @@ CanvasTestCase {
function test_operation(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.globalCompositeOperation = 'xor';
@@ -137,6 +141,7 @@ CanvasTestCase {
function test_solid(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = Qt.rgba(0, 1, 1, 1.0);
@@ -233,6 +238,7 @@ CanvasTestCase {
}
function test_transparent(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
@@ -330,6 +336,7 @@ CanvasTestCase {
function test_uncovered(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml b/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
index 58ea6d7f59..9e8c0785c3 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
verify(ctx.fillStyle, "#000000");
@@ -14,6 +15,7 @@ CanvasTestCase {
}
function test_get(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#fa0';
@@ -23,6 +25,7 @@ CanvasTestCase {
}
function test_hex(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -37,6 +40,7 @@ CanvasTestCase {
}
function test_invalid(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#fa0';
@@ -60,6 +64,7 @@ CanvasTestCase {
}
function test_saverestore(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var old = ctx.fillStyle;
ctx.save();
@@ -75,6 +80,7 @@ CanvasTestCase {
}
function test_namedColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "red";
@@ -91,6 +97,7 @@ CanvasTestCase {
}
function test_rgba(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "rgb(-100, 300, 255)";
@@ -108,6 +115,7 @@ CanvasTestCase {
function test_hsla(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "hsla(120, 100%, 50%, 0.499)";
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml b/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
index ce1c27c6bc..213abdd622 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_fillRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml b/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
index 7c87d896fb..70e77c8760 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -18,6 +19,7 @@ CanvasTestCase {
function test_interpolate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -166,6 +168,7 @@ CanvasTestCase {
}
function test_radial(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -788,6 +791,7 @@ CanvasTestCase {
}
function test_linear(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
try { var err = false;
@@ -907,6 +911,7 @@ CanvasTestCase {
}
function test_object(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
var g1 = ctx.createLinearGradient(0, 0, 100, 0);
@@ -978,6 +983,7 @@ CanvasTestCase {
function test_conical(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
var g = ctx.createConicalGradient(10, 10, 50);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
index 1f695d7080..a1eb272cd0 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
@@ -11,12 +11,12 @@ CanvasTestCase {
canvas.loadImage('rgrg-256x256.png');
canvas.loadImage('ggrr-256x256.png');
canvas.loadImage('broken.png');
- while (!canvas.isImageLoaded('green.png'))
- wait(200);
+ tryVerify(function() { return canvas.isImageLoaded('green.png'); })
}
function test_3args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
ctx.reset();
@@ -35,6 +35,7 @@ CanvasTestCase {
}
function test_5args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -54,6 +55,7 @@ CanvasTestCase {
}
function test_9args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -118,6 +120,7 @@ CanvasTestCase {
}
function test_animated(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -125,6 +128,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -139,6 +143,7 @@ CanvasTestCase {
}
function test_self(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -170,6 +175,7 @@ CanvasTestCase {
function test_outsidesource(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -207,6 +213,7 @@ CanvasTestCase {
function test_null(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -220,15 +227,18 @@ CanvasTestCase {
function test_url(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
canvas.loadImage(testCase.green);
+ tryVerify(function() { return canvas.isImageLoaded(testCase.green); })
ctx.drawImage(testCase.green, 0, 0);
comparePixel(ctx, 0,0, 0,255,0,255,2);
}
function test_composite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -241,12 +251,14 @@ CanvasTestCase {
}
function test_path(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -260,6 +272,7 @@ CanvasTestCase {
function test_imageitem(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -268,6 +281,7 @@ CanvasTestCase {
function test_imageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -276,6 +290,7 @@ CanvasTestCase {
function test_wrongtype(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -293,6 +308,7 @@ CanvasTestCase {
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -606,6 +622,7 @@ CanvasTestCase {
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -661,6 +678,7 @@ CanvasTestCase {
function test_canvas(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -684,6 +702,7 @@ CanvasTestCase {
function test_broken(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -694,6 +713,7 @@ CanvasTestCase {
function test_alpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -706,6 +726,7 @@ CanvasTestCase {
}
function test_multiple_painting(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml b/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
index 76b99a765e..aab7a95589 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
@@ -11,6 +11,7 @@ CanvasTestCase {
skip("ctx.getImageData crashes on offscreen/minimal platforms");
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var size = 17
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
index ba7ff09728..e5d03e5db7 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.lineWidth, 1);
@@ -16,6 +17,7 @@ CanvasTestCase {
function test_cross(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -43,6 +45,7 @@ CanvasTestCase {
function test_join(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -306,6 +309,7 @@ CanvasTestCase {
}
function test_miter(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -481,6 +485,7 @@ CanvasTestCase {
}
function test_width(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -625,6 +630,7 @@ CanvasTestCase {
skip("line::test_cap crashes on Android, QTBUG-103257")
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -841,6 +847,7 @@ CanvasTestCase {
function test_lineDash(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#fff";
@@ -899,6 +906,7 @@ CanvasTestCase {
function test_lineDashReset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#ff0000";
@@ -930,6 +938,7 @@ CanvasTestCase {
function test_lineDashOffset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#fff";
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
index 60c782c8fa..ae08b58519 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -36,6 +37,7 @@ CanvasTestCase {
}
function test_beginPath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@@ -48,6 +50,7 @@ CanvasTestCase {
}
function test_closePath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -86,6 +89,7 @@ CanvasTestCase {
function test_isPointInPath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.arc(50, 25, 10, 0, Math.PI, false);
@@ -256,6 +260,7 @@ CanvasTestCase {
function test_fill(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -378,6 +383,7 @@ CanvasTestCase {
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -662,6 +668,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -784,6 +791,7 @@ CanvasTestCase {
function test_moveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -839,6 +847,7 @@ CanvasTestCase {
}
function test_lineTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -904,6 +913,7 @@ CanvasTestCase {
}
function test_bezierCurveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -1072,6 +1082,7 @@ CanvasTestCase {
}
function test_quadraticCurveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -1173,6 +1184,7 @@ CanvasTestCase {
}
function test_rect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -1371,6 +1383,7 @@ CanvasTestCase {
function test_clearRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -1383,6 +1396,7 @@ CanvasTestCase {
}
function test_fillRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.beginPath();
@@ -1397,6 +1411,7 @@ CanvasTestCase {
function test_strokeRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.beginPath();
@@ -1412,6 +1427,7 @@ CanvasTestCase {
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml b/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
index 1d7fd2c12f..e0a4d7ad23 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
@@ -6,36 +6,42 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_animated(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_image(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_modified(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_paint(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_repeat(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml b/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
index 281264ba92..ef908db92a 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
@@ -7,6 +7,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_createImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var imageData = ctx.createImageData(1, 1);
var imageDataValues = imageData.data;
@@ -20,24 +21,28 @@ CanvasTestCase {
}
function test_getImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_object(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_putImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_filters(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml b/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
index 2baaa072d0..14318b3f56 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
@@ -6,12 +6,14 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_blur(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -19,6 +21,7 @@ CanvasTestCase {
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -26,6 +29,7 @@ CanvasTestCase {
function test_composite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -33,6 +37,7 @@ CanvasTestCase {
function test_enable(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -40,36 +45,42 @@ CanvasTestCase {
function test_gradient(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_image(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_offset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_pattern(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_tranform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_state.qml b/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
index 18464def7c..ce02f9d7f7 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_bitmap(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -19,6 +20,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -35,6 +37,7 @@ CanvasTestCase {
}
function test_fillStyle(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
// Test that restore() undoes any modifications
@@ -56,6 +59,7 @@ CanvasTestCase {
}
function test_font(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -78,6 +82,7 @@ CanvasTestCase {
}
function test_globalAlpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -100,6 +105,7 @@ CanvasTestCase {
}
function test_globalCompositeOperation(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -122,6 +128,7 @@ CanvasTestCase {
}
function test_lineCap(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -144,6 +151,7 @@ CanvasTestCase {
}
function test_lineJoin(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -166,6 +174,7 @@ CanvasTestCase {
}
function test_lineWidth(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -188,6 +197,7 @@ CanvasTestCase {
}
function test_miterLimit(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -210,6 +220,7 @@ CanvasTestCase {
}
function test_path(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -225,6 +236,7 @@ CanvasTestCase {
}
function test_shadow(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -295,6 +307,7 @@ CanvasTestCase {
}
function test_stack(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -330,6 +343,7 @@ CanvasTestCase {
}
function test_strokeStyle(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -353,6 +367,7 @@ CanvasTestCase {
function test_text(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -392,6 +407,7 @@ CanvasTestCase {
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
index a3f1ab0a9b..0dc39b8b4c 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
@@ -8,6 +8,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.strokeStyle, "#000000")
@@ -17,6 +18,7 @@ CanvasTestCase {
}
function test_saverestore(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var old = ctx.strokeStyle;
ctx.save();
@@ -33,6 +35,7 @@ CanvasTestCase {
}
function test_namedColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "red";
@@ -50,6 +53,7 @@ CanvasTestCase {
}
function test_colorFromObjectToString(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -68,6 +72,7 @@ CanvasTestCase {
}
function test_withInvalidColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
index 2b39357bed..011fbd497b 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_svgpath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var svgs = [
// Absolute coordinates, explicit commands.
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_text.qml b/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
index bfc4067040..f7cd66890b 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
@@ -6,36 +6,42 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_baseLine(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_align(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_fill(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_font(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_measure(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml b/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
index b2f5b51fa5..bbfec6a106 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_order(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -20,6 +21,7 @@ CanvasTestCase {
}
function test_rotate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -90,6 +92,7 @@ CanvasTestCase {
}
function test_scale(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -182,6 +185,7 @@ CanvasTestCase {
}
function test_setTransform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -325,6 +329,7 @@ CanvasTestCase {
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -460,6 +465,7 @@ CanvasTestCase {
}
function test_translate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index a6fb0fcb59..51020672b7 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -3117,7 +3117,12 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966
} else if (newExtent >= 0) {
// ...or reduce the content size be be less than current (contentX, contentY) position
// This forces the content item to move.
- expectedContentPos = moveDelta;
+ // contentY: 150
+ // 320 - 150 = 170 pixels down to bottom
+ // Now reduce contentHeight to 200
+ // since we are at the bottom, and the flickable is 100 pixels tall, contentY must land
+ // at newExtent - 100.
+
if (isHorizontal) {
flickable->setContentWidth(newExtent);
} else {
@@ -3127,6 +3132,7 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966
// We therefore cannot scroll/flick it further down. Drag it up towards the top instead
// (by moving mouse down).
pos += moveDelta;
+ expectedContentPos = unitDelta * (newExtent - (isHorizontal ? flickable->width() : flickable->height()));
}
QTest::mouseMove(&window, pos);
diff --git a/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
new file mode 100644
index 0000000000..1b6a3f5018
--- /dev/null
+++ b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+Flipable {
+ id: flipable
+
+ property real angle: 0
+ width: 3840 // wider than 1024 * 2: part of it goes behind the camera while flipping
+ height: 2160
+
+ front: Rectangle {
+ width: parent.width
+ height: parent.height
+ color: "red"
+ anchors.centerIn: parent
+ }
+ back: Rectangle {
+ color: "yellow"
+ anchors.centerIn: parent
+ width: parent.width
+ height: parent.height
+ }
+ transform: Rotation {
+ id: rotation
+ origin.x: flipable.width / 2
+ origin.y: flipable.height / 2
+ axis.x: 0; axis.y: 1; axis.z: 0
+ angle: flipable.angle
+ }
+}
diff --git a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
index 7106041eda..55747a7cdd 100644
--- a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
+++ b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
@@ -52,6 +52,9 @@ private slots:
void QTBUG_9161_crash();
void QTBUG_8474_qgv_abort();
+ void flipRotationAngle_data();
+ void flipRotationAngle();
+
private:
QQmlEngine engine;
};
@@ -137,6 +140,31 @@ void tst_qquickflipable::QTBUG_8474_qgv_abort()
delete window;
}
+void tst_qquickflipable::flipRotationAngle_data()
+{
+ QTest::addColumn<int>("angle");
+ QTest::addColumn<QQuickFlipable::Side>("side");
+
+ QTest::newRow("89") << 89 << QQuickFlipable::Front;
+ QTest::newRow("91") << 91 << QQuickFlipable::Back;
+ QTest::newRow("-89") << -89 << QQuickFlipable::Front;
+ QTest::newRow("-91") << -91 << QQuickFlipable::Back;
+}
+
+void tst_qquickflipable::flipRotationAngle() // QTBUG-75954
+{
+ QFETCH(int, angle);
+ QFETCH(QQuickFlipable::Side, side);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("flip-y-axis-flipable.qml"));
+ QQuickFlipable *obj = qobject_cast<QQuickFlipable*>(c.create());
+ QVERIFY(obj != nullptr);
+ obj->setProperty("angle", angle);
+ QCOMPARE(obj->side(), side);
+ delete obj;
+}
+
QTEST_MAIN(tst_qquickflipable)
#include "tst_qquickflipable.moc"
diff --git a/tests/auto/quick/qquicklistview2/data/qtbug86744.qml b/tests/auto/quick/qquicklistview2/data/qtbug86744.qml
new file mode 100644
index 0000000000..c69a4f7ec6
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/qtbug86744.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQml.Models
+
+Item {
+ height: 200
+ width: 100
+ DelegateModel {
+ id: dm
+ model: 2
+ delegate: Item {
+ width: 100
+ height: 20
+ property bool isCurrent: ListView.isCurrentItem
+ }
+ }
+ ListView {
+ objectName: "listView"
+ model: dm
+ currentIndex: 1
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/qtbug98315.qml b/tests/auto/quick/qquicklistview2/data/qtbug98315.qml
new file mode 100644
index 0000000000..bf2ed857b1
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/qtbug98315.qml
@@ -0,0 +1,98 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQml.Models
+
+Item {
+ width: 500
+ height: 200
+
+ property list<QtObject> myModel: [
+ QtObject {
+ objectName: "Item 0"
+ property bool selected: true
+ },
+ QtObject {
+ objectName: "Item 1"
+ property bool selected: false
+ },
+ QtObject {
+ objectName: "Item 2"
+ property bool selected: false
+ },
+ QtObject {
+ objectName: "Item 3"
+ property bool selected: true
+ },
+ QtObject {
+ objectName: "Item 4"
+ property bool selected: true
+ },
+ QtObject {
+ objectName: "Item 5"
+ property bool selected: true
+ },
+ QtObject {
+ objectName: "Item 6"
+ property bool selected: false
+ }
+ ]
+
+ ListView {
+ objectName: "listView"
+ id: listview
+ width: 500
+ height: 200
+
+ focus: true
+ clip: true
+ spacing: 2
+ orientation: ListView.Horizontal
+ highlightMoveDuration: 300
+ highlightMoveVelocity: -1
+ preferredHighlightBegin: (500 - 100) / 2
+ preferredHighlightEnd: (500 + 100) / 2
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ cacheBuffer: 500
+ currentIndex: 1
+
+ model: DelegateModel {
+ id: delegateModel
+ filterOnGroup: "visible"
+ model: myModel
+ groups: [
+ DelegateModelGroup {
+ name: "visible"
+ includeByDefault: true
+ }
+ ]
+ delegate: Rectangle {
+ id: tile
+ objectName: model.modelData.objectName
+
+ width: 100
+ height: 100
+ border.width: 0
+ anchors.verticalCenter: parent.verticalCenter
+
+ visible: model.modelData.selected
+ Component.onCompleted: {
+ DelegateModel.inPersistedItems = true
+ DelegateModel.inVisible = Qt.binding(function () {
+ return model.modelData.selected
+ })
+ }
+
+ property bool isCurrent: ListView.isCurrentItem
+ color: isCurrent ? "red" : "green"
+
+ Text {
+ id: valueText
+ anchors.centerIn: parent
+ text: model.modelData.objectName
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index 2c701bd5dc..4c6f9c70e2 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -63,6 +63,8 @@ private slots:
void tapDelegateDuringFlicking();
void flickDuringFlicking_data();
void flickDuringFlicking();
+ void isCurrentItem_DelegateModel();
+ void isCurrentItem_NoRegressionWithDelegateModelGroups();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
@@ -423,6 +425,45 @@ void tst_QQuickListView2::delegateModelRefresh()
QTRY_VERIFY(engine.rootObjects().first()->property("done").toBool());
}
+void tst_QQuickListView2::isCurrentItem_DelegateModel()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("qtbug86744.qml"));
+ window->resize(640, 480);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+ QQuickListView* listView = window->rootObject()->findChild<QQuickListView*>("listView");
+ QVERIFY(listView);
+ QVariant value = listView->itemAtIndex(1)->property("isCurrent");
+ QVERIFY(value.toBool() == true);
+}
+
+void tst_QQuickListView2::isCurrentItem_NoRegressionWithDelegateModelGroups()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("qtbug98315.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+ QQuickListView* listView = window->rootObject()->findChild<QQuickListView*>("listView");
+ QVERIFY(listView);
+
+ QQuickItem *item3 = listView->itemAtIndex(1);
+ QVERIFY(item3);
+ QCOMPARE(item3->property("isCurrent").toBool(), true);
+
+ QObject *item0 = listView->itemAtIndex(0);
+ QVERIFY(item0);
+ QCOMPARE(item0->property("isCurrent").toBool(), false);
+
+ // Press left arrow key -> Item 1 should become current, Item 3 should not
+ // be current anymore. After a previous fix of QTBUG-86744 it was working
+ // incorrectly - see QTBUG-98315
+ QTest::keyPress(window.get(), Qt::Key_Left);
+
+ QTRY_COMPARE(item0->property("isCurrent").toBool(), true);
+ QCOMPARE(item3->property("isCurrent").toBool(), false);
+}
+
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index 620ada57db..067fa759d8 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -133,6 +133,7 @@ private slots:
void statusChangeOnlyEmittedOnce();
void setSourceAndCheckStatus();
+ void loadComponentWithStates();
void asyncLoaderRace();
void noEngine();
@@ -1518,6 +1519,26 @@ void tst_QQuickLoader::setSourceAndCheckStatus()
QCOMPARE(loader->status(), QQuickLoader::Null);
}
+void tst_QQuickLoader::loadComponentWithStates()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(QByteArray("import QtQuick\n"
+ "Loader {\n"
+ "id: loader\n"
+ "property int createdObjCount: 0\n"
+ "states: [ State { when: true; PropertyChanges { target: loader; sourceComponent: myComp } } ]\n"
+ "Component { id: myComp; Item { Component.onCompleted: { ++createdObjCount } } }\n"
+ "}" )
+ , dataDirectoryUrl());
+ QScopedPointer<QQuickLoader> loader(qobject_cast<QQuickLoader*>(component.create()));
+ QTest::qWait(200);
+ QTRY_VERIFY(loader != nullptr);
+ QVERIFY(loader->item());
+ QCOMPARE(static_cast<QQuickItem*>(loader.data())->childItems().size(), 1);
+ QCOMPARE(loader->property("createdObjCount").toInt(), 1);
+}
+
void tst_QQuickLoader::asyncLoaderRace()
{
QQmlApplicationEngine engine;
diff --git a/tests/auto/quickcontrols2/controls/data/tst_splitview.qml b/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
index 6004f9eb44..76174a3c8e 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
@@ -2575,4 +2575,26 @@ TestCase {
mouseMove(control, control.width - 100, control.height / 2)
verify(!targetHandle.SplitHandle.hovered)
}
+
+ function test_touch() {
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+
+ let touch = touchEvent(control)
+
+ let handles = findHandles(control)
+ let firstHandle = handles[0]
+ let handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
+ touch.press(0, control, handleCenter.x, handleCenter.y).commit()
+ verify(firstHandle.SplitHandle.pressed)
+
+ touch.move(0, control, handleCenter.x + 100, handleCenter.y).commit()
+ verify(firstHandle.SplitHandle.pressed)
+ let firstItem = control.itemAt(0)
+ compare(firstItem.width, 125)
+
+ touch.release(0, control, handleCenter.x + 100, handleCenter.y).commit()
+ verify(!firstHandle.SplitHandle.pressed)
+ compare(firstItem.width, 125)
+ }
}
diff --git a/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml b/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml
new file mode 100644
index 0000000000..23aa4ca3c2
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+Item {
+ width: 400; height: 400
+
+ Drawer {
+ edge: Qt.LeftEdge
+ height: 200
+ width: 200
+ modal: true
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
index 6b6713bdde..2c6d50a591 100644
--- a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
@@ -46,14 +46,16 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQuick/qquickview.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
-#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
#include <QtQuickTemplates2/private/qquickdrawer_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
@@ -121,6 +123,8 @@ private slots:
void topEdgeScreenEdge();
+ void touchOutsideOverlay();
+
private:
QScopedPointer<QPointingDevice> touchDevice;
};
@@ -1393,6 +1397,41 @@ void tst_QQuickDrawer::topEdgeScreenEdge()
QTRY_COMPARE(drawer->position(), 1.0);
}
+void tst_QQuickDrawer::touchOutsideOverlay() // QTBUG-103811
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("itemPartialOverlayModal.qml")));
+ auto *drawer = window.rootObject()->findChild<QQuickDrawer*>();
+ QVERIFY(drawer);
+ QSignalSpy openedSpy(drawer, &QQuickDrawer::opened);
+ QSignalSpy closedSpy(drawer, &QQuickDrawer::closed);
+
+ drawer->open();
+ QVERIFY(openedSpy.size() == 1 || openedSpy.wait());
+ QVERIFY(drawer->isOpened());
+
+ // tap-dance in bottom area beyond the overlay
+ QPoint p1(100, 250);
+ QPoint p2(300, 250);
+ QTest::touchEvent(&window, touchDevice.data()).press(1, p1);
+ p1 -= QPoint(1, 0);
+ QTest::touchEvent(&window, touchDevice.data()).move(1, p1).press(2, p2);
+ p2 -= QPoint(1, 0);
+ QTest::touchEvent(&window, touchDevice.data()).release(1, p1).move(2, p2);
+ QTest::touchEvent(&window, touchDevice.data()).press(1, p1).stationary(2);
+ QTest::touchEvent(&window, touchDevice.data()).release(1, p1).release(2, p2);
+ QQuickTouchUtils::flush(&window);
+
+ // tap the overlay to try to close the drawer
+ QVERIFY(drawer->closePolicy().testFlag(QQuickPopup::CloseOnReleaseOutside));
+ const QPoint p3(300, 100);
+ QTest::touchEvent(&window, touchDevice.data()).press(3, p3);
+ QTest::touchEvent(&window, touchDevice.data()).release(3, p3);
+ QQuickTouchUtils::flush(&window);
+ QVERIFY(closedSpy.size() == 1 || closedSpy.wait());
+ QCOMPARE(drawer->isOpened(), false);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickDrawer)
#include "tst_qquickdrawer.moc"
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/mirroredCombobox.qml b/tests/auto/quickcontrols2/qquickpopup/data/mirroredCombobox.qml
new file mode 100644
index 0000000000..ed48179bce
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickpopup/data/mirroredCombobox.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ contentItem.rotation: 180
+
+ ComboBox {
+ objectName: "first"
+ x: 100
+ y: 300 // is missing space, needs to unroll in the "mirrored" direction
+ model: ["First", "Second", "Third", "Fourth", "Fifth"]
+ }
+
+ ComboBox {
+ objectName: "second"
+ x: 200
+ y: 100 // has enough space to unroll
+ model: ["A", "B", "C"]
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/rotatedCombobox.qml b/tests/auto/quickcontrols2/qquickpopup/data/rotatedCombobox.qml
new file mode 100644
index 0000000000..df217be4b7
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickpopup/data/rotatedCombobox.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ contentItem.rotation: 90
+
+ ComboBox {
+ objectName: "first"
+ x: 100
+ y: 320 // is missing space, needs to unroll in the "mirrored" direction
+ model: ["First", "Second", "Third", "Fourth", "Fifth"]
+ }
+
+ ComboBox {
+ objectName: "second"
+ x: 200
+ y: 100 // has enough space to unroll
+ model: ["A", "B", "C"]
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
index 75d66335f1..d2cfda9375 100644
--- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
@@ -118,6 +118,8 @@ private slots:
void dimmerContainmentMask();
void shrinkPopupThatWasLargerThanWindow_data();
void shrinkPopupThatWasLargerThanWindow();
+ void mirroredCombobox();
+ void rotatedCombobox();
private:
static bool hasWindowActivation();
@@ -1878,6 +1880,155 @@ void tst_QQuickPopup::shrinkPopupThatWasLargerThanWindow()
.arg(popup->height()).arg(window->height())));
}
+void tst_QQuickPopup::mirroredCombobox()
+{
+#ifdef Q_OS_ANDROID
+ // Android screens might be pretty small, such that additional
+ // repositioning (apart from the mirroring) will happen to the
+ // popups and mess up the expected positions below.
+ QSKIP("Skipping test for Android.");
+#endif
+ QStringList nativeStyles;
+ nativeStyles.append("macOS");
+ nativeStyles.append("iOS");
+ nativeStyles.append("Windows");
+ if (nativeStyles.contains(QQuickStyle::name()))
+ QSKIP("Skipping test for native styles: they might rearrange their combobox the way they "
+ "want.");
+
+ QQuickControlsApplicationHelper helper(this, "mirroredCombobox.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ {
+ QQuickComboBox *comboBox = window->findChild<QQuickComboBox *>("first");
+ QVERIFY(comboBox);
+ QQuickPopup *popup = comboBox->popup();
+ QVERIFY(popup);
+ popup->open();
+ QTRY_COMPARE(popup->isVisible(), true);
+ const QPointF popupPos(popup->contentItem()->mapToItem(comboBox->parentItem(),
+ popup->contentItem()->position()));
+ const QSizeF popupSize(popup->contentItem()->size());
+
+ // ignore popup.{top,bottom}Padding() as not included in popup->contentItem()->size()
+ // some styles prefer to draw the popup "over" (in z-axis direction) the combobox to hide
+ // the combobox
+ const bool styleDrawsPopupOverCombobox =
+ comboBox->position().y() - popupSize.height() + comboBox->size().height()
+ == popupPos.y();
+ // some styles prefer to draw the popup below (in y-axis direction) the combobox
+ const bool styleDrawsPopupBelowCombobox =
+ comboBox->position().y() - popupSize.height() + comboBox->topPadding()
+ == popupPos.y();
+
+ QVERIFY(styleDrawsPopupOverCombobox || styleDrawsPopupBelowCombobox);
+
+ popup->close();
+ }
+
+ {
+ QQuickComboBox *comboBox = window->findChild<QQuickComboBox *>("second");
+ QVERIFY(comboBox);
+ QQuickPopup *popup = comboBox->popup();
+ QVERIFY(popup);
+ popup->open();
+ QTRY_COMPARE(popup->isVisible(), true);
+ const QPointF popupPos(popup->contentItem()->mapToItem(comboBox->parentItem(),
+ popup->contentItem()->position()));
+
+ // some styles prefer to draw the popup "over" (in z-axis direction) the combobox to hide
+ // the combobox
+ const bool styleDrawsPopupOverCombobox = comboBox->position().y() + comboBox->topPadding()
+ + popup->topPadding() + popup->bottomPadding()
+ == popupPos.y();
+ // some styles prefer to draw the popup above (in y-axis direction) the combobox
+ const bool styleDrawsPopupAboveCombobox =
+ comboBox->position().y() + comboBox->height() - comboBox->topPadding()
+ == popupPos.y();
+
+ QVERIFY(styleDrawsPopupOverCombobox || styleDrawsPopupAboveCombobox);
+
+ popup->close();
+ }
+}
+
+void tst_QQuickPopup::rotatedCombobox()
+{
+#ifdef Q_OS_ANDROID
+ // Android screens might be pretty small, such that additional
+ // repositioning (apart from the rotating) will happen to the
+ // popups and mess up the expected positions below.
+ QSKIP("Skipping test for Android.");
+#endif
+ QStringList nativeStyles;
+ nativeStyles.append("macOS");
+ nativeStyles.append("iOS");
+ nativeStyles.append("Windows");
+ if (nativeStyles.contains(QQuickStyle::name()))
+ QSKIP("Skipping test for native styles: they might rearrange their combobox the way they "
+ "want.");
+
+ QQuickControlsApplicationHelper helper(this, "rotatedCombobox.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ {
+ QQuickComboBox *comboBox = window->findChild<QQuickComboBox *>("first");
+ QVERIFY(comboBox);
+ QQuickPopup *popup = comboBox->popup();
+ QVERIFY(popup);
+ popup->open();
+ QTRY_COMPARE(popup->isVisible(), true);
+ const QPointF popupPos(popup->contentItem()->mapToItem(comboBox->parentItem(),
+ popup->contentItem()->position()));
+ const QSizeF popupSize(popup->contentItem()->size());
+
+ // ignore popup.{left,right}Padding() as not included in popup->contentItem()->size()
+ // some styles prefer to draw the popup "over" (in z-axis direction) the combobox to hide
+ // the combobox
+ const bool styleDrawsPopupOverCombobox =
+ comboBox->position().x() - popupSize.width() + comboBox->width() == popupPos.x();
+ // some styles prefer to draw the popup right (in x-axis direction) of the combobox
+ const bool styleDrawsPopupBelowCombobox =
+ comboBox->position().x() - popupSize.width() - comboBox->leftPadding()
+ == popupPos.x();
+
+ QVERIFY(styleDrawsPopupOverCombobox || styleDrawsPopupBelowCombobox);
+ }
+
+ {
+ QQuickComboBox *comboBox = window->findChild<QQuickComboBox *>("second");
+ QVERIFY(comboBox);
+ QQuickPopup *popup = comboBox->popup();
+ QVERIFY(popup);
+ popup->open();
+ QTRY_COMPARE(popup->isVisible(), true);
+ const QPointF popupPos(popup->contentItem()->mapToItem(comboBox->parentItem(),
+ popup->contentItem()->position()));
+
+ // some styles prefer to draw the popup "over" (in z-axis direction) the combobox to hide
+ // the combobox
+ const bool styleDrawsPopupOverCombobox = comboBox->position().x() + comboBox->leftPadding()
+ + popup->leftPadding() + popup->rightPadding()
+ == popupPos.x();
+ // some styles prefer to draw the popup left (in y-axis direction) of the combobox
+ const bool styleDrawsPopupAboveCombobox =
+ comboBox->position().x() + comboBox->width() - comboBox->leftPadding()
+ == popupPos.x();
+
+ QVERIFY(styleDrawsPopupOverCombobox || styleDrawsPopupAboveCombobox);
+
+ popup->close();
+ }
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
index 24da087db5..5ce5cf3125 100644
--- a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
+++ b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
@@ -19,6 +19,7 @@ qt_internal_add_test(tst_qquickwidget
Qt::GuiPrivate
Qt::QmlPrivate
Qt::QuickPrivate
+ Qt::QuickTemplates2Private
Qt::QuickWidgets
Qt::QuickWidgetsPrivate
Qt::WidgetsPrivate
diff --git a/tests/auto/quickwidgets/qquickwidget/data/button.qml b/tests/auto/quickwidgets/qquickwidget/data/button.qml
new file mode 100644
index 0000000000..30698908d7
--- /dev/null
+++ b/tests/auto/quickwidgets/qquickwidget/data/button.qml
@@ -0,0 +1,27 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ width: 100
+ height: 100
+ visible: true
+
+ property bool wasPressed: false
+ property bool wasReleased: false
+ property bool wasClicked: false
+
+ Popup {
+ closePolicy: Popup.NoAutoClose
+ visible: true
+
+ Button {
+ objectName: "button"
+ text: "TAP ME"
+ anchors.fill: parent
+
+ onPressed: wasPressed = true
+ onReleased: wasReleased = true
+ onClicked: wasClicked = true
+ }
+ }
+}
diff --git a/tests/auto/quickwidgets/qquickwidget/data/mouse.qml b/tests/auto/quickwidgets/qquickwidget/data/mouse.qml
index 5d1c6e8443..25a7329a08 100644
--- a/tests/auto/quickwidgets/qquickwidget/data/mouse.qml
+++ b/tests/auto/quickwidgets/qquickwidget/data/mouse.qml
@@ -7,8 +7,10 @@ Rectangle {
property bool wasClicked: false
property bool wasDoubleClicked: false
property bool wasMoved: false
+ color: ma.pressed ? "wheat" : "lightsteelblue"
MouseArea {
+ id: ma
anchors.fill: parent
hoverEnabled: true
onClicked: wasClicked = true
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index 1ef70e6515..54673b70ce 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -35,6 +35,8 @@
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtGui/QWindow>
#include <QtGui/QScreen>
@@ -148,6 +150,9 @@ private slots:
void mouseEventWindowPos();
void synthMouseFromTouch_data();
void synthMouseFromTouch();
+ void touchTapMouseArea();
+ void touchTapButton();
+ void touchMultipleWidgets();
void tabKey();
void resizeOverlay();
void controls();
@@ -641,6 +646,7 @@ void tst_qquickwidget::synthMouseFromTouch()
QTest::touchEvent(&window, device).move(0, p2, &window);
QTest::touchEvent(&window, device).release(0, p2, &window);
+ qCDebug(lcTests) << item->m_touchEvents << item->m_mouseEvents;
QCOMPARE(item->m_touchEvents.count(), synthMouse ? 0 : (acceptTouch ? 3 : 1));
QCOMPARE(item->m_mouseEvents.count(), synthMouse ? 3 : 0);
QCOMPARE(childView->m_mouseEvents.count(), 0);
@@ -648,6 +654,98 @@ void tst_qquickwidget::synthMouseFromTouch()
QCOMPARE(ev, Qt::MouseEventSynthesizedByQt);
}
+void tst_qquickwidget::touchTapMouseArea()
+{
+ QWidget window;
+ window.resize(100, 100);
+ window.setObjectName("window widget");
+ window.setAttribute(Qt::WA_AcceptTouchEvents);
+ QVERIFY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
+ QQuickWidget *quick = new QQuickWidget(&window);
+ quick->setSource(testFileUrl("mouse.qml"));
+ quick->move(50, 50);
+ quick->setObjectName("quick widget");
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QQuickItem *rootItem = quick->rootObject();
+ QVERIFY(rootItem);
+ QQuickMouseArea *ma = rootItem->findChild<QQuickMouseArea *>();
+ QVERIFY(ma);
+
+ QPoint p1 = QPoint(70, 70);
+ QTest::touchEvent(&window, device).press(0, p1, &window);
+ QTRY_COMPARE(ma->pressed(), true);
+ QTest::touchEvent(&window, device).move(0, p1, &window);
+ QTest::touchEvent(&window, device).release(0, p1, &window);
+ QTRY_COMPARE(ma->pressed(), false);
+ QVERIFY(rootItem->property("wasClicked").toBool());
+}
+
+void tst_qquickwidget::touchTapButton()
+{
+ QWidget window;
+ QQuickWidget *quick = new QQuickWidget;
+ quick->setSource(testFileUrl("button.qml"));
+
+ QHBoxLayout hbox;
+ hbox.addWidget(quick);
+ window.setLayout(&hbox);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *rootItem = quick->rootObject();
+ QVERIFY(rootItem);
+ QQuickButton *button = rootItem->findChild<QQuickButton *>("button");
+ QVERIFY(button);
+
+ const QPoint point = quick->mapTo(&window, button->mapToScene(button->boundingRect().center()).toPoint());
+ QTest::touchEvent(&window, device).press(0, point, &window).commit();
+ QTRY_VERIFY(rootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).release(0, point, &window).commit();
+ QTRY_VERIFY(rootItem->property("wasReleased").toBool());
+ QTRY_VERIFY(rootItem->property("wasClicked").toBool());
+}
+
+void tst_qquickwidget::touchMultipleWidgets()
+{
+ QWidget window;
+ QQuickWidget *leftQuick = new QQuickWidget;
+ leftQuick->setSource(testFileUrl("button.qml"));
+ QQuickWidget *rightQuick = new QQuickWidget;
+ rightQuick->setSource(testFileUrl("button.qml"));
+
+ QHBoxLayout hbox;
+ hbox.addWidget(leftQuick);
+ hbox.addWidget(rightQuick);
+ window.setLayout(&hbox);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *leftRootItem = leftQuick->rootObject();
+ QQuickItem *rightRootItem = rightQuick->rootObject();
+ QVERIFY(leftRootItem);
+ QVERIFY(rightRootItem);
+ QQuickButton *leftButton = leftRootItem->findChild<QQuickButton *>("button");
+ QQuickButton *rightButton = rightRootItem->findChild<QQuickButton *>("button");
+ QVERIFY(leftButton);
+ QVERIFY(rightButton);
+
+ const QPoint leftPoint = leftQuick->mapTo(&window, leftButton->mapToScene(
+ leftButton->boundingRect().center()).toPoint());
+ const QPoint rightPoint = rightQuick->mapTo(&window, rightButton->mapToScene(
+ rightButton->boundingRect().center()).toPoint());
+ QTest::touchEvent(&window, device).press(0, leftPoint, &window).commit();
+ QTRY_VERIFY(leftRootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).press(1, rightPoint, &window).commit();
+ QTRY_VERIFY(rightRootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).release(1, rightPoint, &window).commit();
+ QTRY_VERIFY(rightRootItem->property("wasReleased").toBool());
+ QVERIFY(rightRootItem->property("wasClicked").toBool());
+ QTest::touchEvent(&window, device).release(0, leftPoint, &window).commit();
+ QTRY_VERIFY(leftRootItem->property("wasReleased").toBool());
+ QVERIFY(leftRootItem->property("wasClicked").toBool());
+}
+
void tst_qquickwidget::tabKey()
{
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf
index 262ed08785..b7413a3de3 100644
--- a/tools/qmlcachegen/qtquickcompiler.prf
+++ b/tools/qmlcachegen/qtquickcompiler.prf
@@ -77,6 +77,7 @@ for(res, QMLCACHE_RESOURCE_FILES) {
defineReplace(qmlCacheOutputName) {
name = $$absolute_path($$1, $$OUT_PWD)
name = $$relative_path($$name, $$_PRO_FILE_PWD_)
+ contains(name, ^\\.\\..*): name = $$relative_path($$1, $$OUT_PWD)
name = $$replace(name, \\.qml$, _qml)
name = $$replace(name, \\.js$, _js)
name = $$replace(name, \\.mjs$, _mjs)