aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake.conf2
-rw-r--r--dependencies.yaml10
-rw-r--r--examples/quick/scenegraph/graph/shaders/line.frag.qsbbin1282 -> 1273 bytes
-rw-r--r--examples/quick/scenegraph/graph/shaders/line.vert.qsbbin1628 -> 1611 bytes
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.frag.qsbbin1760 -> 1759 bytes
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.vert.qsbbin1646 -> 1643 bytes
-rw-r--r--src/3rdparty/masm/qt_attribution.json1
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodel.cpp21
-rw-r--r--src/labs/platform/qquicklabsplatformmenu_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmenuitem_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformsystemtrayicon_p.h4
-rw-r--r--src/labs/settings/qqmlsettings.cpp92
-rw-r--r--src/labs/settings/qqmlsettings_p.h16
-rw-r--r--src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp32
-rw-r--r--src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h3
-rw-r--r--src/qml/Qt6QmlMacros.cmake6
-rw-r--r--src/qml/compiler/qv4codegen.cpp1
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc2
-rw-r--r--src/qml/doc/src/cppintegration/exposecppattributes.qdoc6
-rw-r--r--src/qml/doc/src/javascript/hostenvironment.qdoc4
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc8
-rw-r--r--src/qml/jit/qv4assemblercommon_p.h2
-rw-r--r--src/qml/jsapi/qjsengine.cpp6
-rw-r--r--src/qml/jsapi/qjsengine.h22
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp13
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.h6
-rw-r--r--src/qml/jsapi/qjsvalue.cpp30
-rw-r--r--src/qml/jsapi/qjsvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp12
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp12
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp18
-rw-r--r--src/qml/jsruntime/qv4object.cpp4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp4
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp31
-rw-r--r--src/qml/jsruntime/qv4value_p.h3
-rw-r--r--src/qml/qml/qqml.cpp12
-rw-r--r--src/qml/qml/qqmlabstractbinding.cpp15
-rw-r--r--src/qml/qml/qqmlbinding.cpp10
-rw-r--r--src/qml/qml/qqmlcomponent.cpp12
-rw-r--r--src/qml/qml/qqmlcontextdata.cpp6
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h9
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp3
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp2
-rw-r--r--src/qml/qml/qqmlglobal.cpp9
-rw-r--r--src/qml/qml/qqmlimport_p.h10
-rw-r--r--src/qml/qml/qqmlincubator.cpp2
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp4
-rw-r--r--src/qml/qml/qqmlproperty.cpp21
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp3
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp4
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp2
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp2
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h2
-rw-r--r--src/qml/qml/qqmltypedata.cpp297
-rw-r--r--src/qml/qml/qqmltypedata_p.h24
-rw-r--r--src/qml/qml/qqmltypeloader.cpp117
-rw-r--r--src/qml/qml/qqmltypeloader_p.h3
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp12
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp6
-rw-r--r--src/qml/qmltc/supportlibrary/qqmlcpptypehelpers_p.h6
-rw-r--r--src/qml/types/qqmlbind.cpp67
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp2
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp2
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h76
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp37
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp8
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp2
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp115
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h2
-rw-r--r--src/qmlcompiler/qqmlsa.cpp7
-rw-r--r--src/qmlcompiler/qqmlsa_p.h3
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp35
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp34
-rw-r--r--src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc13
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp3
-rw-r--r--src/quick/items/qquickitem.cpp9
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp4
-rw-r--r--src/quick/items/qquicktableview.cpp4
-rw-r--r--src/quick/items/qquicktextcontrol.cpp2
-rw-r--r--src/quick/items/qquicktextedit.cpp13
-rw-r--r--src/quick/items/qquicktextinput.cpp16
-rw-r--r--src/quick/items/qquicktreeview.cpp4
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp11
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp7
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h2
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h3
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp3
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp6
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp33
-rw-r--r--src/quick/util/qquickapplication.cpp2
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp5
-rw-r--r--src/quick/util/qquickforeignutils.cpp78
-rw-r--r--src/quick/util/qquickpath.cpp2
-rw-r--r--src/quick/util/qquicksvgparser.cpp12
-rw-r--r--src/quick/util/qquicktransition.cpp67
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc2
-rw-r--r--src/quickcontrols/macos/qtquickcontrols2macosstyleplugin.cpp5
-rw-r--r--src/quickcontrols/material/qt_attribution.json1
-rw-r--r--src/quickcontrols/qquickattachedpropertypropagator.cpp4
-rw-r--r--src/quickcontrolsimpl/qquickiconlabel.cpp15
-rw-r--r--src/quickcontrolstestutils/dialogstestutils.cpp4
-rw-r--r--src/quickcontrolstestutils/dialogstestutils_p.h6
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp7
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp20
-rw-r--r--src/quicklayouts/qquicklinearlayout.cpp3
-rw-r--r--src/quicktemplates/qquickabstractbutton.cpp9
-rw-r--r--src/quicktemplates/qquickcontainer.cpp40
-rw-r--r--src/quicktemplates/qquickmenu.cpp18
-rw-r--r--src/quicktemplates/qquickmenubar.cpp3
-rw-r--r--src/quicktemplates/qquickmonthgrid.cpp3
-rw-r--r--src/quicktemplates/qquickoverlay.cpp33
-rw-r--r--src/quicktemplates/qquickpopup.cpp27
-rw-r--r--src/quicktemplates/qquickshortcutcontext.cpp6
-rw-r--r--src/quicktestutils/quick/visualtestutils_p.h10
-rw-r--r--tests/auto/core/qqmlsettings/CMakeLists.txt2
-rw-r--r--tests/auto/core/qqmlstandardpaths/CMakeLists.txt2
-rw-r--r--tests/auto/core/qqmlsysteminformation/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp29
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp2
-rw-r--r--tests/auto/qml/qml/tst_qml.cpp35
-rw-r--r--tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml25
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp57
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Categorizer.qml60
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mergeSideEffects.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp48
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp4
-rw-r--r--tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Main.qml6
-rw-r--r--tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Other.qml8
-rw-r--r--tests/auto/qml/qmllint/data/locationUnqualifiedLookupAssign.qml7
-rw-r--r--tests/auto/qml/qmllint/data/requiredPropertyInGroupedPropertyScope.qml10
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp17
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.cpp2
-rw-r--r--tests/auto/qml/qqmlbinding/data/bindingInDeadContext.qml18
-rw-r--r--tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml51
-rw-r--r--tests/auto/qml/qqmlbinding/data/multiValueTypeBinding.qml16
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp72
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp3
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp10
-rw-r--r--tests/auto/qml/qqmlecmascript/data/jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown.qml40
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp35
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp4
-rw-r--r--tests/auto/qml/qqmllanguage/data/WithInline.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/urlWithFragment.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp14
-rw-r--r--tests/auto/qml/qqmllistmodel/data/valuesOfInnerList.qml17
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp19
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt1
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml3
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp22
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp12
-rw-r--r--tests/auto/qml/qqmlsettings/data/coexistence/Core.qml5
-rw-r--r--tests/auto/qml/qqmlsettings/data/coexistence/Labs.qml8
-rw-r--r--tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp13
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestModel.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestUtils.js20
-rw-r--r--tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp61
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp2
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp2
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST4
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp9
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp11
-rw-r--r--tests/auto/quick/qquickanimations/data/animationInstantiator.qml40
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp11
-rw-r--r--tests/auto/quick/qquickapplication/BLACKLIST2
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp3
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickloader/data/invalidateContext.qml28
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp56
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/inFlickableWithScale.qml27
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp30
-rw-r--r--tests/auto/quick/qquickshape/data/changeElementsImperatively.qml21
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp8
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST3
-rw-r--r--tests/auto/quick/qquicktextedit/data/readOnly.qml30
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp42
-rw-r--r--tests/auto/quick/qquicktreeview/data/editdelegate.qml91
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.cpp9
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.h3
-rw-r--r--tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp334
-rw-r--r--tests/auto/quickcontrols/accessibility/data/accessibleName/button.qml10
-rw-r--r--tests/auto/quickcontrols/accessibility/data/accessibleName/button2.qml10
-rw-r--r--tests/auto/quickcontrols/accessibility/data/accessibleName/button3.qml11
-rw-r--r--tests/auto/quickcontrols/accessibility/tst_accessibility.cpp41
-rw-r--r--tests/auto/quickcontrols/controls/basic/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/fusion/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/imagine/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/ios/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/macos/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/material/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/universal/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/windows/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/cursor/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/customization/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/designer/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/focus/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/font/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/palette/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/platform/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/pressandhold/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickcolor/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml57
-rw-r--r--tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp31
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/data/childPaintOrder.qml32
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp20
-rw-r--r--tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml29
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/animationOnHeight.qml70
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml1
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/shortcutInNestedSubMenuAction.qml30
-rw-r--r--tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp38
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/clearMenus.qml35
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp11
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/propagateTouchEvents.qml17
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp38
-rw-r--r--tests/auto/quickcontrols/qquickstyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/revisions/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/sanity/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/snippets/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimports/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/translation/CMakeLists.txt2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp231
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp110
-rw-r--r--tests/auto/quickwidgets/qquickwidget/BLACKLIST1
-rw-r--r--tests/baseline/scenegraph/scenegrabber/CMakeLists.txt6
-rw-r--r--tests/benchmarks/quickcontrols/creationtime/CMakeLists.txt2
-rw-r--r--tests/benchmarks/quickcontrols/objectcount/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/buttons/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/fonts/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/gifs/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/headerview/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/material/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/screenshots/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/styles-cover-flow/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/styles/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/systemtrayicon/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/testbench/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/viewinqwidget/CMakeLists.txt2
-rw-r--r--tests/manual/quickdialogs/dialogs/CMakeLists.txt2
-rw-r--r--tests/manual/righttoleft/layoutdirection/CMakeLists.txt2
-rw-r--r--tests/manual/righttoleft/layoutmirroring/CMakeLists.txt2
-rw-r--r--tests/manual/righttoleft/textalignment/CMakeLists.txt2
-rw-r--r--tests/manual/tableview/abstracttablemodel/CMakeLists.txt2
-rw-r--r--tests/manual/tableview/listmodel/CMakeLists.txt2
-rw-r--r--tests/manual/tableview/storagemodel/CMakeLists.txt2
-rw-r--r--tests/manual/tableview/tablemodel/form/CMakeLists.txt2
-rw-r--r--tests/manual/tableview/tablemodel/json/CMakeLists.txt2
-rw-r--r--tests/manual/treeview/sidebyside/CMakeLists.txt2
-rw-r--r--tools/qml/main.cpp3
-rw-r--r--tools/shared/qqmltoolingsettings.cpp19
283 files changed, 3519 insertions, 945 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 856ec9c329..2d5368129d 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,4 +1,4 @@
-set(QT_REPO_MODULE_VERSION "6.5.9")
+set(QT_REPO_MODULE_VERSION "6.5.10")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1")
diff --git a/dependencies.yaml b/dependencies.yaml
index fcbad32e87..73fed8c711 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../tqtc-qtbase:
- ref: ee5586e7ad1fa061b9cdb529dba9cd131678f4b7
+ ref: 95e942cf3183e59429f33aca180583a59b5c1bf5
required: true
../tqtc-qtimageformats:
- ref: dc8311f1d12bd0e0a560493f0d3e797a4ff376fc
+ ref: d927dae2539188aad964a808947f54dd6798024d
required: false
../tqtc-qtlanguageserver:
- ref: 693d3ed6424afd701ec9c004b00fa230a3d79366
+ ref: 04dd5eb97cc7fd1281f4597c7f1052b0c5c6a35f
required: false
../tqtc-qtshadertools:
- ref: 1f3cc2e23adf1943131465aa0ada1feb12563fb9
+ ref: d979959b35c8e5ad5bb29cbe9e19929c18e607d5
required: false
../tqtc-qtsvg:
- ref: f69e56c3fffde393ac5bf889ebd933d41e991876
+ ref: bc016bc41f42cbc00983e4a39720103ab82c3017
required: false
diff --git a/examples/quick/scenegraph/graph/shaders/line.frag.qsb b/examples/quick/scenegraph/graph/shaders/line.frag.qsb
index 0a2fd4d8bc..2a1ec176cf 100644
--- a/examples/quick/scenegraph/graph/shaders/line.frag.qsb
+++ b/examples/quick/scenegraph/graph/shaders/line.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/graph/shaders/line.vert.qsb b/examples/quick/scenegraph/graph/shaders/line.vert.qsb
index 078123c45b..6eeebbde65 100644
--- a/examples/quick/scenegraph/graph/shaders/line.vert.qsb
+++ b/examples/quick/scenegraph/graph/shaders/line.vert.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb b/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
index dd1739b1fe..7bdfb7a966 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
+++ b/examples/quick/scenegraph/graph/shaders/noisy.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb b/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
index 9de384e3a6..22b65bd894 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
+++ b/examples/quick/scenegraph/graph/shaders/noisy.vert.qsb
Binary files differ
diff --git a/src/3rdparty/masm/qt_attribution.json b/src/3rdparty/masm/qt_attribution.json
index 77b144d738..1c98ab8b26 100644
--- a/src/3rdparty/masm/qt_attribution.json
+++ b/src/3rdparty/masm/qt_attribution.json
@@ -5,6 +5,7 @@
"QtUsage": "Used in Qt QML.",
"Homepage": "https://trac.webkit.org/wiki/JavaScriptCore",
+ "Comment": "no relevant PURL or CPE set, because the dependency is huge, and would show a lot of false positives, at this point it should be considered as part of Qt",
"License": "BSD 2-clause \"Simplified\" License",
"LicenseId": "BSD-2-Clause",
"LicenseFile": "LICENSE",
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
index 04d4054cf5..ee05d34904 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
@@ -343,38 +343,39 @@ QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const
Q_D(const QQuickFolderListModel);
QVariant rv;
- if (index.row() >= d->data.size())
+ const int row = index.row();
+ if (row < 0 || row >= d->data.size())
return rv;
switch (role)
{
case FileNameRole:
- rv = d->data.at(index.row()).fileName();
+ rv = d->data.at(row).fileName();
break;
case FilePathRole:
- rv = d->data.at(index.row()).filePath();
+ rv = d->data.at(row).filePath();
break;
case FileBaseNameRole:
- rv = d->data.at(index.row()).baseName();
+ rv = d->data.at(row).baseName();
break;
case FileSuffixRole:
- rv = d->data.at(index.row()).suffix();
+ rv = d->data.at(row).suffix();
break;
case FileSizeRole:
- rv = d->data.at(index.row()).size();
+ rv = d->data.at(row).size();
break;
case FileLastModifiedRole:
- rv = d->data.at(index.row()).lastModified();
+ rv = d->data.at(row).lastModified();
break;
case FileLastReadRole:
- rv = d->data.at(index.row()).lastRead();
+ rv = d->data.at(row).lastRead();
break;
case FileIsDirRole:
- rv = d->data.at(index.row()).isDir();
+ rv = d->data.at(row).isDir();
break;
case FileUrlRole:
case FileURLRole:
- rv = QUrl::fromLocalFile(d->data.at(index.row()).filePath());
+ rv = QUrl::fromLocalFile(d->data.at(row).filePath());
break;
default:
break;
diff --git a/src/labs/platform/qquicklabsplatformmenu_p.h b/src/labs/platform/qquicklabsplatformmenu_p.h
index b8577dcf49..0665dba937 100644
--- a/src/labs/platform/qquicklabsplatformmenu_p.h
+++ b/src/labs/platform/qquicklabsplatformmenu_p.h
@@ -134,7 +134,7 @@ Q_SIGNALS:
void minimumWidthChanged();
void fontChanged();
void typeChanged();
- Q_REVISION(2, 1) void iconChanged();
+ Q_REVISION(1, 1) void iconChanged();
protected:
void classBegin() override;
diff --git a/src/labs/platform/qquicklabsplatformmenuitem_p.h b/src/labs/platform/qquicklabsplatformmenuitem_p.h
index f02f2536fd..632007dd88 100644
--- a/src/labs/platform/qquicklabsplatformmenuitem_p.h
+++ b/src/labs/platform/qquicklabsplatformmenuitem_p.h
@@ -117,7 +117,7 @@ Q_SIGNALS:
void textChanged();
void shortcutChanged();
void fontChanged();
- Q_REVISION(2, 1) void iconChanged();
+ Q_REVISION(1, 1) void iconChanged();
protected:
void classBegin() override;
diff --git a/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h b/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
index 4adac1a8f7..7e5a0057d0 100644
--- a/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
+++ b/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
@@ -80,8 +80,8 @@ Q_SIGNALS:
void visibleChanged();
void tooltipChanged();
void menuChanged();
- Q_REVISION(2, 1) void geometryChanged();
- Q_REVISION(2, 1) void iconChanged();
+ Q_REVISION(1, 1) void geometryChanged();
+ Q_REVISION(1, 1) void iconChanged();
protected:
void init();
diff --git a/src/labs/settings/qqmlsettings.cpp b/src/labs/settings/qqmlsettings.cpp
index 19ebdaf3c2..48fd37ed9b 100644
--- a/src/labs/settings/qqmlsettings.cpp
+++ b/src/labs/settings/qqmlsettings.cpp
@@ -30,7 +30,7 @@ QT_BEGIN_NAMESPACE
/*!
\qmltype Settings
-//! \instantiates QQmlSettings
+//! \instantiates QQmlSettingsLabs
\inqmlmodule Qt.labs.settings
\ingroup settings
\deprecated [6.5] Use \l [QML] {QtCore::}{Settings} from Qt QML Core instead.
@@ -205,12 +205,12 @@ Q_LOGGING_CATEGORY(lcSettings, "qt.labs.settings")
static const int settingsWriteDelay = 500;
-class QQmlSettingsPrivate
+class QQmlSettingsLabsPrivate
{
- Q_DECLARE_PUBLIC(QQmlSettings)
+ Q_DECLARE_PUBLIC(QQmlSettingsLabs)
public:
- QQmlSettingsPrivate();
+ QQmlSettingsLabsPrivate();
QSettings *instance() const;
@@ -223,7 +223,7 @@ public:
void _q_propertyChanged();
QVariant readProperty(const QMetaProperty &property) const;
- QQmlSettings *q_ptr = nullptr;
+ QQmlSettingsLabs *q_ptr = nullptr;
int timerId = 0;
bool initialized = false;
QString category;
@@ -232,12 +232,12 @@ public:
QHash<const char *, QVariant> changedProperties;
};
-QQmlSettingsPrivate::QQmlSettingsPrivate() {}
+QQmlSettingsLabsPrivate::QQmlSettingsLabsPrivate() {}
-QSettings *QQmlSettingsPrivate::instance() const
+QSettings *QQmlSettingsLabsPrivate::instance() const
{
if (!settings) {
- QQmlSettings *q = const_cast<QQmlSettings*>(q_func());
+ QQmlSettingsLabs *q = const_cast<QQmlSettingsLabs*>(q_func());
settings = fileName.isEmpty() ? new QSettings(q) : new QSettings(fileName, QSettings::IniFormat, q);
if (settings->status() != QSettings::NoError) {
// TODO: can't print out the enum due to the following error:
@@ -267,25 +267,25 @@ QSettings *QQmlSettingsPrivate::instance() const
return settings;
}
-void QQmlSettingsPrivate::init()
+void QQmlSettingsLabsPrivate::init()
{
if (!initialized) {
- qCDebug(lcSettings) << "QQmlSettings: stored at" << instance()->fileName();
+ qCDebug(lcSettings) << "QQmlSettingsLabs: stored at" << instance()->fileName();
load();
initialized = true;
}
}
-void QQmlSettingsPrivate::reset()
+void QQmlSettingsLabsPrivate::reset()
{
if (initialized && settings && !changedProperties.isEmpty())
store();
delete settings;
}
-void QQmlSettingsPrivate::load()
+void QQmlSettingsLabsPrivate::load()
{
- Q_Q(QQmlSettings);
+ Q_Q(QQmlSettingsLabs);
const QMetaObject *mo = q->metaObject();
const int offset = mo->propertyOffset();
const int count = mo->propertyCount();
@@ -306,7 +306,7 @@ void QQmlSettingsPrivate::load()
|| (currentValue.canConvert(previousValue.metaType())
&& previousValue != currentValue))) {
property.write(q, currentValue);
- qCDebug(lcSettings) << "QQmlSettings: load" << property.name() << "setting:" << currentValue << "default:" << previousValue;
+ qCDebug(lcSettings) << "QQmlSettingsLabs: load" << property.name() << "setting:" << currentValue << "default:" << previousValue;
}
// ensure that a non-existent setting gets written
@@ -322,20 +322,20 @@ void QQmlSettingsPrivate::load()
}
}
-void QQmlSettingsPrivate::store()
+void QQmlSettingsLabsPrivate::store()
{
QHash<const char *, QVariant>::const_iterator it = changedProperties.constBegin();
while (it != changedProperties.constEnd()) {
instance()->setValue(QString::fromUtf8(it.key()), it.value());
- qCDebug(lcSettings) << "QQmlSettings: store" << it.key() << ":" << it.value();
+ qCDebug(lcSettings) << "QQmlSettingsLabs: store" << it.key() << ":" << it.value();
++it;
}
changedProperties.clear();
}
-void QQmlSettingsPrivate::_q_propertyChanged()
+void QQmlSettingsLabsPrivate::_q_propertyChanged()
{
- Q_Q(QQmlSettings);
+ Q_Q(QQmlSettingsLabs);
const QMetaObject *mo = q->metaObject();
const int offset = mo->propertyOffset();
const int count = mo->propertyCount();
@@ -343,32 +343,32 @@ void QQmlSettingsPrivate::_q_propertyChanged()
const QMetaProperty &property = mo->property(i);
const QVariant value = readProperty(property);
changedProperties.insert(property.name(), value);
- qCDebug(lcSettings) << "QQmlSettings: cache" << property.name() << ":" << value;
+ qCDebug(lcSettings) << "QQmlSettingsLabs: cache" << property.name() << ":" << value;
}
if (timerId != 0)
q->killTimer(timerId);
timerId = q->startTimer(settingsWriteDelay);
}
-QVariant QQmlSettingsPrivate::readProperty(const QMetaProperty &property) const
+QVariant QQmlSettingsLabsPrivate::readProperty(const QMetaProperty &property) const
{
- Q_Q(const QQmlSettings);
+ Q_Q(const QQmlSettingsLabs);
QVariant var = property.read(q);
if (var.metaType() == QMetaType::fromType<QJSValue>())
var = var.value<QJSValue>().toVariant();
return var;
}
-QQmlSettings::QQmlSettings(QObject *parent)
- : QObject(parent), d_ptr(new QQmlSettingsPrivate)
+QQmlSettingsLabs::QQmlSettingsLabs(QObject *parent)
+ : QObject(parent), d_ptr(new QQmlSettingsLabsPrivate)
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
d->q_ptr = this;
}
-QQmlSettings::~QQmlSettings()
+QQmlSettingsLabs::~QQmlSettingsLabs()
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
d->reset(); // flush pending changes
}
@@ -379,15 +379,15 @@ QQmlSettings::~QQmlSettings()
Categories can be used to group related settings together.
*/
-QString QQmlSettings::category() const
+QString QQmlSettingsLabs::category() const
{
- Q_D(const QQmlSettings);
+ Q_D(const QQmlSettingsLabs);
return d->category;
}
-void QQmlSettings::setCategory(const QString &category)
+void QQmlSettingsLabs::setCategory(const QString &category)
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
if (d->category != category) {
d->reset();
d->category = category;
@@ -406,15 +406,15 @@ void QQmlSettings::setCategory(const QString &category)
\sa QSettings::fileName, QSettings::IniFormat
*/
-QString QQmlSettings::fileName() const
+QString QQmlSettingsLabs::fileName() const
{
- Q_D(const QQmlSettings);
+ Q_D(const QQmlSettingsLabs);
return d->fileName;
}
-void QQmlSettings::setFileName(const QString &fileName)
+void QQmlSettingsLabs::setFileName(const QString &fileName)
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
if (d->fileName != fileName) {
d->reset();
d->fileName = fileName;
@@ -433,9 +433,9 @@ void QQmlSettings::setFileName(const QString &fileName)
\sa QSettings::value
*/
-QVariant QQmlSettings::value(const QString &key, const QVariant &defaultValue) const
+QVariant QQmlSettingsLabs::value(const QString &key, const QVariant &defaultValue) const
{
- Q_D(const QQmlSettings);
+ Q_D(const QQmlSettingsLabs);
return d->instance()->value(key, defaultValue);
}
@@ -449,11 +449,11 @@ QVariant QQmlSettings::value(const QString &key, const QVariant &defaultValue) c
\sa QSettings::setValue
*/
-void QQmlSettings::setValue(const QString &key, const QVariant &value)
+void QQmlSettingsLabs::setValue(const QString &key, const QVariant &value)
{
- Q_D(const QQmlSettings);
+ Q_D(const QQmlSettingsLabs);
d->instance()->setValue(key, value);
- qCDebug(lcSettings) << "QQmlSettings: setValue" << key << ":" << value;
+ qCDebug(lcSettings) << "QQmlSettingsLabs: setValue" << key << ":" << value;
}
/*!
@@ -469,28 +469,28 @@ void QQmlSettings::setValue(const QString &key, const QVariant &value)
\sa QSettings::sync
*/
-void QQmlSettings::sync()
+void QQmlSettingsLabs::sync()
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
d->instance()->sync();
}
-void QQmlSettings::classBegin()
+void QQmlSettingsLabs::classBegin()
{
}
-void QQmlSettings::componentComplete()
+void QQmlSettingsLabs::componentComplete()
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
d->init();
qmlWarning(this) << "The Settings type from Qt.labs.settings is deprecated"
" and will be removed in a future release. Please use "
"the one from QtCore instead.";
}
-void QQmlSettings::timerEvent(QTimerEvent *event)
+void QQmlSettingsLabs::timerEvent(QTimerEvent *event)
{
- Q_D(QQmlSettings);
+ Q_D(QQmlSettingsLabs);
if (event->timerId() == d->timerId) {
killTimer(d->timerId);
d->timerId = 0;
diff --git a/src/labs/settings/qqmlsettings_p.h b/src/labs/settings/qqmlsettings_p.h
index 895df8c5e3..e1e60d721b 100644
--- a/src/labs/settings/qqmlsettings_p.h
+++ b/src/labs/settings/qqmlsettings_p.h
@@ -24,9 +24,9 @@
QT_BEGIN_NAMESPACE
-class QQmlSettingsPrivate;
+class QQmlSettingsLabsPrivate;
-class Q_LABSSETTINGS_PRIVATE_EXPORT QQmlSettings : public QObject, public QQmlParserStatus
+class Q_LABSSETTINGS_PRIVATE_EXPORT QQmlSettingsLabs : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -36,8 +36,8 @@ class Q_LABSSETTINGS_PRIVATE_EXPORT QQmlSettings : public QObject, public QQmlPa
QML_ADDED_IN_VERSION(1, 0)
public:
- explicit QQmlSettings(QObject *parent = nullptr);
- ~QQmlSettings();
+ explicit QQmlSettingsLabs(QObject *parent = nullptr);
+ ~QQmlSettingsLabs();
QString category() const;
void setCategory(const QString &category);
@@ -56,14 +56,14 @@ protected:
void componentComplete() override;
private:
- Q_DISABLE_COPY(QQmlSettings)
- Q_DECLARE_PRIVATE(QQmlSettings)
- QScopedPointer<QQmlSettingsPrivate> d_ptr;
+ Q_DISABLE_COPY(QQmlSettingsLabs)
+ Q_DECLARE_PRIVATE(QQmlSettingsLabs)
+ QScopedPointer<QQmlSettingsLabsPrivate> d_ptr;
Q_PRIVATE_SLOT(d_func(), void _q_propertyChanged())
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlSettings)
+QML_DECLARE_TYPE(QQmlSettingsLabs)
#endif // QQMLSETTINGS_P_H
diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
index d3ec1c447a..1d4cf47b64 100644
--- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
@@ -13,7 +13,8 @@ using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
void DebugMessageHandler(QtMsgType type, const QMessageLogContext &ctxt,
const QString &buf)
{
- QQmlDebugConnector::service<QDebugMessageServiceImpl>()->sendDebugMessage(type, ctxt, buf);
+ if (QDebugMessageServiceImpl *service = QQmlDebugConnector::service<QDebugMessageServiceImpl>())
+ service->sendDebugMessage(type, ctxt, buf);
}
QDebugMessageServiceImpl::QDebugMessageServiceImpl(QObject *parent) :
@@ -29,6 +30,12 @@ QDebugMessageServiceImpl::QDebugMessageServiceImpl(QObject *parent) :
}
}
+QDebugMessageServiceImpl::~QDebugMessageServiceImpl()
+{
+ if (oldMsgHandler)
+ restoreOldMessageHandler();
+}
+
void QDebugMessageServiceImpl::sendDebugMessage(QtMsgType type,
const QMessageLogContext &ctxt,
const QString &buf)
@@ -50,19 +57,26 @@ void QDebugMessageServiceImpl::stateChanged(State state)
{
QMutexLocker lock(&initMutex);
- if (state != Enabled && prevState == Enabled) {
- QtMessageHandler handler = qInstallMessageHandler(oldMsgHandler);
- // has our handler been overwritten in between?
- if (handler != DebugMessageHandler)
- qInstallMessageHandler(handler);
-
- } else if (state == Enabled && prevState != Enabled) {
+ if (state != Enabled && prevState == Enabled)
+ restoreOldMessageHandler();
+ else if (state == Enabled && prevState != Enabled)
oldMsgHandler = qInstallMessageHandler(DebugMessageHandler);
- }
prevState = state;
}
+void QDebugMessageServiceImpl::restoreOldMessageHandler()
+{
+ QtMessageHandler handler = qInstallMessageHandler(oldMsgHandler);
+
+ // has our handler been overwritten in between?
+ // In that case, leave the other one alone and try again on destruction.
+ if (handler == DebugMessageHandler)
+ oldMsgHandler = nullptr;
+ else
+ qInstallMessageHandler(handler);
+}
+
void QDebugMessageServiceImpl::synchronizeTime(const QElapsedTimer &otherTimer)
{
QMutexLocker lock(&initMutex);
diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h
index 1ae4bd8224..7fbcd64a4a 100644
--- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h
+++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h
@@ -28,6 +28,7 @@ class QDebugMessageServiceImpl : public QDebugMessageService
Q_OBJECT
public:
QDebugMessageServiceImpl(QObject *parent = nullptr);
+ ~QDebugMessageServiceImpl();
void sendDebugMessage(QtMsgType type, const QMessageLogContext &ctxt, const QString &buf);
void synchronizeTime(const QElapsedTimer &otherTimer) override;
@@ -36,6 +37,8 @@ protected:
void stateChanged(State) override;
private:
+ void restoreOldMessageHandler();
+
friend class QQmlDebuggerServiceFactory;
QtMessageHandler oldMsgHandler;
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 91229d8d1f..ac9165711b 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -3262,7 +3262,7 @@ function(qt6_generate_deploy_qml_app_script)
qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
if(NOT DEFINED __QT_DEPLOY_POST_BUILD)
qt_deploy_runtime_dependencies(
- EXECUTABLE $<TARGET_FILE_NAME:${arg_TARGET}>.app
+ EXECUTABLE \"$<TARGET_FILE_NAME:${arg_TARGET}>.app\"
ADDITIONAL_MODULES \${plugins_found}
${common_deploy_args})
endif()")
@@ -3290,7 +3290,7 @@ endif()")
CONTENT "
qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
qt_deploy_runtime_dependencies(
- EXECUTABLE $<TARGET_FILE:${arg_TARGET}>
+ EXECUTABLE \"$<TARGET_FILE:${arg_TARGET}>\"
ADDITIONAL_MODULES \${plugins_found}
GENERATE_QT_CONF
${common_deploy_args})")
@@ -3303,7 +3303,7 @@ ${common_deploy_args})")
CONTENT "
qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
qt_deploy_runtime_dependencies(
- EXECUTABLE $<TARGET_FILE:${arg_TARGET}>
+ EXECUTABLE \"$<TARGET_FILE:${arg_TARGET}>\"
ADDITIONAL_MODULES \${plugins_found}
GENERATE_QT_CONF
${common_deploy_args})")
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 5df9b3fa4c..b9f784a761 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1422,6 +1422,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
} else if (ast->op == QSOperator::Assign) {
+ bytecodeGenerator->setLocation(ast->left->firstSourceLocation());
if (AST::Pattern *p = ast->left->patternCast()) {
RegisterScope scope(this);
Reference right = expression(ast->right);
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index cba6a122b9..e89cca452f 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -497,7 +497,7 @@ Q_ENUM(Status)
\endcode
Enum classes are registered in QML as scoped and unscoped properties.
-The \c Ready value will be registered at \c Message.Status.Ready and \c Message.Ready .
+The \c Ready value will be registered as \c Message.Status.Ready and \c Message.Ready .
When using enum classes, there can be multiple enums using the same identifiers.
The unscoped registration will be overwriten by the last registered enum. For classes
diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
index 6031d0eebb..83b7155636 100644
--- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
+++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
@@ -521,6 +521,12 @@ pragma NativeMethodBehavior: AcceptThisObject
With this line added, the example above will work as expected.
+\section2 Overriding toString()
+
+If you provide a Q_INVOKABLE method called \e{toString} (with no arguments), that
+method will be used to convert the object to a string in place of JavaScript's
+native \e{toString} implementation.
+
\section1 Exposing Signals
Any public \l{Signals & Slots}{signal} of a QObject-derived type is accessible
diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc
index c267371bbd..26705f9e19 100644
--- a/src/qml/doc/src/javascript/hostenvironment.qdoc
+++ b/src/qml/doc/src/javascript/hostenvironment.qdoc
@@ -47,6 +47,10 @@ to use from C++. See
\l {qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}
for more information.
+The exception to this rule are functions assigned to signal handlers: There, type annotations are
+forbidden to avoid a potential mismatch with the types of the signal. This does not cause issues for
+tooling, as the signal already provides the necessary information.
+
By default, type annotations are ignored by the interpreter and the JIT
compiler, but enforced by \l{qmlcachegen} and \l{qmlsc} when compiling to C++.
This can lead to differences in behavior if you either pass values that
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index ca63c9c03a..e61dfe0ae7 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -1181,11 +1181,11 @@
This returns the attached object instance that has been attached to the specified
\a attachee by the attaching type \e T.
- If \a create is true and type \e T is a valid attaching type, this creates and returns a new
- attached object instance.
+ If \a attachee is \nullptr, returns \nullptr.
- Returns 0 if type \e T is not a valid attaching type, or if \a create is false and no
- attachment object instance has previously been created for \a attachee.
+ If an existing attached object instance of type \e T exists, it will return
+ it. Otherwise, it will return a newly created instance if \a create is
+ \c true and \e T is a valid attaching type, or \nullptr if it's not.
\sa QML_ATTACHED(), {Providing Attached Properties}
*/
diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h
index cccd90fab8..d02f57b42b 100644
--- a/src/qml/jit/qv4assemblercommon_p.h
+++ b/src/qml/jit/qv4assemblercommon_p.h
@@ -583,12 +583,12 @@ public:
for (Jump j : catchyJumps)
j.link(this);
+ loadUndefined();
// We don't need to check for isInterrupted here because if that is set,
// then the first checkException() in any exception handler will find another "exception"
// and jump out of the exception handler.
loadPtr(exceptionHandlerAddress(), ScratchRegister);
Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
- loadUndefined();
jump(ScratchRegister);
exitFunction.link(this);
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 374d170019..45990f6996 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -928,7 +928,8 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
return convertString(*string, metaType, ptr);
// Does not need scoping since QJSValue still holds on to the value.
- return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr);
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&value)),
+ metaType, ptr);
}
bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr)
@@ -1155,7 +1156,8 @@ void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message
*/
void QJSEngine::throwError(const QJSValue &error)
{
- m_v4Engine->throwError(QJSValuePrivate::asReturnedValue(&error));
+ // safe, QJSValue holds a persistent reference
+ m_v4Engine->throwError(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&error)));
}
/*!
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index dbeecb45ae..a16ac60603 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -145,6 +145,18 @@ public:
}
}
+ if constexpr (std::is_same_v<T, double>) {
+ if (sourceType == QMetaType::fromType<int>())
+ return double(*static_cast<const int *>(value.constData()));
+ }
+
+ if constexpr (std::is_same_v<T, int>) {
+ if (sourceType == QMetaType::fromType<double>()) {
+ return QJSNumberCoercion::toInteger(
+ *static_cast<const double *>(value.constData()));
+ }
+ }
+
if constexpr (std::is_same_v<QObject, std::remove_const_t<std::remove_pointer_t<T>>>) {
if (sourceType.flags() & QMetaType::PointerToQObject) {
return *static_cast<QObject *const *>(value.constData());
@@ -254,6 +266,16 @@ public:
return from;
}
+ if constexpr (std::is_same_v<To, double>) {
+ if constexpr (std::is_same_v<From, int>)
+ return double(from);
+ }
+
+ if constexpr (std::is_same_v<To, int>) {
+ if constexpr (std::is_same_v<From, double>)
+ return QJSNumberCoercion::toInteger(from);
+ }
+
{
const QMetaType sourceType = QMetaType::fromType<From>();
const QMetaType targetType = QMetaType::fromType<To>();
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
index a4f2f97432..b429784a96 100644
--- a/src/qml/jsapi/qjsmanagedvalue.cpp
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -787,7 +787,8 @@ void QJSManagedValue::setProperty(const QString &name, const QJSValue &value)
return;
}
QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
- obj->put(key, QJSValuePrivate::convertToReturnedValue(scope.engine, value));
+ QV4::ScopedValue val(scope, QJSValuePrivate::convertToReturnedValue(scope.engine, value));
+ obj->put(key, val);
}
}
@@ -905,7 +906,10 @@ void QJSManagedValue::setProperty(quint32 arrayIndex, const QJSValue &value)
"Value was created in different engine.");
return;
}
- obj->put(arrayIndex, QJSValuePrivate::convertToReturnedValue(v4, value));
+ v4 = obj->engine(); // in case value was primitive
+ QV4::Scope scope(v4);
+ QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(v4, value));
+ obj->put(arrayIndex, v);
}
}
@@ -1120,6 +1124,8 @@ QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) c
*result.d = c->engine()->newObject(c->d());
QV4::Object *o = result.d->as<QV4::Object>();
+ QV4::Scope scope(engine);
+ QV4::ScopedValue val(scope);
for (uint i = 0, end = qMin(qsizetype(c->d()->size), values.size()); i < end; ++i) {
const QJSValue &arg = values[i];
if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
@@ -1127,7 +1133,8 @@ QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) c
"Argument was created in different engine.");
return QJSManagedValue();
}
- o->setProperty(i, QJSValuePrivate::convertToReturnedValue(engine, arg));
+ val = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ o->setProperty(i, val);
}
return result;
diff --git a/src/qml/jsapi/qjsprimitivevalue.h b/src/qml/jsapi/qjsprimitivevalue.h
index 49a00ed180..ea65aa5610 100644
--- a/src/qml/jsapi/qjsprimitivevalue.h
+++ b/src/qml/jsapi/qjsprimitivevalue.h
@@ -777,10 +777,14 @@ private:
}
union {
- bool m_bool = false;
+ bool m_bool;
int m_int;
double m_double;
QString m_string;
+
+ // Dummy value to trigger initialization of the whole storage.
+ // We don't want to see maybe-uninitialized warnings every time we access m_string.
+ std::byte m_zeroInitialize[sizeof(QString)] = {};
};
Type m_type = Undefined;
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 8717462729..943808b322 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -902,7 +902,10 @@ QJSValue& QJSValue::operator=(const QJSValue& other)
if (const QString *string = QJSValuePrivate::asQString(&other))
QJSValuePrivate::setString(this, *string);
else
- QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other));
+ // fomReturnedValue is safe, as the QJSValue still has a persistent reference
+ QJSValuePrivate::setValue(
+ this,
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other)));
return *this;
}
@@ -990,17 +993,25 @@ static bool js_equal(const QString &string, const QV4::Value &value)
*/
bool QJSValue::equals(const QJSValue& other) const
{
+ // QJSValue stores heap items in persistent values, which already ensures marking
+ // therefore, fromReturnedValue below is safe
if (const QString *string = QJSValuePrivate::asQString(this)) {
if (const QString *otherString = QJSValuePrivate::asQString(&other))
return *string == *otherString;
- return js_equal(*string, QJSValuePrivate::asReturnedValue(&other));
+ return js_equal(
+ *string,
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other)));
}
- if (const QString *otherString = QJSValuePrivate::asQString(&other))
- return js_equal(*otherString, QJSValuePrivate::asReturnedValue(this));
+ if (const QString *otherString = QJSValuePrivate::asQString(&other)) {
+ return js_equal(
+ *otherString,
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)));
+ }
- return Runtime::CompareEqual::call(QJSValuePrivate::asReturnedValue(this),
- QJSValuePrivate::asReturnedValue(&other));
+ return Runtime::CompareEqual::call(
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)),
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other)));
}
/*!
@@ -1041,8 +1052,11 @@ bool QJSValue::strictlyEquals(const QJSValue& other) const
return false;
}
- return RuntimeHelpers::strictEqual(QJSValuePrivate::asReturnedValue(this),
- QJSValuePrivate::asReturnedValue(&other));
+ // QJSValue stores heap objects persistently, so we can be sure that they'll be marked
+ // thus we can safely use fromReturnedValue
+ return RuntimeHelpers::strictEqual(
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)),
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other)));
}
/*!
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index b95e76e76e..6bc731aace 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -189,7 +189,7 @@ public:
static QJSValue fromReturnedValue(QV4::ReturnedValue d)
{
QJSValue result;
- setValue(&result, d);
+ setValue(&result, QV4::Value::fromReturnedValue(d));
return result;
}
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 05e73d0295..e4170b459e 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -576,6 +576,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *functionObject,
const Value *thisObject, const Value *argv, int argc)
{
Scope scope(functionObject);
+ CHECK_STACK_LIMITS(scope.engine)
ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index a807cc42df..3bb140a4fd 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -2453,9 +2453,12 @@ bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
return false;
const QMetaType elementMetaType = iterable.valueMetaType();
+ QV4::Scope scope(sequence->engine());
+ QV4::ScopedValue v(scope);
for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
QVariant element(elementMetaType);
- ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data());
+ v = sequence->get(i);
+ ExecutionEngine::metaTypeFromJS(v, elementMetaType, element.data());
iterable.addValue(element, QSequentialIterable::AtEnd);
}
return true;
@@ -2515,9 +2518,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
QByteArray result;
const qint64 length = ao->getLength();
result.reserve(length);
+ QV4::Scope scope(ao->engine());
+ QV4::ScopedValue v(scope);
for (qint64 i = 0; i < length; ++i) {
char value = 0;
- ExecutionEngine::metaTypeFromJS(ao->get(i), QMetaType::fromType<char>(), &value);
+ v = ao->get(i);
+ ExecutionEngine::metaTypeFromJS(v, QMetaType::fromType<char>(), &value);
result.push_back(value);
}
*reinterpret_cast<QByteArray*>(data) = std::move(result);
@@ -2778,7 +2784,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
*reinterpret_cast<void* *>(data) = nullptr;
return true;
} else if (metaType == QMetaType::fromType<QJSValue>()) {
- QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue());
+ QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value);
return true;
} else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
*reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index dc24f2d2a9..aed1a84f63 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -695,8 +695,10 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively(
return fragment.native;
ScopedObject o(scope, dependentModule.native);
- if (o)
- return engine->registerNativeModule(request, o->get(importName));
+ if (o) {
+ ScopedValue nativeModule(scope, o->get(importName));
+ return engine->registerNativeModule(request, nativeModule);
+ }
return nullptr;
} else {
@@ -727,8 +729,10 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively(
resolution = fragment.native;
} else {
ScopedObject o(scope, dependentModule.native);
- if (o)
- resolution = engine->registerNativeModule(request, o->get(exportName));
+ if (o) {
+ ScopedValue nativeModule(scope, o->get(exportName));
+ resolution = engine->registerNativeModule(request, nativeModule);
+ }
}
}
}
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index 32e93cedb4..ec74fbceed 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -301,15 +301,15 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
return Encode::undefined();
ExecutionEngine *v4 = engine();
- bool isStrict = v4->currentStackFrame->v4Function->isStrict();
+ const Function *v4Function = v4->currentStackFrame
+ ? v4->currentStackFrame->v4Function
+ : v4->globalCode;
+ const bool isStrict = v4Function && v4Function->isStrict();
Scope scope(v4);
- ScopedContext ctx(scope, v4->currentContext());
- if (!directCall) {
- // the context for eval should be the global scope
- ctx = v4->scriptContext();
- }
+ // In case of !directCall, the context for eval should be the global scope
+ ScopedContext ctx(scope, directCall ? v4->currentContext() : v4->scriptContext());
String *scode = argv[0].stringValue();
if (!scode)
@@ -330,14 +330,14 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
return Encode::undefined();
function->kind = Function::Eval;
+ ScopedValue thisObject(scope, directCall
+ ? scope.engine->currentStackFrame->thisObject()
+ : scope.engine->globalObject->asReturnedValue());
if (function->isStrict() || isStrict) {
ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function));
- ScopedValue thisObject(scope, directCall ? scope.engine->currentStackFrame->thisObject() : scope.engine->globalObject->asReturnedValue());
return checkedResult(v4, e->call(thisObject, nullptr, 0));
}
- ScopedValue thisObject(scope, scope.engine->currentStackFrame->thisObject());
-
return checkedResult(v4, function->call(thisObject, nullptr, 0, ctx));
}
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 0851b2f5ab..2071e39520 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -42,7 +42,9 @@ void Object::setInternalClass(Heap::InternalClass *ic)
for (uint i = 0; i < ic->size; ++i) {
// Note that some members might have been deleted. The key may be invalid.
const PropertyKey key = ic->nameMap.at(i);
- newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined());
+ // fromReturnedValue is safe, we directly write it through the WriteBarrier
+ newMembers->set(scope.engine, i,
+ QV4::Value::fromReturnedValue(key.isValid() ? get(key) : Encode::undefined()));
}
p->internalClass.set(scope.engine, ic);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index e19c57fd40..6d08764a11 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -2219,7 +2219,9 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
default:
if (type == qMetaTypeId<QJSValue>()) {
qjsValuePtr = new (&allocData) QJSValue;
- QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue());
+ Scope scope(engine);
+ ScopedValue v(scope, value);
+ QJSValuePrivate::setValue(qjsValuePtr, v);
return true;
}
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 1dd2654382..2cafd9a741 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -709,7 +709,8 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < quint32(length); ++i) {
const QMetaType valueMetaType = priv->typeId;
- QVariant variant = ExecutionEngine::toVariant(a->get(i), valueMetaType, false);
+ QV4::ScopedValue element(scope, a->get(i));
+ QVariant variant = ExecutionEngine::toVariant(element, valueMetaType, false);
if (valueMetaType == QMetaType::fromType<QVariant>()) {
meta->addValueAtEnd(result.data(), &variant);
} else {
diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp
index 637d033bd4..aa0d47c766 100644
--- a/src/qml/jsruntime/qv4urlobject.cpp
+++ b/src/qml/jsruntime/qv4urlobject.cpp
@@ -806,7 +806,8 @@ ReturnedValue UrlSearchParamsCtor::virtualCallAsConstructor(const FunctionObject
uint len = argArray->getLength();
for (uint i = 0; i < len; i++) {
- QV4::Value pair = argArray->get(i);
+ // safe to user fromReturnedValue: Normal array, which will ensure marking
+ QV4::Value pair = Value::fromReturnedValue(argArray->get(i));
auto *pairArrayObject = pair.as<ArrayObject>();
if (pairArrayObject == nullptr) {
@@ -895,11 +896,14 @@ void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params)
for (uint i = 0; i < len; i++)
{
- QV4::Value pair = params->get(i);
+ // fromReturnedValue is safe; everything is reachable via params
+ // so the gc won't collect it; we control params, so there can't be
+ // any weird proxy magic
+ QV4::Value pair = Value::fromReturnedValue(params->get(i));
auto *pairArrayObject = pair.as<ArrayObject>();
- QV4::Value key = pairArrayObject->get(uint(0));
- QV4::Value value = pairArrayObject->get(uint(1));
+ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
+ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
scopedKeys->put(i, key);
scopedValues->put(i, value);
@@ -1014,11 +1018,11 @@ QList<QStringList> UrlSearchParamsObject::params() const
uint len = scopedArray->getLength();
for (uint i = 0; i < len; i++) {
- QV4::Value pair = scopedArray->get(i);
+ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
auto *pairArrayObject = pair.as<ArrayObject>();
- QV4::Value key = pairArrayObject->get(uint(0));
- QV4::Value value = pairArrayObject->get(uint(1));
+ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
+ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
result << QStringList { key.toQString(), value.toQString() };
}
@@ -1063,10 +1067,11 @@ int UrlSearchParamsObject::indexOf(QString name, int last) const
int len = scopedArray->getLength();
for (int i = last + 1; i < len; i++) {
- QV4::Value pair = scopedArray->get(i);
+ // fromReturnedValue is safe, scopedArray is a normal array and takes care of marking
+ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
auto *pairArrayObject = pair.as<ArrayObject>();
- QV4::Value key = pairArrayObject->get(uint(0));
+ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
if (key.toQString() == name)
return i;
@@ -1084,10 +1089,10 @@ QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const
if (index >= scopedArray->getLength())
return {};
- QV4::Value pair = scopedArray->get(index);
+ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
auto *pairArrayObject = pair.as<ArrayObject>();
- QV4::Value value = pairArrayObject->get(pairIndex);
+ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
return value.toQString();
}
@@ -1101,10 +1106,10 @@ QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex)
if (index >= scopedArray->getLength())
return nullptr;
- QV4::Value pair = scopedArray->get(index);
+ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
auto *pairArrayObject = pair.as<ArrayObject>();
- QV4::Value value = pairArrayObject->get(pairIndex);
+ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
return value.as<String>()->d();
}
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 643d6c16a9..36fe261dea 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -38,9 +38,6 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
{
using ManagedPtr = Managed *;
- Value() = default;
- constexpr Value(quint64 val) : StaticValue(val) {}
-
static constexpr Value fromStaticValue(StaticValue staticValue)
{
return {staticValue._val};
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 44e8955b7a..f6bb6d4082 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1267,7 +1267,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
} else {
QVariant var(propType);
QV4::ExecutionEngine *v4 = engine->handle();
- v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
+ QV4::Scope scope(v4);
+ QV4::ScopedValue val(scope, v4->metaTypeToJS(type, value));
+ v4->metaTypeFromJS(val, propType, var.data());
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
@@ -1286,7 +1288,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
} else {
QVariant var(propType);
QV4::ExecutionEngine *v4 = engine->handle();
- v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
+ QV4::Scope scope(v4);
+ QV4::ScopedValue val(scope, v4->metaTypeToJS(type, value));
+ v4->metaTypeFromJS(val, propType, var.data());
storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
}
break;
@@ -1504,7 +1508,9 @@ void AOTCompiledContext::initCallGlobalLookup(uint index) const
bool AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (!QV4::ExecutionEngine::metaTypeFromJS(l->globalGetter(l, engine->handle()), type, target)) {
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue val(scope, l->globalGetter(l, engine->handle()));
+ if (!QV4::ExecutionEngine::metaTypeFromJS(val, type, target)) {
engine->handle()->throwTypeError();
return false;
}
diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp
index 78d1d68f55..d1c0a855d1 100644
--- a/src/qml/qml/qqmlabstractbinding.cpp
+++ b/src/qml/qml/qqmlabstractbinding.cpp
@@ -49,16 +49,15 @@ void QQmlAbstractBinding::addToObject()
// Find the value type proxy (if there is one)
QQmlValueTypeProxyBinding *proxy = nullptr;
- if (data->hasBindingBit(coreIndex)) {
- QQmlAbstractBinding *b = data->bindings;
- while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
- b->targetPropertyIndex().hasValueTypeIndex()))
- b = b->nextBinding();
- Q_ASSERT(b && b->kind() == QQmlAbstractBinding::ValueTypeProxy);
- proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
+ QQmlAbstractBinding *b = data->bindings;
+ while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
+ b->targetPropertyIndex().hasValueTypeIndex())) {
+ b = b->nextBinding();
}
- if (!proxy) {
+ if (b && b->kind() == QQmlAbstractBinding::ValueTypeProxy) {
+ proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
+ } else {
proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex));
Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex);
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 551f55b4be..31b3c28690 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -218,7 +218,9 @@ protected:
}
// If the type didn't match, we need to do JavaScript conversion. This should be rare.
- return write(engine()->handle()->metaTypeToJS(type, result), isUndefined, flags);
+ QV4::Scope scope(engine()->handle());
+ QV4::ScopedValue value(scope, engine()->handle()->metaTypeToJS(type, result));
+ return write(value, isUndefined, flags);
}
// Returns true if successful, false if an error description was set on expression
@@ -439,8 +441,10 @@ bool QQmlBinding::slowWrite(
if (core.isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data());
Q_ASSERT(vmemo);
+ QV4::Scope scope(qmlEngine->handle());
+ QV4::ScopedValue value(scope, qmlEngine->handle()->metaTypeToJS(resultType, result));
vmemo->setVMEProperty(core.coreIndex(),
- qmlEngine->handle()->metaTypeToJS(resultType, result));
+ value);
} else if (isUndefined && core.isResettable()) {
void *args[] = { nullptr };
QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
@@ -697,7 +701,7 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write
error = !write(result, returnType, isUndefined, flags);
returnType.destruct(result);
} else if (canWrite()) {
- error = !write(QV4::Encode::undefined(), true, flags);
+ error = !write(QV4::Value::undefinded(), true, flags);
}
}
} else {
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 60e5dd4c9b..9cc57eed13 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -382,7 +382,8 @@ bool QQmlComponentPrivate::setInitialProperty(
}
const QString lastProperty = properties.last();
segment = scope.engine->newString(lastProperty);
- object->put(segment, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
+ QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
+ object->put(segment, v);
if (scope.engine->hasException) {
qmlWarning(base, scope.engine->catchExceptionAsQmlError());
scope.engine->hasException = false;
@@ -927,6 +928,11 @@ QObject *QQmlComponent::create(QQmlContext *context)
there are unset required properties, the object creation fails and returns
\c nullptr, in which case \l isError() will return \c true.
+ If \a context is \nullptr (the default), it will create the instance in the
+ \l {QQmlEngine::rootContext()}{root context} of the engine.
+
+ The ownership of the returned object instance is transferred to the caller.
+
\sa QQmlComponent::create
\since 5.14
*/
@@ -1419,8 +1425,8 @@ void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyString
\a incubator. \a context specifies the context within which to create the object
instance.
- If \a context is \nullptr (by default), it will create the instance in the
- engine's \l {QQmlEngine::rootContext()}{root context}.
+ If \a context is \nullptr (the default), it will create the instance in the
+ \l {QQmlEngine::rootContext()}{root context} of the engine.
\a forContext specifies a context that this object creation depends upon.
If the \a forContext is being created asynchronously, and the
diff --git a/src/qml/qml/qqmlcontextdata.cpp b/src/qml/qml/qqmlcontextdata.cpp
index 8667a1ce81..22a359d93f 100644
--- a/src/qml/qml/qqmlcontextdata.cpp
+++ b/src/qml/qml/qqmlcontextdata.cpp
@@ -100,7 +100,6 @@ void QQmlContextData::invalidate()
}
m_importedScripts.clear();
-
m_engine = nullptr;
clearParent();
}
@@ -111,6 +110,8 @@ void QQmlContextData::clearContextRecursively()
for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild)
ctxIt->clearContextRecursively();
+
+ m_engine = nullptr;
}
void QQmlContextData::clearContext()
@@ -137,8 +138,7 @@ QQmlContextData::~QQmlContextData()
// avoid recursion
addref();
- if (m_engine)
- invalidate();
+ invalidate();
m_linkedContext.reset();
Q_ASSERT(refCount() == 1);
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h
index f5d54c8fd2..ae95123d8c 100644
--- a/src/qml/qml/qqmlcontextdata_p.h
+++ b/src/qml/qml/qqmlcontextdata_p.h
@@ -277,6 +277,15 @@ public:
QV4::PersistentValue importedScripts() const { return m_importedScripts; }
void setImportedScripts(const QV4::PersistentValue &scripts) { m_importedScripts = scripts; }
+ /*
+ we can safely pass a ReturnedValue here, as setImportedScripts will directly store
+ scripts in a persistentValue, without any intermediate allocation that could trigger
+ a gc run
+ */
+ void setImportedScripts(QV4::ExecutionEngine *engine, QV4::ReturnedValue scripts) {
+ m_importedScripts.set(engine, scripts);
+ }
+
QQmlRefPointer<QQmlContextData> linkedContext() const { return m_linkedContext; }
void setLinkedContext(const QQmlRefPointer<QQmlContextData> &context) { m_linkedContext = context; }
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 96a5679599..655264435c 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -153,7 +153,8 @@ void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4Fun
QV4::ScopedArrayObject array(scope, engine->newArrayObject(length));
uint i = 0;
for (int j = offset, ej = args->length(); j < ej; ++i, ++j)
- array->put(i, (*args)[j]);
+ // fromReturnedValue is safe, args should already ensure marking
+ array->put(i, QV4::Value::fromReturnedValue((*args)[j]));
dfc.m_args.set(engine, array);
}
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 441fc6200d..09f400c173 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -35,7 +35,7 @@ QT_BEGIN_NAMESPACE
The \l {Writing QML Extensions with C++} tutorial also contains a chapter
on creating QML plugins.
- \sa QQmlEngine::importPlugin(), {How to Create Qt Plugins}
+ \sa {How to Create Qt Plugins}
*/
/*!
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 398011226f..3dfbe9c85a 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -363,7 +363,7 @@ static bool byProperties(
if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
const QJSValue *val = static_cast<const QJSValue *>(source);
return byProperties(
- targetMetaObject, target, QV4::Value(QJSValuePrivate::asReturnedValue(val)));
+ targetMetaObject, target, QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(val)));
}
if (sourceMetaType == QMetaType::fromType<QVariantMap>()) {
@@ -431,7 +431,8 @@ bool QQmlValueTypeProvider::createValueType(
if (targetType.canPopulateValueType()
&& byProperties(
targetMetaObject, target,
- QV4::Value(QJSValuePrivate::asReturnedValue(&source)))) {
+ // fromReturnedValue is OK here because QJSValue holds a PersistentValue
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&source)))) {
return true;
}
@@ -440,7 +441,9 @@ bool QQmlValueTypeProvider::createValueType(
targetMetaObject, targetMetaType, target,
[&](QMetaType parameterType, auto callback) {
QVariant variant = QV4::ExecutionEngine::toVariant(
- QJSValuePrivate::asReturnedValue(&source), parameterType);
+ // fromReturnedValue is OK here because QJSValue holds a PersistentValue
+ QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&source)),
+ parameterType);
return callback(variant.metaType(), variant.data());
})) {
return true;
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index fe8ae5295c..fbe0459343 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -389,6 +389,16 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
QString qmldirAbsoluteFilePath;
for (QString qmldirPath : qmlDirPaths) {
if (hasInterceptors) {
+ // TODO:
+ // 1. This is inexact. It triggers only on the existence of interceptors, not on
+ // actual interception. If the URL was remote to begin with but no interceptor
+ // actually changes it, we still clear the qmldirPath and consider it
+ // QmldirInterceptedToRemote.
+ // 2. This misdiagnosis makes addLibraryImport do the right thing and postpone
+ // the loading of pre-registered types for any QML engine that has interceptors
+ // (even if they don't do anything in this case).
+ // Fixing this would open the door to follow-up problems but wouldn't result in any
+ // significant benefit.
const QUrl intercepted = engine->interceptUrl(
QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
QQmlAbstractUrlInterceptor::QmldirFile);
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 90571fdf50..4fc3f1a737 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -103,6 +103,8 @@ QQmlIncubatorPrivate::~QQmlIncubatorPrivate()
void QQmlIncubatorPrivate::clear()
{
+ if (!enginePriv)
+ return;
// reset the tagged pointer
if (requiredPropertiesFromComponent)
requiredPropertiesFromComponent = decltype(requiredPropertiesFromComponent){};
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index e3c0ba19ae..497fe43a73 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -377,9 +377,11 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons
ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
+ QV4::ScopedValue wrappedObject(scope);
for (qsizetype i = 0; i < deleteCount; ++i) {
+ wrappedObject = QObjectWrapper::wrap(scope.engine, property->at(property, start + i));
newArray->arrayPut(
- i, QObjectWrapper::wrap(scope.engine, property->at(property, start + i)));
+ i, wrappedObject);
}
newArray->setArrayLengthUnchecked(deleteCount);
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index f09024d330..5caaafdc5c 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1169,10 +1169,22 @@ QVariant QQmlPropertyPrivate::readValueProperty()
}
return QVariant();
} else if (core.isQList()) {
+ auto coreMetaType = core.propType();
- QQmlListProperty<QObject> prop;
- core.readProperty(object, &prop);
- return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType()));
+ // IsQmlList is set for QQmlListPropery and list<ObjectType>
+ if (coreMetaType.flags() & QMetaType::IsQmlList) {
+ QQmlListProperty<QObject> prop;
+ core.readProperty(object, &prop);
+ return QVariant::fromValue(QQmlListReferencePrivate::init(prop, coreMetaType));
+ } else {
+ // but not for lists of value types
+ QVariant result(coreMetaType);
+ // TODO: ideally, we would not default construct and copy assign,
+ // but do a single copy-construct; we don't have API for that, though
+ coreMetaType.construct(result.data());
+ core.readProperty(object, result.data());
+ return result;
+ }
} else if (core.isQObject()) {
@@ -1504,7 +1516,8 @@ static bool tryAssignBinding(
if (!f || !f->isBinding())
return false;
- QV4::QObjectWrapper::setProperty(f->engine(), object, &property, f->asReturnedValue());
+ // fromReturnedValue is safe! f is stored in the QJSValue, so htere's already a persistent reference to it
+ QV4::QObjectWrapper::setProperty(f->engine(), object, &property, QV4::Value::fromReturnedValue(f->asReturnedValue()));
return true;
}
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 5f646b62de..4de4ce8ccc 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -131,6 +131,9 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP
void QQmlPropertyBindingJS::expressionChanged()
{
+ if (!hasValidContext())
+ return;
+
auto binding = asBinding();
if (!binding->propertyDataPtr)
return;
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index c49ce14351..c7d187ef3a 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -140,8 +140,8 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(
if (!pendingBinding.resolveInstantiatingProperty())
continue;
- auto cache = pendingBinding.instantiatingPropertyCache();
- propertyCaches->set(groupPropertyObjectIndex, cache);
+ if (auto cache = pendingBinding.instantiatingPropertyCache())
+ propertyCaches->set(groupPropertyObjectIndex, cache);
}
}
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index 4ea3816ed4..1b038a34ee 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -150,7 +150,7 @@ void QQmlScriptBlob::done()
for (int scriptIndex = 0; scriptIndex < m_scripts.size(); ++scriptIndex) {
const ScriptReference &script = m_scripts.at(scriptIndex);
- m_scriptData->scripts.append(script.script);
+ m_scriptData->scripts.append(script.script->scriptData());
if (!script.nameSpace.isNull()) {
if (!ns.contains(script.nameSpace)) {
diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp
index 1a6a8157d0..5ed9345a4f 100644
--- a/src/qml/qml/qqmlscriptdata.cpp
+++ b/src/qml/qml/qqmlscriptdata.cpp
@@ -63,7 +63,7 @@ QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext(
}
QV4::ScopedValue v(scope);
for (int ii = 0; ii < scripts.size(); ++ii) {
- v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData);
+ v = scripts.at(ii)->scriptValueForContext(qmlContextData);
scriptsArray->put(ii, v);
}
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
index c7100861e2..208e55d13f 100644
--- a/src/qml/qml/qqmlscriptdata_p.h
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -39,7 +39,7 @@ public:
QUrl url;
QString urlString;
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
- QVector<QQmlRefPointer<QQmlScriptBlob>> scripts;
+ QVector<QQmlRefPointer<QQmlScriptData>> scripts;
QV4::ReturnedValue scriptValueForContext(const QQmlRefPointer<QQmlContextData> &parentCtxt);
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index d3cfeb3108..e18101af2b 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -350,104 +350,167 @@ void QQmlTypeData::setCompileUnit(const Container &container)
}
}
-void QQmlTypeData::done()
+bool QQmlTypeData::checkScripts()
{
- auto cleanup = qScopeGuard([this]{
- m_backupSourceCode = SourceCodeData();
- m_document.reset();
- m_typeReferences.clear();
- if (isError()) {
- const auto encounteredErrors = errors();
- for (const QQmlError &e : encounteredErrors)
- qCDebug(DBG_DISK_CACHE) << e.toString();
- m_compiledData.reset();
- }
- });
-
- if (isError())
- return;
-
// Check all script dependencies for errors
for (int ii = 0; ii < m_scripts.size(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
Q_ASSERT(script.script->isCompleteOrError());
if (script.script->isError()) {
- QList<QQmlError> errors = script.script->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column()));
- error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
- errors.prepend(error);
- setError(errors);
- return;
+ createError(
+ script,
+ QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
+ return false;
}
}
+ return true;
+}
+
+void QQmlTypeData::createError(const TypeReference &type, const QString &message)
+{
+ createError(type, message, type.typeData ? type.typeData->errors() : QList<QQmlError>());
+}
+
+void QQmlTypeData::createError(const ScriptReference &script, const QString &message)
+{
+ createError(script, message, script.script ? script.script->errors() : QList<QQmlError>());
+}
+bool QQmlTypeData::checkDependencies()
+{
// Check all type dependencies for errors
- for (auto it = std::as_const(m_resolvedTypes).begin(), end = std::as_const(m_resolvedTypes).end(); it != end;
- ++it) {
+ for (auto it = std::as_const(m_resolvedTypes).begin(), end = std::as_const(m_resolvedTypes).end();
+ it != end; ++it) {
const TypeReference &type = *it;
- Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType());
+ Q_ASSERT(!type.typeData
+ || type.typeData->isCompleteOrError()
+ || type.type.isInlineComponentType());
if (type.errorWhenNotFound
- && type.type.isInlineComponentType()
- && !type.type.pendingResolutionName().isEmpty()) {
+ && type.type.isInlineComponentType()
+ && !type.type.pendingResolutionName().isEmpty()) {
auto containingType = type.type.containingType();
- auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName());
- if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component
- const QString typeName = stringAt(it.key());
- int lastDot = typeName.lastIndexOf(u'.');
+ auto objectId
+ = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName());
- QList<QQmlError> errors = type.typeData ? type.typeData->errors() : QList<QQmlError>{};
- QQmlError error;
- error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
- error.setDescription(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(QStringView{typeName}.left(lastDot), type.type.pendingResolutionName()));
- errors.prepend(error);
- setError(errors);
- return;
+ // can be any negative number if we tentatively resolved it in QQmlImport but it
+ // actually was not an inline component
+ if (objectId < 0) {
+ const QString &typeName = stringAt(it.key());
+ int lastDot = typeName.lastIndexOf(u'.');
+ createError(
+ type,
+ QQmlTypeLoader::tr("Type %1 has no inline component type called %2")
+ .arg(QStringView{typeName}.left(lastDot), type.type.elementName()));
+ return false;
} else {
type.type.setInlineComponentObjectId(objectId);
}
}
if (type.errorWhenNotFound && type.typeData && type.typeData->isError()) {
- const QString typeName = stringAt(it.key());
-
- QList<QQmlError> errors = type.typeData->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
- error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
- errors.prepend(error);
- setError(errors);
- return;
+ const QString &typeName = stringAt(it.key());
+ createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ return false;
}
}
+ return true;
+}
+
+bool QQmlTypeData::checkCompositeSingletons()
+{
// Check all composite singleton type dependencies for errors
for (int ii = 0; ii < m_compositeSingletons.size(); ++ii) {
const TypeReference &type = m_compositeSingletons.at(ii);
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
QString typeName = type.type.qmlTypeName();
-
- QList<QQmlError> errors = type.typeData->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
- error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
- errors.prepend(error);
- setError(errors);
- return;
+ createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ return false;
}
}
+ return true;
+}
+
+void QQmlTypeData::createQQmlType()
+{
m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl());
if (!m_typeClassName.isEmpty())
m_typeIds = CompositeMetaTypeIds::fromCompositeName(m_typeClassName);
+}
+
+bool QQmlTypeData::rebuildFromSource()
+{
+ // Clear and re-build everything.
+
+ m_typeReferences.clear();
+ m_scripts.clear();
+ m_namespaces.clear();
+ m_compositeSingletons.clear();
+
+ m_resolvedTypes.clear();
+ m_typesResolved = false;
+
+ m_typeClassName.clear();
+
+ m_inlineComponentData.clear();
+ m_compiledData.reset();
+
+ m_implicitImportLoaded = false;
+
+ m_importCache.adopt(new QQmlImports(m_typeLoader));
+ m_unresolvedImports.clear();
+
+ if (!loadFromSource())
+ return false;
+
+ continueLoadFromIR();
+
+ if (!resolveTypes())
+ return false;
+
+ if (!checkScripts())
+ return false;
+
+ if (!checkDependencies())
+ return false;
+
+ if (!checkCompositeSingletons())
+ return false;
+
+ createQQmlType();
+
+ setupICs(m_document, &m_inlineComponentData, finalUrl());
+ return true;
+}
+
+void QQmlTypeData::done()
+{
+ auto cleanup = qScopeGuard([this]{
+ m_backupSourceCode = SourceCodeData();
+ m_document.reset();
+ m_typeReferences.clear();
+ if (isError()) {
+ const auto encounteredErrors = errors();
+ for (const QQmlError &e : encounteredErrors)
+ qCDebug(DBG_DISK_CACHE) << e.toString();
+ m_compiledData.reset();
+ }
+ });
+
+ if (isError())
+ return;
+
+ if (!checkScripts())
+ return;
+
+ if (!checkDependencies())
+ return;
+
+ if (!checkCompositeSingletons())
+ return;
+
+ createQQmlType();
if (m_document) {
setupICs(m_document, &m_inlineComponentData, finalUrl());
@@ -493,28 +556,18 @@ void QQmlTypeData::done()
<< m_compiledData->fileName();
}
- if (!loadFromSource())
+ resolvedTypeCache.clear();
+ typeNameCache.reset();
+
+ if (!rebuildFromSource())
return;
- // We want to keep our resolve types ...
- m_compiledData->resolvedTypes.clear();
- // ... but we don't want the property caches we've created for the broken CU.
- for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache)) {
- const auto compilationUnit = ref->compilationUnit();
- if (compilationUnit.isNull()) {
- // Inline component references without CU belong to the surrounding CU.
- // We have to clear them. Inline component references to other documents
- // have a CU.
- if (!ref->type().isInlineComponentType())
- continue;
- } else if (compilationUnit != m_compiledData) {
- continue;
- }
- ref->setTypePropertyCache(QQmlPropertyCache::ConstPtr());
- ref->setCompilationUnit(QQmlRefPointer<QV4::ExecutableCompilationUnit>());
+ const QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
+ if (error.isValid()) {
+ setError(error);
+ qDeleteAll(resolvedTypeCache);
+ return;
}
-
- m_compiledData.reset();
}
}
@@ -809,36 +862,8 @@ void QQmlTypeData::allDependenciesDone()
{
QQmlTypeLoader::Blob::allDependenciesDone();
- if (!m_typesResolved) {
- // Check that all imports were resolved
- QList<QQmlError> errors;
- auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
- for ( ; it != end; ++it) {
- if ((*it)->priority == 0) {
- // This import was not resolved
- for (auto keyIt = m_unresolvedImports.constBegin(),
- keyEnd = m_unresolvedImports.constEnd();
- keyIt != keyEnd; ++keyIt) {
- const PendingImportPtr &import = *keyIt;
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
- error.setUrl(m_importCache->baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(
- import->location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
- import->location.column()));
- errors.prepend(error);
- }
- }
- }
- if (errors.size()) {
- setError(errors);
- return;
- }
-
+ if (!m_typesResolved)
resolveTypes();
- m_typesResolved = true;
- }
}
void QQmlTypeData::downloadProgressChanged(qreal p)
@@ -889,8 +914,39 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
}
}
-void QQmlTypeData::resolveTypes()
+bool QQmlTypeData::resolveTypes()
{
+ Q_ASSERT(!m_typesResolved);
+
+ // Check that all imports were resolved
+ QList<QQmlError> errors;
+ auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
+ for ( ; it != end; ++it) {
+ const PendingImportPtr &import = *it;
+ if (import->priority != 0)
+ continue;
+
+ // If the import was potentially remote and all the network requests have failed,
+ // we now know that there is no qmldir. We can register its types.
+ if (registerPendingTypes(import))
+ continue;
+
+ // This import was not resolved
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
+ error.setUrl(m_importCache->baseUrl());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.column()));
+ errors.prepend(error);
+ }
+
+ if (errors.size()) {
+ setError(errors);
+ return false;
+ }
+
// Add any imported scripts to our resolved set
const auto resolvedScripts = m_importCache->resolvedScripts();
for (const QQmlImports::ScriptReference &script : resolvedScripts) {
@@ -927,7 +983,7 @@ void QQmlTypeData::resolveTypes()
QTypeRevision version = csRef.version;
if (!resolveType(typeName, version, ref, -1, -1, true, QQmlType::CompositeSingletonType))
- return;
+ return false;
if (ref.type.isCompositeSingleton()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
@@ -943,7 +999,7 @@ void QQmlTypeData::resolveTypes()
}
}
- for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
+ for (auto unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
unresolvedRef != end; ++unresolvedRef) {
TypeReference ref; // resolved reference
@@ -959,7 +1015,7 @@ void QQmlTypeData::resolveTypes()
if (!resolveType(name, version, ref, unresolvedRef->location.line(),
unresolvedRef->location.column(), reportErrors,
QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
- return;
+ return false;
if (ref.type.isComposite() && !ref.selfReference) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
@@ -969,7 +1025,7 @@ void QQmlTypeData::resolveTypes()
auto containingType = ref.type.containingType();
if (containingType.isValid()) {
auto const url = containingType.sourceUrl();
- if (url.isValid()) {
+ if (!url.isEmpty() && url != finalUrl()) {
auto typeData = typeLoader()->getType(url);
ref.typeData = typeData;
addDependency(typeData.data());
@@ -985,8 +1041,11 @@ void QQmlTypeData::resolveTypes()
}
// ### this allows enums to work without explicit import or instantiation of the type
- if (!m_implicitImportLoaded)
- loadImplicitImport();
+ if (!m_implicitImportLoaded && !loadImplicitImport())
+ return false;
+
+ m_typesResolved = true;
+ return true;
}
QQmlError QQmlTypeData::buildTypeResolutionCaches(
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index b067dea5bf..00007e1433 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -15,6 +15,7 @@
// We mean it.
//
+#include <private/qqmlsourcecoordinate_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qv4executablecompilationunit_p.h>
@@ -87,7 +88,7 @@ private:
bool loadFromSource();
void restoreIR(QV4::CompiledData::CompilationUnit &&unit);
void continueLoadFromIR();
- void resolveTypes();
+ bool resolveTypes();
QQmlError buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
QV4::ResolvedTypeReferenceMap *resolvedTypeCache
@@ -136,6 +137,27 @@ private:
bool m_implicitImportLoaded;
bool loadImplicitImport();
+ bool checkScripts();
+ bool checkDependencies();
+
+ template<typename Reference>
+ void createError(const Reference &ref, const QString &message, QList<QQmlError> errors)
+ {
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(ref.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(ref.location.column()));
+ error.setDescription(message);
+ errors.prepend(std::move(error));
+ setError(errors);
+ }
+
+ void createError(const TypeReference &type, const QString &message);
+ void createError(const ScriptReference &script, const QString &message);
+
+ bool checkCompositeSingletons();
+ void createQQmlType();
+ bool rebuildFromSource();
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 68c1163c8c..cf861cfd65 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -681,67 +681,59 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
// If there is a qmldir we cannot see, yet, then we have to wait.
// The qmldir might contain import directives.
- if (qmldirResult != QQmlImportDatabase::QmldirInterceptedToRemote && (
- // Major version of module already registered:
- // We believe that the registration is complete.
- QQmlMetaType::typeModule(import->uri, import->version)
+ // TODO: This should trigger on any potentially remote URLs, not only intercepted ones.
+ // However, fixing this would open the door for follow-up problems while providing
+ // rather limited benefits.
+ if (qmldirResult != QQmlImportDatabase::QmldirInterceptedToRemote
+ && registerPendingTypes(import)) {
+ if (m_importCache->addLibraryImport(
+ importDatabase, import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags, import->precedence, errors).isValid()) {
+ return true;
+ }
- // Otherwise, try to register further module types.
- || QQmlMetaType::qmlRegisterModuleTypes(import->uri)
+ return false;
+ }
- // Otherwise, there is no way to register any further types.
- // Try with any module of that name.
- || QQmlMetaType::latestModuleVersion(import->uri).isValid())) {
+ // We haven't yet resolved this import
+ m_unresolvedImports << import;
- if (!m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags, import->precedence, errors).isValid()) {
- return false;
- }
- } else {
- // We haven't yet resolved this import
- m_unresolvedImports << import;
-
- const QQmlEngine *engine = typeLoader()->engine();
- const bool hasInterceptors
- = !(QQmlEnginePrivate::get(engine)->urlInterceptors.isEmpty());
-
- // Query any network import paths for this library.
- // Interceptor might redirect local paths.
- QStringList remotePathList = importDatabase->importPathList(
- hasInterceptors ? QQmlImportDatabase::LocalOrRemote
- : QQmlImportDatabase::Remote);
- if (!remotePathList.isEmpty()) {
- // Add this library and request the possible locations for it
- const QTypeRevision version = m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags | QQmlImports::ImportIncomplete,
- import->precedence, errors);
-
- if (!version.isValid())
- return false;
+ // Add this library and request the possible locations for it
+ const QTypeRevision version = m_importCache->addLibraryImport(
+ importDatabase, import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence,
+ errors);
+
+ if (!version.isValid())
+ return false;
- // Use more specific version for finding the qmldir if possible
- if (version.hasMajorVersion())
- import->version = version;
-
- // Probe for all possible locations
- int priority = 0;
- const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
- import->uri, remotePathList, import->version);
- for (const QString &qmldirPath : qmlDirPaths) {
- if (hasInterceptors) {
- QUrl url = engine->interceptUrl(
- QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
- QQmlAbstractUrlInterceptor::QmldirFile);
- if (!QQmlFile::isLocalFile(url)
- && !fetchQmldir(url, import, ++priority, errors)) {
- return false;
- }
- } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ // Use more specific version for finding the qmldir if possible
+ if (version.hasMajorVersion())
+ import->version = version;
+
+ const QQmlEngine *engine = typeLoader()->engine();
+ const bool hasInterceptors = !(QQmlEnginePrivate::get(engine)->urlInterceptors.isEmpty());
+
+ // Query any network import paths for this library.
+ // Interceptor might redirect local paths.
+ QStringList remotePathList = importDatabase->importPathList(
+ hasInterceptors ? QQmlImportDatabase::LocalOrRemote : QQmlImportDatabase::Remote);
+ if (!remotePathList.isEmpty()) {
+ // Probe for all possible locations
+ int priority = 0;
+ const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
+ import->uri, remotePathList, import->version);
+ for (const QString &qmldirPath : qmlDirPaths) {
+ if (hasInterceptors) {
+ QUrl url = engine->interceptUrl(
+ QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
+ QQmlAbstractUrlInterceptor::QmldirFile);
+ if (!QQmlFile::isLocalFile(url)
+ && !fetchQmldir(url, import, ++priority, errors)) {
return false;
}
-
+ } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ return false;
}
}
}
@@ -749,6 +741,21 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
return true;
}
+bool QQmlTypeLoader::Blob::registerPendingTypes(const PendingImportPtr &import)
+{
+ return
+ // Major version of module already registered:
+ // We believe that the registration is complete.
+ QQmlMetaType::typeModule(import->uri, import->version)
+
+ // Otherwise, try to register further module types.
+ || QQmlMetaType::qmlRegisterModuleTypes(import->uri)
+
+ // Otherwise, there is no way to register any further types.
+ // Try with any module of that name.
+ || QQmlMetaType::latestModuleVersion(import->uri).isValid();
+}
+
bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import,
QQmlImports::ImportFlags flags, QList<QQmlError> *errors)
{
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 854778125a..0c6df56d5a 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -102,6 +102,9 @@ public:
QQmlImports::ImportFlags flags, QList<QQmlError> *errors);
protected:
+
+ bool registerPendingTypes(const PendingImportPtr &import);
+
bool loadDependentImports(
const QList<QQmlDirParser::Import> &imports, const QString &qualifier,
QTypeRevision version, quint16 precedence, QQmlImports::ImportFlags flags,
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 6bd26f65dd..b036066f2f 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -570,6 +570,13 @@ QMetaType QQmlValueTypeWrapper::type() const
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
{
bool destructGadgetOnExit = false;
+ auto cleanup = qScopeGuard([&]() {
+ if (destructGadgetOnExit) {
+ d()->valueType()->metaType().destruct(d()->gadgetPtr());
+ d()->setGadgetPtr(nullptr);
+ }
+ });
+
Q_ALLOCA_DECLARE(void, gadget);
if (d()->isReference()) {
if (!d()->gadgetPtr()) {
@@ -586,11 +593,6 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
int status = -1;
void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
-
- if (destructGadgetOnExit) {
- d()->valueType()->metaType().destruct(d()->gadgetPtr());
- d()->setGadgetPtr(nullptr);
- }
return true;
}
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 94c2c254c0..e27ab165be 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -119,8 +119,9 @@ void ResolvedList::append(QObject *o) const
QV4::ScopedObject object(scope, m_list);
QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false);
+ QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o));
arrayData->vtable()->put(
- object, length, QV4::QObjectWrapper::wrap(scope.engine, o));
+ object, length, wrappedObject);
}
QObject *ResolvedList::at(qsizetype i) const
@@ -134,7 +135,8 @@ void ResolvedList::replace(qsizetype i, QObject *o) const
{
QV4::Scope scope(m_list->internalClass->engine);
QV4::ScopedObject object(scope, m_list);
- m_list->arrayData->vtable()->put(object, i, QV4::QObjectWrapper::wrap(scope.engine, o));
+ QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o));
+ m_list->arrayData->vtable()->put(object, i, wrappedObject);
}
static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
diff --git a/src/qml/qmltc/supportlibrary/qqmlcpptypehelpers_p.h b/src/qml/qmltc/supportlibrary/qqmlcpptypehelpers_p.h
index 2470d87efe..bd3b987624 100644
--- a/src/qml/qmltc/supportlibrary/qqmlcpptypehelpers_p.h
+++ b/src/qml/qmltc/supportlibrary/qqmlcpptypehelpers_p.h
@@ -1,8 +1,8 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QQMLCPPTYPEHELPERS_H
-#define QQMLCPPTYPEHELPERS_H
+#ifndef QQMLCPPTYPEHELPERS_P_H
+#define QQMLCPPTYPEHELPERS_P_H
//
// W A R N I N G
@@ -25,4 +25,4 @@ using passByConstRefOrValue =
std::conditional_t<((sizeof(T) > 3 * sizeof(void *)) || !std::is_trivial_v<T>), const T &,
T>;
-#endif // QQMLCPPTYPEHELPERS_H
+#endif // QQMLCPPTYPEHELPERS_P_H
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index f6513c60a1..437d9b06f0 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -57,10 +57,8 @@ public:
silentDestroy(oldKind);
switch (newKind) {
case QQmlBindEntryKind::V4Value:
- new (&v4Value) QV4::PersistentValue(std::move(other.v4Value));
- break;
case QQmlBindEntryKind::Variant:
- new (&variant) QVariant(std::move(other.variant));
+ new (&v4Value) QV4::PersistentValue(std::move(other.v4Value));
break;
case QQmlBindEntryKind::Binding:
new (&binding) QQmlAnyBinding(std::move(other.binding));
@@ -77,10 +75,8 @@ public:
silentDestroy(oldKind);
switch (newKind) {
case QQmlBindEntryKind::V4Value:
- new (&v4Value) QV4::PersistentValue(other.v4Value);
- break;
case QQmlBindEntryKind::Variant:
- new (&variant) QVariant(other.variant);
+ new (&v4Value) QV4::PersistentValue(other.v4Value);
break;
case QQmlBindEntryKind::Binding:
new (&binding) QQmlAnyBinding(other.binding);
@@ -95,10 +91,8 @@ public:
{
switch (kind) {
case QQmlBindEntryKind::V4Value:
- v4Value.~PersistentValue();
- break;
case QQmlBindEntryKind::Variant:
- variant.~QVariant();
+ v4Value.~PersistentValue();
break;
case QQmlBindEntryKind::Binding:
binding.~QQmlAnyBinding();
@@ -109,10 +103,11 @@ public:
return QQmlBindEntryKind::None;
}
- [[nodiscard]] QQmlBindEntryKind set(QVariant v, QQmlBindEntryKind oldKind)
+ [[nodiscard]] QQmlBindEntryKind set(
+ QV4::ExecutionEngine *engine, const QVariant &v, QQmlBindEntryKind oldKind)
{
silentDestroy(oldKind);
- new (&variant) QVariant(std::move(v));
+ new (&v4Value) QV4::PersistentValue(engine, engine->fromVariant(v));
return QQmlBindEntryKind::Variant;
}
@@ -123,6 +118,13 @@ public:
return QQmlBindEntryKind::V4Value;
}
+ [[nodiscard]] QQmlBindEntryKind setVariant(QV4::PersistentValue v, QQmlBindEntryKind oldKind)
+ {
+ silentDestroy(oldKind);
+ new (&v4Value) QV4::PersistentValue(std::move(v));
+ return QQmlBindEntryKind::Variant;
+ }
+
[[nodiscard]] QQmlBindEntryKind set(QQmlAnyBinding v, QQmlBindEntryKind oldKind)
{
silentDestroy(oldKind);
@@ -131,7 +133,6 @@ public:
}
QV4::PersistentValue v4Value;
- QVariant variant;
QQmlAnyBinding binding;
private:
@@ -558,14 +559,22 @@ QVariant QQmlBind::value() const
if (!d->lastIsTarget)
return QVariant();
Q_ASSERT(d->entries.last().currentKind == QQmlBindEntryKind::Variant);
- return d->entries.last().current.variant;
+ const auto &v4Value = d->entries.last().current.v4Value;
+ QV4::ExecutionEngine *engine = v4Value.engine();
+ return engine->toVariant(*v4Value.valueRef(), QMetaType());
}
void QQmlBind::setValue(const QVariant &v)
{
Q_D(QQmlBind);
QQmlBindEntry *targetEntry = d->targetEntry();
- targetEntry->currentKind = targetEntry->current.set(v, targetEntry->currentKind);
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning() << "QQmlBind must be created in a QML context";
+ return;
+ }
+ targetEntry->currentKind
+ = targetEntry->current.set(engine->handle(), v, targetEntry->currentKind);
prepareEval();
}
@@ -843,8 +852,8 @@ void QQmlBindPrivate::decodeBinding(
return;
}
- const auto setVariant = [&entry](QVariant var) {
- entry.currentKind = entry.current.set(std::move(var), entry.currentKind);
+ const auto setVariant = [&entry](QV4::PersistentValue value) {
+ entry.currentKind = entry.current.setVariant(value, entry.currentKind);
};
const auto setBinding = [&entry](QQmlAnyBinding binding) {
@@ -874,16 +883,20 @@ void QQmlBindPrivate::decodeBinding(
}
break;
case QV4::CompiledData::Binding::Type_String:
- setVariant(compilationUnit->bindingValueAsString(binding));
+ setVariant(QV4::PersistentValue(
+ compilationUnit->engine,
+ compilationUnit->runtimeStrings[binding->stringIndex]->asReturnedValue()));
break;
case QV4::CompiledData::Binding::Type_Number:
- setVariant(compilationUnit->bindingValueAsNumber(binding));
+ setVariant(QV4::PersistentValue(
+ compilationUnit->engine,
+ compilationUnit->constants[binding->value.constantValueIndex].asReturnedValue()));
break;
case QV4::CompiledData::Binding::Type_Boolean:
- setVariant(binding->valueAsBoolean());
+ setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode(binding->value.b)));
break;
case QV4::CompiledData::Binding::Type_Null:
- setVariant(QVariant::fromValue(nullptr));
+ setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode::null()));
break;
case QV4::CompiledData::Binding::Type_Object:
case QV4::CompiledData::Binding::Type_Invalid:
@@ -1038,7 +1051,9 @@ void QQmlBind::eval()
case QQmlBindEntryKind::Variant:
if (d->restoreValue) {
QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active
- entry.prop.write(entry.previous.variant);
+ const QV4::PersistentValue &v4Value = entry.previous.v4Value;
+ entry.prop.write(v4Value.engine()->toVariant(
+ *v4Value.valueRef(), entry.prop.propertyMetaType()));
entry.clearPrev();
}
break;
@@ -1066,7 +1081,8 @@ void QQmlBind::eval()
QV4::PersistentValue(vmemo->engine, retVal), entry.previousKind);
} else {
// nope, use the meta object to get a QVariant
- entry.previousKind = entry.previous.set(entry.prop.read(), entry.previousKind);
+ entry.previousKind = entry.previous.set(
+ propPriv->engine->handle(), entry.prop.read(), entry.previousKind);
}
}
}
@@ -1085,9 +1101,12 @@ void QQmlBind::eval()
if (!entry.prop.isValid())
continue;
switch (entry.currentKind) {
- case QQmlBindEntryKind::Variant:
- entry.prop.write(entry.current.variant);
+ case QQmlBindEntryKind::Variant: {
+ const QV4::PersistentValue &v4Value = entry.current.v4Value;
+ entry.prop.write(
+ v4Value.engine()->toVariant(*v4Value.valueRef(), entry.prop.propertyMetaType()));
break;
+ }
case QQmlBindEntryKind::Binding:
Q_ASSERT(!d->delayed);
entry.current.binding.installOn(entry.prop);
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
index d5bbae037f..e95ad114e4 100644
--- a/src/qmlcompiler/qqmljsbasicblocks.cpp
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -216,7 +216,7 @@ void QQmlJSBasicBlocks::populateReaderLocations()
m_readerLocations.erase(reader);
// If it's not a label and has no side effects, we can drop the instruction.
- if (!it->second.hasSideEffects) {
+ if (!it->second.hasInternalSideEffects) {
if (!it->second.readRegisters.isEmpty()) {
it->second.readRegisters.clear();
erasedReaders = true;
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 01ef6a95e6..709dcbea8f 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -2561,7 +2561,7 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
// If the instruction has no side effects and doesn't write any register, it's dead.
// We might still need the label, though, and the source code comment.
- if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) {
+ if (!m_state.hasInternalSideEffects() && changedRegisterVariable().isEmpty()) {
generateJumpCodeWithTypeConversions(0);
return SkipInstruction;
}
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index b35adfb66a..7ca9744bb1 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -64,7 +64,8 @@ public:
QQmlJSRegisterContent changedRegister;
int changedRegisterIndex = InvalidRegister;
- bool hasSideEffects = false;
+ bool hasInternalSideEffects = false;
+ bool hasExternalSideEffects = false;
bool isRename = false;
};
@@ -158,16 +159,33 @@ public:
return m_readRegisters.contains(registerIndex);
}
- bool hasSideEffects() const { return m_hasSideEffects; }
- void setHasSideEffects(bool hasSideEffects) {
- m_hasSideEffects = hasSideEffects;
- if (!hasSideEffects)
+ bool hasInternalSideEffects() const { return m_hasInternalSideEffects; }
+ bool hasExternalSideEffects() const { return m_hasExternalSideEffects; }
+
+ void resetSideEffects()
+ {
+ m_hasInternalSideEffects = false;
+ m_hasExternalSideEffects = false;
+ }
+
+ void applyExternalSideEffects(bool hasExternalSideEffects)
+ {
+ if (!hasExternalSideEffects)
return;
for (auto it = registers.begin(), end = registers.end(); it != end; ++it)
it.value().affectedBySideEffects = true;
}
+ void setHasInternalSideEffects() { m_hasInternalSideEffects = true; }
+ void setHasExternalSideEffects()
+ {
+ m_hasExternalSideEffects = true;
+ m_hasInternalSideEffects = true;
+ applyExternalSideEffects(true);
+ }
+
+
bool isRename() const { return m_isRename; }
void setIsRename(bool isRename) { m_isRename = isRename; }
@@ -178,11 +196,36 @@ public:
return m_readRegisters.begin().key();
}
+ void applyAnnotation(const InstructionAnnotation &annotation)
+ {
+ m_readRegisters = annotation.readRegisters;
+
+ m_hasInternalSideEffects = annotation.hasInternalSideEffects;
+ m_hasExternalSideEffects = annotation.hasExternalSideEffects;
+ m_isRename = annotation.isRename;
+
+ for (auto it = annotation.typeConversions.constBegin(),
+ end = annotation.typeConversions.constEnd(); it != end; ++it) {
+ Q_ASSERT(it.key() != InvalidRegister);
+ registers[it.key()] = it.value();
+ }
+
+ if (annotation.changedRegisterIndex != InvalidRegister)
+ setRegister(annotation.changedRegisterIndex, annotation.changedRegister);
+ }
+
private:
VirtualRegisters m_readRegisters;
QQmlJSRegisterContent m_changedRegister;
int m_changedRegisterIndex = InvalidRegister;
- bool m_hasSideEffects = false;
+
+ // If the instruction's value is unused, we still cannot optimize it out.
+ bool m_hasInternalSideEffects = false;
+
+ // Side effect created by calls to other functions or writes to properties,
+ // affects tracked value types and lists. Implies the effects of Internal.
+ bool m_hasExternalSideEffects = false;
+
bool m_isRename = false;
};
@@ -245,23 +288,12 @@ protected:
= oldState.changedRegister();
}
- if (instruction == annotations.constEnd())
- return newState;
-
- newState.setHasSideEffects(instruction->second.hasSideEffects);
- newState.setReadRegisters(instruction->second.readRegisters);
- newState.setIsRename(instruction->second.isRename);
+ // Side effects are applied at the end of an instruction: An instruction with side
+ // effects can still read its registers before the side effects happen.
+ newState.applyExternalSideEffects(oldState.hasExternalSideEffects());
- for (auto it = instruction->second.typeConversions.begin(),
- end = instruction->second.typeConversions.end(); it != end; ++it) {
- Q_ASSERT(it.key() != InvalidRegister);
- newState.registers[it.key()] = it.value();
- }
-
- if (instruction->second.changedRegisterIndex != InvalidRegister) {
- newState.setRegister(instruction->second.changedRegisterIndex,
- instruction->second.changedRegister);
- }
+ if (instruction != annotations.constEnd())
+ newState.applyAnnotation(instruction->second);
return newState;
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 4bc215add7..bd693cc619 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -18,6 +18,7 @@
#include <QtQml/private/qqmlirbuilder_p.h>
#include "qqmljsscope_p.h"
#include "qqmljsutils_p.h"
+#include "qqmlsa_p.h"
#include <algorithm>
#include <variant>
@@ -799,8 +800,8 @@ void QQmlJSImportVisitor::checkRequiredProperties()
}
}
- const auto isInComponent = [this](const QQmlJSScope::ConstPtr &requiredScope) {
- const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
+ const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
+ const auto isInComponent = [&](const QQmlJSScope::ConstPtr &requiredScope) {
for (auto s = requiredScope; s; s = s->parentScope()) {
if (s->isWrappedInImplicitComponent() || s->baseType() == compType)
return true;
@@ -811,9 +812,11 @@ void QQmlJSImportVisitor::checkRequiredProperties()
const auto requiredHasBinding = [](const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
const QString &propName) {
for (const auto &scope : scopesToSearch) {
+ if (scope->property(propName).isAlias())
+ continue;
const auto &[begin, end] = scope->ownPropertyBindings(propName);
for (auto it = begin; it != end; ++it) {
- if (!scope->property(propName).isAlias())
+ if (QQmlSA::isRegularBindingType(it->bindingType()))
return true;
}
}
@@ -880,16 +883,17 @@ void QQmlJSImportVisitor::checkRequiredProperties()
: u"here"_s;
if (!prevRequiredScope.isNull()) {
- auto sourceScope = prevRequiredScope->baseType();
- suggestion = QQmlJSFixSuggestion{
- "%1:%2:%3: Property marked as required in %4."_L1
- .arg(sourceScope->filePath())
- .arg(sourceScope->sourceLocation().startLine)
- .arg(sourceScope->sourceLocation().startColumn)
- .arg(requiredScopeName),
- sourceScope->sourceLocation()
- };
- suggestion->setFilename(sourceScope->filePath());
+ if (auto sourceScope = prevRequiredScope->baseType()) {
+ suggestion = QQmlJSFixSuggestion{
+ "%1:%2:%3: Property marked as required in %4."_L1
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation()
+ };
+ suggestion->setFilename(sourceScope->filePath());
+ }
} else {
message += " (marked as required by %1)"_L1.arg(requiredScopeName);
}
@@ -902,7 +906,8 @@ void QQmlJSImportVisitor::checkRequiredProperties()
for (const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent()
- || defScope->isComponentRootElement()) {
+ || defScope->isComponentRootElement()
+ || defScope->scopeType() != QQmlJSScope::QMLScope) {
continue;
}
@@ -917,6 +922,8 @@ void QQmlJSImportVisitor::checkRequiredProperties()
for (auto propertyIt = ownProperties.constBegin();
propertyIt != ownProperties.constEnd(); ++propertyIt) {
const QString propName = propertyIt.key();
+ if (descendant->hasOwnPropertyBindings(propName))
+ continue;
QQmlJSScope::ConstPtr prevRequiredScope;
for (QQmlJSScope::ConstPtr requiredScope : std::as_const(scopesToSearch)) {
@@ -1613,7 +1620,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
auto tryParseAlias = [&]() {
typeName.clear(); // type name is useless for alias here, so keep it empty
if (!publicMember->statement) {
- m_logger->log(QStringLiteral("Invalid alias expression – an initalizer is needed."),
+ m_logger->log(QStringLiteral("Invalid alias expression - an initalizer is needed."),
qmlSyntax, publicMember->memberType->firstSourceLocation()); // TODO: extend warning to cover until endSourceLocation
return;
}
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index 5067210261..29a0db05cf 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -361,9 +361,15 @@ qFindInlineComponents(QStringView typeName, const QQmlJSScope::ContextualTypes &
if (current->isInlineComponent() && current->inlineComponentName() == inlineComponentName) {
return { current, inlineComponentParent.revision };
}
+
// check alternatively the inline components at layer 1 in current and basetype, then at
// layer 2, etc...
- candidatesForInlineComponents.append(current->childScopes());
+ const auto &childScopes = current->childScopes();
+ for (const auto &child : childScopes) {
+ if (child->scopeType() == QQmlJSScope::ScopeType::QMLScope)
+ candidatesForInlineComponents.enqueue(child);
+ }
+
if (const auto base = current->baseType())
candidatesForInlineComponents.enqueue(base);
}
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index ead1813afe..600e40d389 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -72,7 +72,7 @@ void QQmlJSShadowCheck::generate_SetLookup(int index, int base)
QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type)
{
m_state = nextStateFromAnnotations(m_state, *m_annotations);
- return (m_state.hasSideEffects() || m_state.changedRegisterIndex() != InvalidRegister)
+ return (m_state.hasInternalSideEffects() || m_state.changedRegisterIndex() != InvalidRegister)
? ProcessInstruction
: SkipInstruction;
}
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 06b49d067d..bff83817fe 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -105,7 +105,7 @@ void QQmlJSTypePropagator::generate_Ret()
addReadAccumulator(m_returnType);
}
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
m_state.skipInstructionsUntilNextJumpTarget = true;
}
@@ -631,7 +631,7 @@ void QQmlJSTypePropagator::generate_StoreNameCommon(int nameIndex)
m_function->qmlScope, getCurrentSourceLocation());
}
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
if (type.property().reset().isEmpty())
@@ -725,7 +725,7 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
// Writing to a JS array can have side effects all over the place since it's
// passed by reference.
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
return;
}
@@ -742,7 +742,7 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
// If we're writing to a list retrieved from a property, that _should_ have side effects,
// but currently the QML engine doesn't implement them.
// TODO: Figure out the above and accurately set the flag.
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
}
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
@@ -963,7 +963,7 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
addReadAccumulator(property);
addReadRegister(base, callBase);
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
}
void QQmlJSTypePropagator::generate_SetLookup(int index, int base)
@@ -1000,7 +1000,7 @@ void QQmlJSTypePropagator::generate_Resume(int)
void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(name)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -1009,7 +1009,7 @@ void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(name)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -1075,7 +1075,7 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
for (int i = 1; i < argc; ++i)
addReadRegister(argv + i, stringType);
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
setAccumulator(voidType);
return;
}
@@ -1087,7 +1087,7 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
for (int i = 0; i < argc; ++i)
addReadRegister(argv + i, jsValueType);
setAccumulator(jsValueType);
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
return;
}
@@ -1238,14 +1238,17 @@ void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &c
}
void QQmlJSTypePropagator::mergeRegister(
- int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
+ int index, const VirtualRegister &a, const VirtualRegister &b)
{
- auto merged = m_typeResolver->merge(a, b);
+ const VirtualRegister merged = {
+ m_typeResolver->merge(a.content, b.content),
+ a.affectedBySideEffects || b.affectedBySideEffects,
+ };
- Q_ASSERT(merged.isValid());
- Q_ASSERT(merged.isConversion());
+ Q_ASSERT(merged.content.isValid());
+ Q_ASSERT(merged.content.isConversion());
- auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
+ auto tryPrevStateConversion = [this](int index, const VirtualRegister &merged) -> bool {
auto it = m_prevStateAnnotations.find(currentInstructionOffset());
if (it == m_prevStateAnnotations.end())
return false;
@@ -1259,22 +1262,31 @@ void QQmlJSTypePropagator::mergeRegister(
Q_ASSERT(lastTry.content.isValid());
Q_ASSERT(lastTry.content.isConversion());
- if (!m_typeResolver->equals(lastTry.content.conversionResult(), merged.conversionResult())
- || lastTry.content.conversionOrigins() != merged.conversionOrigins()) {
+ if (!m_typeResolver->equals(lastTry.content.conversionResult(),
+ merged.content.conversionResult())
+ || lastTry.content.conversionOrigins() != merged.content.conversionOrigins()
+ || lastTry.affectedBySideEffects != merged.affectedBySideEffects) {
return false;
}
// We don't need to track it again if we've come to the same conclusion before.
m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
+
+ // Do not reset the side effects
+ Q_ASSERT(!m_state.registers[index].affectedBySideEffects || lastTry.affectedBySideEffects);
+
m_state.registers[index] = lastTry;
return true;
};
if (!tryPrevStateConversion(index, merged)) {
- merged = m_typeResolver->tracked(merged);
- Q_ASSERT(merged.isValid());
- m_state.annotations[currentInstructionOffset()].typeConversions[index].content = merged;
- m_state.registers[index].content = merged;
+ const VirtualRegister cloned = {
+ m_typeResolver->tracked(merged.content),
+ merged.affectedBySideEffects,
+ };
+ Q_ASSERT(cloned.content.isValid());
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = cloned;
+ m_state.registers[index] = cloned;
}
}
@@ -1316,7 +1328,7 @@ void QQmlJSTypePropagator::propagateCall(
if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
const auto types = match.parameters();
for (int i = 0; i < argc; ++i) {
if (i < types.size()) {
@@ -1478,7 +1490,7 @@ void QQmlJSTypePropagator::generate_CallName(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(argc)
Q_UNUSED(argv)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -1523,7 +1535,7 @@ void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(int index, int
void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -1533,7 +1545,7 @@ void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int
void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -1543,7 +1555,7 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(func)
Q_UNUSED(argv)
@@ -1554,7 +1566,7 @@ void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasExternalSideEffects();
Q_UNUSED(func)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -1563,20 +1575,20 @@ void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int
void QQmlJSTypePropagator::generate_SetUnwindHandler(int offset)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindDispatch()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
Q_UNUSED(level)
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -1591,7 +1603,7 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
m_state.skipInstructionsUntilNextJumpTarget = true;
}
@@ -1602,18 +1614,18 @@ void QQmlJSTypePropagator::generate_GetException()
void QQmlJSTypePropagator::generate_SetException()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CreateCallContext()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
}
void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
Q_UNUSED(index)
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
@@ -1621,39 +1633,39 @@ void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
void QQmlJSTypePropagator::generate_PushWithContext()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushBlockContext(int index)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CloneBlockContext()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushScriptContext(int index)
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopScriptContext()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopContext()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
}
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
@@ -1784,7 +1796,7 @@ void QQmlJSTypePropagator::generate_Jump(int offset)
{
saveRegisterStateForJump(offset);
m_state.skipInstructionsUntilNextJumpTarget = true;
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
}
void QQmlJSTypePropagator::generate_JumpTrue(int offset)
@@ -1796,7 +1808,7 @@ void QQmlJSTypePropagator::generate_JumpTrue(int offset)
return;
}
saveRegisterStateForJump(offset);
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
@@ -1809,14 +1821,14 @@ void QQmlJSTypePropagator::generate_JumpFalse(int offset)
return;
}
saveRegisterStateForJump(offset);
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpNoException(int offset)
{
saveRegisterStateForJump(offset);
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
}
void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
@@ -1827,7 +1839,7 @@ void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
void QQmlJSTypePropagator::generate_CheckException()
{
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
}
void QQmlJSTypePropagator::recordEqualsNullType()
@@ -2199,7 +2211,7 @@ void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg
{
Q_UNUSED(firstReg)
Q_UNUSED(count)
- m_state.setHasSideEffects(true);
+ m_state.setHasInternalSideEffects();
// Ignore. We reject uninitialized values anyway.
}
@@ -2254,8 +2266,8 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
registerIt != end; ++registerIt) {
const int registerIndex = registerIt.key();
- auto newType = registerIt.value().content;
- if (!newType.isValid()) {
+ const VirtualRegister &newType = registerIt.value();
+ if (!newType.content.isValid()) {
setError(u"When reached from offset %1, %2 is undefined"_s
.arg(stateToMerge.originatingOffset)
.arg(registerName(registerIndex)));
@@ -2264,7 +2276,7 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
auto currentRegister = m_state.registers.find(registerIndex);
if (currentRegister != m_state.registers.end())
- mergeRegister(registerIndex, newType, currentRegister.value().content);
+ mergeRegister(registerIndex, newType, currentRegister.value());
else
mergeRegister(registerIndex, newType, newType);
}
@@ -2279,7 +2291,8 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
currentInstruction.changedRegister = m_state.changedRegister();
currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
currentInstruction.readRegisters = m_state.takeReadRegisters();
- currentInstruction.hasSideEffects = m_state.hasSideEffects();
+ currentInstruction.hasExternalSideEffects = m_state.hasExternalSideEffects();
+ currentInstruction.hasInternalSideEffects = m_state.hasInternalSideEffects();
currentInstruction.isRename = m_state.isRename();
switch (instr) {
@@ -2323,7 +2336,7 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
&& instr != QV4::Moth::Instr::Type::DeadTemporalZoneCheck) {
// An instruction needs to have side effects or write to another register otherwise it's a
// noop. DeadTemporalZoneCheck is not needed by the compiler and is ignored.
- Q_ASSERT(m_state.hasSideEffects() || m_state.changedRegisterIndex() != -1);
+ Q_ASSERT(m_state.hasInternalSideEffects() || m_state.changedRegisterIndex() != -1);
}
if (m_state.changedRegisterIndex() != InvalidRegister) {
@@ -2335,7 +2348,7 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
m_state.clearChangedRegister();
}
- m_state.setHasSideEffects(false);
+ m_state.resetSideEffects();
m_state.setIsRename(false);
m_state.setReadRegisters(VirtualRegisters());
}
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index dd0c0ec750..99a421a4a1 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -227,7 +227,7 @@ private:
void setAccumulator(const QQmlJSRegisterContent &content);
void setRegister(int index, const QQmlJSRegisterContent &content);
- void mergeRegister(int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b);
+ void mergeRegister(int index, const VirtualRegister &a, const VirtualRegister &b);
void addReadRegister(int index, const QQmlJSRegisterContent &convertTo);
void addReadAccumulator(const QQmlJSRegisterContent &convertTo)
diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp
index 7a209f52c1..388a5fe599 100644
--- a/src/qmlcompiler/qqmlsa.cpp
+++ b/src/qmlcompiler/qqmlsa.cpp
@@ -360,6 +360,13 @@ void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &p
+ QString::number(location.startColumn),
qmlPlugin, location);
}
+
+bool isRegularBindingType(QQmlJSMetaPropertyBinding::BindingType type)
+{
+ return type >= QQmlJSMetaPropertyBinding::BindingType::BoolLiteral
+ && type <= QQmlJSMetaPropertyBinding::BindingType::Object;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmlsa_p.h b/src/qmlcompiler/qqmlsa_p.h
index 6bbbe9441b..18caee3640 100644
--- a/src/qmlcompiler/qqmlsa_p.h
+++ b/src/qmlcompiler/qqmlsa_p.h
@@ -183,6 +183,9 @@ public:
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
QQmlJS::SourceLocation location) override;
};
+
+bool isRegularBindingType(QQmlJSMetaPropertyBinding::BindingType type);
+
}
#define QmlLintPluginInterface_iid "org.qt-project.Qt.Qml.SA.LintPlugin/1.0"
diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index c04be1cae7..67ba6339be 100644
--- a/src/qmlmodels/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -625,7 +625,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
if (role.type == ListLayout::Role::QObject)
roleIndex = e->setQObjectProperty(role, wrapper);
} else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
- o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
+ // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe
+ QV4::Value::fromReturnedValue(o->asReturnedValue()),
+ QMetaType::fromType<QUrl>(), true);
maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
QUrl qurl = maybeUrl.toUrl();
@@ -717,7 +719,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
e->setQObjectPropertyFast(r, wrapper);
} else {
QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
- o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
+ // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe
+ QV4::Value::fromReturnedValue(o->asReturnedValue()),
+ QMetaType::fromType<QUrl>(), true);
if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
const QUrl qurl = maybeUrl.toUrl();
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
@@ -1539,7 +1543,9 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
roleIndex = setVariantMapProperty(role, o);
} else if (role.type == ListLayout::Role::Url) {
QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
- o.asReturnedValue(), QMetaType::fromType<QUrl>(), true);
+ // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe
+ QV4::Value::fromReturnedValue(o.asReturnedValue()),
+ QMetaType::fromType<QUrl>(), true);
if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
roleIndex = setUrlProperty(role, maybeUrl.toUrl());
}
@@ -1746,9 +1752,11 @@ PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *p
if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
auto size = recursiveListModel->count();
auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
+ QV4::ScopedValue val(scope);
for (auto i = 0; i < size; i++) {
- array->arrayPut(i, QJSValuePrivate::convertToReturnedValue(
- v4, recursiveListModel->get(i)));
+ val = QJSValuePrivate::convertToReturnedValue(
+ v4, recursiveListModel->get(i));
+ array->arrayPut(i, val);
}
pd->value = array;
} else {
@@ -2060,7 +2068,11 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen
m_agent = agent;
m_dynamicRoles = orig->m_dynamicRoles;
- m_layout = new ListLayout(orig->m_layout);
+ if (ListLayout *layout = orig->m_layout)
+ m_layout = new ListLayout(layout);
+ else
+ m_layout = new ListLayout;
+
m_listModel = new ListModel(m_layout, this);
if (m_dynamicRoles)
@@ -2080,12 +2092,13 @@ QQmlListModel::~QQmlListModel()
m_listModel->destroy();
delete m_listModel;
- if (m_mainThread && m_agent) {
+ if (m_mainThread && m_agent)
m_agent->modelDestroyed();
- m_agent->release();
- }
}
+ if (m_mainThread && m_agent)
+ m_agent->release();
+
m_listModel = nullptr;
delete m_layout;
@@ -2355,7 +2368,7 @@ void QQmlListModel::setDynamicRoles(bool enableDynamicRoles)
{
if (m_mainThread && m_agent == nullptr) {
if (enableDynamicRoles) {
- if (m_layout->roleCount())
+ if (m_layout && m_layout->roleCount())
qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty");
else
m_dynamicRoles = true;
@@ -2928,7 +2941,7 @@ bool QQmlListModelParser::applyProperty(
if (v4->hasException)
v4->catchException();
else
- QJSValuePrivate::setValue(&v, result->asReturnedValue());
+ QJSValuePrivate::setValue(&v, result);
value.setValue(v);
} else {
bool ok;
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index 8df631c6d3..40e4617382 100644
--- a/src/qmlworkerscript/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -194,6 +194,8 @@ bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
QV4::ExecutionEngine *QQuickWorkerScriptEnginePrivate::workerEngine(int id)
{
+ QMutexLocker locker(&m_lock);
+
const auto it = workers.find(id);
if (it == workers.end())
return nullptr;
@@ -347,19 +349,16 @@ QQmlError WorkerErrorEvent::error() const
QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent)
: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent))
{
- d->m_lock.lock();
connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection);
+ QMutexLocker locker(&d->m_lock);
start(QThread::LowestPriority);
d->m_wait.wait(&d->m_lock);
d->moveToThread(this);
- d->m_lock.unlock();
}
QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
{
- d->m_lock.lock();
QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent));
- d->m_lock.unlock();
//We have to force to cleanup the main thread's event queue here
//to make sure the main GUI release all pending locks/wait conditions which
@@ -408,23 +407,26 @@ int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
{
const int id = d->m_nextId++;
- d->m_lock.lock();
+ QMutexLocker locker(&d->m_lock);
d->workers.insert(id, owner);
- d->m_lock.unlock();
return id;
}
void QQuickWorkerScriptEngine::removeWorkerScript(int id)
{
- const auto it = d->workers.find(id);
- if (it == d->workers.end())
- return;
+ {
+ QMutexLocker locker(&d->m_lock);
+ const auto it = d->workers.find(id);
+ if (it == d->workers.end())
+ return;
- if (it->isT1()) {
- QV4::ExecutionEngine *engine = it->asT1();
- workerScriptExtension(engine)->owner = nullptr;
+ if (it->isT1())
+ workerScriptExtension(it->asT1())->owner = nullptr;
+ else
+ *it = static_cast<QQuickWorkerScript *>(nullptr);
}
+
QCoreApplication::postEvent(d, new WorkerRemoveEvent(id));
}
@@ -440,12 +442,14 @@ void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data)
void QQuickWorkerScriptEngine::run()
{
- d->m_lock.lock();
- d->m_wait.wakeAll();
- d->m_lock.unlock();
+ {
+ QMutexLocker locker(&d->m_lock);
+ d->m_wait.wakeAll();
+ }
exec();
+ QMutexLocker locker(&d->m_lock);
for (auto it = d->workers.begin(), end = d->workers.end(); it != end; ++it) {
if (it->isT1())
delete it->asT1();
diff --git a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc b/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
index 1ce20de5f0..876723d66e 100644
--- a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
@@ -8,10 +8,10 @@
anti-patterns.
\e qmllint is a tool shipped with Qt, that verifies the syntatic validity of
-QML files.
-It also warns about some QML anti-patterns. If you want to disable a specific
-warning type, you can find the appropriate flag for doing so by passing
-\c{--help} on the command line.
+QML files. You can \l{Using qmllint with CMake}{integrate qmllint into your build system}
+for ease of use. It also warns about some QML anti-patterns. If you want to
+disable a specific warning type, you can find the appropriate flag for doing so
+by passing \c{--help} on the command line.
By default, some issues will result in warnings that will be printed and result
in a non-zero exit code.
@@ -49,6 +49,11 @@ qmllint can warn you about code that cannot be compiled by \l{qmlsc}.
These warnigs are not enabled by default. In order to enable them specify
\c{--compiler warning} or adjust your settings file accordingly.
+\section2 Using qmllint with CMake
+For projects using the \l{qt6_add_qml_module}{qt_add_qml_module()} CMake API for creating QML
+modules, \l{Linting QML sources}{convenience targets} such as \c all_qmllint are created
+automatically. These run qmllint on all the QML files of a specific module or of the project.
+
\section2 Marking components and properties as deprecated
qmllint allows you to mark both properties and components as deprecated:
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 865fb8bf11..b0c55b4653 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -430,6 +430,9 @@ void QQuickAccessibleAttached::setIgnored(bool ignored)
{
if (this->ignored() != ignored && item()) {
item()->d_func()->isAccessible = !ignored;
+ QAccessibleEvent event(item(),
+ ignored ? QAccessible::ObjectDestroyed : QAccessible::ObjectCreated);
+ QAccessible::updateAccessibility(&event);
emit ignoredChanged();
}
}
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index ec339ccc3b..239b512b9b 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -6425,10 +6425,11 @@ void QQuickItem::setVisible(bool v)
This property holds whether the item receives mouse and keyboard events.
By default this is true.
- Setting this property directly affects the \c enabled value of child
- items. When set to \c false, the \c enabled values of all child items also
- become \c false. When set to \c true, the \c enabled values of child items
- are returned to \c true, unless they have explicitly been set to \c false.
+ Setting this property affects the \e effective \c enabled states of child items.
+ When an item's parent is not enabled, the (child) item is effectively disabled and
+ does not receive input events, even if its own \c enabled property is explicitly
+ set to \c true. The child \c enabled property keeps its value, but becomes
+ effective again only if all its ancestor items are enabled.
Setting this property to \c false automatically causes \l activeFocus to be
set to \c false, and this item will no longer receive keyboard events.
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index c1188ac4d7..684aa8f98e 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -42,7 +42,7 @@ public:
};
-class Q_AUTOTEST_EXPORT QQuickItemViewChangeSet
+class Q_QUICK_EXPORT QQuickItemViewChangeSet
{
public:
QQuickItemViewChangeSet();
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index b0b99334a2..33d801a2a3 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -660,8 +660,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints
for (const QEventPoint &p : std::as_const(touchPoints)) {
if (p.state() == QEventPoint::State::Released)
continue;
- const QPointF &currentPos = p.scenePosition();
- const QPointF &startPos = p.scenePressPosition();
+ const QPointF currentPos = mapFromScene(p.scenePosition());
+ const QPointF startPos = mapFromScene(p.scenePressPosition());
if (qAbs(currentPos.x() - startPos.x()) > dragThreshold)
offerGrab = true;
else if (qAbs(currentPos.y() - startPos.y()) > dragThreshold)
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 3cc63cece0..dbbdfec6cc 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -4866,8 +4866,8 @@ bool QQuickTableViewPrivate::canEdit(const QModelIndex tappedIndex, bool warn)
return false;
}
- if (auto const qaim = model->abstractItemModel()) {
- if (!(qaim->flags(tappedIndex) & Qt::ItemIsEditable)) {
+ if (auto const sourceModel = qaim(modelImpl())) {
+ if (!(sourceModel->flags(tappedIndex) & Qt::ItemIsEditable)) {
if (warn)
qmlWarning(q) << "cannot edit: QAbstractItemModel::flags(index) doesn't contain Qt::ItemIsEditable";
return false;
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 510ece726a..c5b87a7c30 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -742,7 +742,7 @@ void QQuickTextControl::processEvent(QEvent *e, const QTransform &transform)
case QEvent::ShortcutOverride:
if (d->interactionFlags & Qt::TextEditable) {
- QKeyEvent* ke = static_cast<QKeyEvent *>(e);
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
ke->setAccepted(isCommonTextEditShortcut(ke));
}
break;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 4471d6e4b9..57c12d2825 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1751,12 +1751,15 @@ QRectF QQuickTextEdit::cursorRectangle() const
bool QQuickTextEdit::event(QEvent *event)
{
Q_D(QQuickTextEdit);
- if (event->type() == QEvent::ShortcutOverride) {
- d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
- if (event->isAccepted())
- return true;
+ bool state = QQuickImplicitSizeItem::event(event);
+ if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(this);
+ if (!itemPriv->extra.isAllocated() || !itemPriv->extra->keyHandler) {
+ d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
+ }
+ state = true;
}
- return QQuickImplicitSizeItem::event(event);
+ return state;
}
/*!
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 08713eb026..495f99505f 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -49,6 +49,13 @@ Q_LOGGING_CATEGORY(lcQuickTextInput, "qt.quick.textInput")
and setting \l echoMode to an appropriate value enables TextInput to be used for
a password input field.
+ To react to the user accepting text via the Return or Enter keys, handle
+ the \l accepted() signal. When Return or Enter are pressed and the text
+ input loses focus, \l editingFinished() will be emitted. When the text is
+ edited in any way by the user, \l textEdited() is emitted. These signals
+ \l {Property change signals}{should be preferred} over \c textChanged()
+ in most cases.
+
On \macos, the Up/Down key bindings for Home/End are explicitly disabled.
If you want such bindings (on any platform), you will need to construct them in QML.
@@ -96,7 +103,8 @@ void QQuickTextInput::componentComplete()
the \l preeditText property. To get whole text displayed in the TextInput
use \l displayText property.
- \sa clear(), displayText, preeditText
+ \sa clear(), displayText, preeditText, accepted(), editingFinished(),
+ textEdited()
*/
QString QQuickTextInput::text() const
{
@@ -1184,6 +1192,8 @@ bool QQuickTextInput::hasAcceptableInput() const
Note that if there is a \l validator or \l inputMask set on the text
input, the signal will only be emitted if the input is in an acceptable
state.
+
+ \sa editingFinished(), textEdited()
*/
/*!
@@ -1195,6 +1205,8 @@ bool QQuickTextInput::hasAcceptableInput() const
inputMask set on the text input and enter/return is pressed, this
signal will only be emitted if the input follows
the inputMask and the validator returns an acceptable state.
+
+ \sa accepted(), textEdited()
*/
/*!
@@ -1204,6 +1216,8 @@ bool QQuickTextInput::hasAcceptableInput() const
This signal is emitted whenever the text is edited. Unlike \c textChanged(),
this signal is not emitted when the text is changed programmatically, for example,
by changing the value of the \c text property or by calling \c clear().
+
+ \sa accepted(), editingFinished()
*/
#if QT_CONFIG(im)
diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp
index 88373a1706..17e21a75f4 100644
--- a/src/quick/items/qquicktreeview.cpp
+++ b/src/quick/items/qquicktreeview.cpp
@@ -650,8 +650,10 @@ void QQuickTreeView::keyPressEvent(QKeyEvent *event)
break;
}
- if (!event->isAccepted())
+ if (!event->isAccepted()) {
+ event->accept();
QQuickTableView::keyPressEvent(event);
+ }
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index eee8316d05..cc229708f1 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -371,6 +371,17 @@ QSGRenderNode::RenderState::~RenderState()
}
/*!
+ \class QSGRenderNode::RenderState
+ \brief Provides information about the projection matrix and clipping.
+ \inmodule QtQuick
+
+ The render state contains information for the renderer when invoking
+ commands to the scenegraph backends.
+
+ \sa QSGRenderNode::render()
+ */
+
+/*!
\fn const QMatrix4x4 *QSGRenderNode::RenderState::projectionMatrix() const
\return pointer to the current projection matrix.
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 64f862f948..b0ab978908 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -9,7 +9,8 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcSgText, "qt.scenegraph.text")
-qint64 QSGDistanceFieldGlyphNode::m_totalAllocation = 0;
+// all SG glyph vertices and indices; only for qCDebug metrics
+static std::atomic<qint64> s_totalAllocation = 0;
QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGRenderContext *context)
: m_glyphNodeType(RootGlyphNode)
@@ -314,9 +315,9 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
}
}
- m_totalAllocation += vp.size() * sizeof(QSGGeometry::TexturedPoint2D) + ip.size() * sizeof(quint16);
+ s_totalAllocation += vp.size() * sizeof(QSGGeometry::TexturedPoint2D) + ip.size() * sizeof(quint16);
qCDebug(lcSgText) << "allocating for" << vp.size() << "vtx (reserved" << likelyGlyphCount * 4 << "):" << vp.size() * sizeof(QSGGeometry::TexturedPoint2D)
- << "bytes;" << ip.size() << "idx:" << ip.size() * sizeof(quint16) << "bytes; total bytes so far" << m_totalAllocation;
+ << "bytes;" << ip.size() << "idx:" << ip.size() * sizeof(quint16) << "bytes; total bytes so far" << s_totalAllocation;
g->allocate(vp.size(), ip.size());
memcpy(g->vertexDataAsTexturedPoint2D(), vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
memcpy(g->indexDataAsUShort(), ip.constData(), ip.size() * sizeof(quint16));
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index d92bf0a836..04b17f0ab7 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -84,8 +84,6 @@ private:
uint m_dirtyGeometry: 1;
uint m_dirtyMaterial: 1;
-
- static qint64 m_totalAllocation; // all SG glyph vertices and indices; only for qCDebug metrics
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 73d30ffb6b..bbd169708a 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -105,6 +105,9 @@ WM_Obscure = QEvent::User + 1,
// (updatePaintNode())
WM_RequestSync = QEvent::User + 2,
+// Passed from the RL to the RT when a window is exposed
+WM_Exposed = QEvent::User + 3,
+
// Passed by the RL to the RT to free up maybe release SG and GL contexts
// if no windows are rendering.
WM_TryRelease = QEvent::User + 4,
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index f1db5804e6..4c9facbb57 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -9,6 +9,9 @@
#include <private/qsgplaintexture_p.h>
#include <QtGui/private/qshaderdescription_p.h>
#include <QQmlFile>
+
+#include <QtGui/qquaternion.h>
+
#include <QFile>
#include <QFileSelector>
#include <QMutexLocker>
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 57cd51f171..25b7b620e0 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -1334,7 +1334,11 @@ QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
// There was no rendercontrol which means a custom render target
// should not be set either. Set our own, temporarily.
- window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(rt.data()));
+ auto renderTarget = QQuickRenderTarget::fromRhiRenderTarget(rt.data());
+ // Pass on the scale factor. No need to use effectiveDevicePixelRatio(),
+ // because there is no rendercontrol anyway.
+ renderTarget.setDevicePixelRatio(window->devicePixelRatio());
+ window->setRenderTarget(renderTarget);
QRhiCommandBuffer *cb = nullptr;
if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) {
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index d2a160c420..fc92673d8e 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -335,6 +335,17 @@ bool QSGRenderThread::event(QEvent *e)
return true; }
+
+ case WM_Exposed: {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_Exposed");
+
+ mutex.lock();
+ window = static_cast<WMWindowEvent *>(e)->window;
+ waitCondition.wakeOne();
+ mutex.unlock();
+
+ return true; }
+
case WM_RequestSync: {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestSync");
WMSyncEvent *se = static_cast<WMSyncEvent *>(e);
@@ -632,8 +643,14 @@ void QSGRenderThread::syncAndRender()
// An update request could still be delivered right before we get an
// unexpose. With Vulkan on Windows for example attempting to render
// leads to failures at this stage since the surface size is already 0.
- if (effectiveOutputSize.isEmpty())
+ if (effectiveOutputSize.isEmpty()) {
+ if (syncRequested) {
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
return;
+ }
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
@@ -1249,10 +1266,6 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
}
}
- // set this early as we'll be rendering shortly anyway and this avoids
- // specialcasing exposure in polishAndSync.
- w->thread->window = window;
-
#ifndef QT_NO_DEBUG
if (w->window->width() <= 0 || w->window->height() <= 0
|| (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
@@ -1271,6 +1284,10 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
if (!w->thread->isRunning()) {
qCDebug(QSG_LOG_RENDERLOOP, "- starting render thread");
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // specialcasing exposure in polishAndSync.
+ w->thread->window = window;
+
if (!w->thread->rhi) {
QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
if (!w->thread->offscreenSurface)
@@ -1295,6 +1312,12 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
} else {
qCDebug(QSG_LOG_RENDERLOOP, "- render thread already running");
+
+ // set w->thread->window here too, but using an event so it's thread-safe
+ w->thread->mutex.lock();
+ w->thread->postEvent(new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
}
polishAndSync(w, true);
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index 2cb8fe36ba..f56533e355 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -69,7 +69,7 @@ QT_BEGIN_NAMESPACE
\qml
Timer {
interval: 1000; repeat: true
- active: Application.state === Qt.Qt.ApplicationActive
+ active: Application.state === Qt.ApplicationActive
onTriggered: imageFetcher.fetchLatestImages()
}
\endqml
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 6fc50aaa5c..9bd2efb279 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -899,7 +899,9 @@ void QQuickDeliveryAgentPrivate::deliverKeyEvent(QKeyEvent *e)
e->isAutoRepeat(), e->count());
do {
- e->accept();
+ Q_ASSERT(e->type() != QEvent::ShortcutOverride || !e->isAccepted());
+ if (e->type() != QEvent::ShortcutOverride)
+ e->accept();
QCoreApplication::sendEvent(item, e);
} while (!e->isAccepted() && (item = item->parentItem()));
}
@@ -1055,6 +1057,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
if (subtreeHoverEnabled) {
hoveredLeafItemFound = false;
+ QQuickPointerHandlerPrivate::deviceDeliveryTargets(QPointingDevice::primaryPointingDevice()).clear();
deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
}
diff --git a/src/quick/util/qquickforeignutils.cpp b/src/quick/util/qquickforeignutils.cpp
index 34b3225abe..6ca5a42ade 100644
--- a/src/quick/util/qquickforeignutils.cpp
+++ b/src/quick/util/qquickforeignutils.cpp
@@ -19,32 +19,58 @@ QT_BEGIN_NAMESPACE
The following properties are available:
\list
- \li \l bool \c eventPoint.accepted: see also \l QEventPoint::accepted
- \li \l PointerDevice \c eventPoint.device: see also \l QEventPoint::device
- \li \l size \c eventPoint.ellipseDiameters: see also \l QEventPoint::ellipseDiameters
- \li \l point \c eventPoint.globalGrabPosition: see also \l QEventPoint::globalGrabPosition
- \li \l point \c eventPoint.globalLastPosition: see also \l QEventPoint::globalLastPosition
- \li \l point \c eventPoint.globalPosition: see also \l QEventPoint::globalPosition
- \li \l point \c eventPoint.globalPressPosition: see also \l QEventPoint::globalPressPosition
- \li \l bool \c eventPoint.grabPosition: see also \l QEventPoint::grabPosition
- \li \l int \c eventPoint.id: see also \l QEventPoint::id
- \li \l point \c eventPoint.lastPosition: see also \l QEventPoint::lastPosition
- \li \l ulong \c eventPoint.lastTimestamp: see also \l QEventPoint::lastTimestamp
- \li \l point \c eventPoint.position: see also \l QEventPoint::position
- \li \l point \c eventPoint.pressPosition: see also \l QEventPoint::pressPosition
- \li \l int \c eventPoint.pressTimestamp: see also \l QEventPoint::pressTimestamp
- \li \l real \c eventPoint.pressure: see also \l QEventPoint::pressure
- \li \l real \c eventPoint.rotation: see also \l QEventPoint::rotation
- \li \l point \c eventPoint.sceneGrabPosition: see also \l QEventPoint::sceneGrabPosition
- \li \l point \c eventPoint.sceneLastPosition: see also \l QEventPoint::sceneLastPosition
- \li \l point \c eventPoint.scenePosition: see also \l QEventPoint::scenePosition
- \li \l ulong \c eventPoint.pressTimestamp: see also \l QEventPoint::pressTimestamp
- \li \l point \c eventPoint.scenePressPosition: see also \l QEventPoint::scenePressPosition
- \li \l {QML Enumerations}{enumeration} \c eventPoint.state: see also \l QEventPoint::state
- \li \l real \c eventPoint.timeHeld: see also \l QEventPoint::timeHeld
- \li \l ulong \c eventPoint.timestamp: see also \l QEventPoint::timestamp
- \li \l pointingDeviceUniqueId \c eventPoint.uniqueId: see also \l QEventPoint::uniqueId
- \li \l vector2d \c eventPoint.velocity: see also \l QEventPoint::velocity
+ \li \l bool \c eventPoint.accepted \br
+ See also \l {QEventPoint::accepted}.
+ \li \l PointerDevice \c eventPoint.device \br
+ See also \l {QEventPoint::device}.
+ \li \l size \c eventPoint.ellipseDiameters \br
+ See also \l {QEventPoint::ellipseDiameters}.
+ \li \l point \c eventPoint.globalGrabPosition \br
+ See also \l {QEventPoint::globalGrabPosition}.
+ \li \l point \c eventPoint.globalLastPosition \br
+ See also \l {QEventPoint::globalLastPosition}.
+ \li \l point \c eventPoint.globalPosition \br
+ See also \l {QEventPoint::globalPosition}.
+ \li \l point \c eventPoint.globalPressPosition \br
+ See also \l {QEventPoint::globalPressPosition}.
+ \li \l bool \c eventPoint.grabPosition \br
+ See also \l {QEventPoint::grabPosition}.
+ \li \l int \c eventPoint.id \br
+ See also \l {QEventPoint::id}.
+ \li \l point \c eventPoint.lastPosition \br
+ See also \l {QEventPoint::lastPosition}.
+ \li \l ulong \c eventPoint.lastTimestamp \br
+ See also \l {QEventPoint::lastTimestamp}.
+ \li \l point \c eventPoint.position \br
+ See also \l {QEventPoint::position}.
+ \li \l point \c eventPoint.pressPosition \br
+ See also \l {QEventPoint::pressPosition}.
+ \li \l int \c eventPoint.pressTimestamp \br
+ See also \l {QEventPoint::pressTimestamp}.
+ \li \l real \c eventPoint.pressure \br
+ See also \l {QEventPoint::pressure}.
+ \li \l real \c eventPoint.rotation \br
+ See also \l {QEventPoint::rotation}.
+ \li \l point \c eventPoint.sceneGrabPosition \br
+ See also \l {QEventPoint::sceneGrabPosition}.
+ \li \l point \c eventPoint.sceneLastPosition \br
+ See also \l {QEventPoint::sceneLastPosition}.
+ \li \l point \c eventPoint.scenePosition \br
+ See also \l {QEventPoint::scenePosition}.
+ \li \l ulong \c eventPoint.pressTimestamp \br
+ See also \l {QEventPoint::pressTimestamp}.
+ \li \l point \c eventPoint.scenePressPosition \br
+ See also \l {QEventPoint::scenePressPosition}.
+ \li \l {QML Enumerations}{enumeration} \c eventPoint.state \br
+ See also \l {QEventPoint::state}.
+ \li \l real \c eventPoint.timeHeld \br
+ See also \l {QEventPoint::timeHeld}.
+ \li \l ulong \c eventPoint.timestamp \br
+ See also \l {QEventPoint::timestamp}.
+ \li \l pointingDeviceUniqueId \c eventPoint.uniqueId \br
+ See also \l {QEventPoint::uniqueId}.
+ \li \l vector2d \c eventPoint.velocity \br
+ See also \l {QEventPoint::velocity}.
\endlist
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 17529317d5..7562b74d46 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -1982,8 +1982,6 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
{
const QPointF &startPoint = path.currentPosition();
const QPointF &endPoint = positionForCurve(data, startPoint);
- if (startPoint == endPoint)
- return;
QQuickSvgParser::pathArc(path,
_radiusX,
_radiusY,
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 5f88805ef7..8cca5addbb 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -159,6 +159,10 @@ void QQuickSvgParser::pathArc(QPainterPath &path,
qreal y,
qreal curx, qreal cury)
{
+ // Check if the start point is equal to the end point.
+ if (QPointF(curx, cury) == QPointF(x, y))
+ return;
+
qreal sin_th, cos_th;
qreal a00, a01, a10, a11;
qreal x0, y0, x1, y1, xc, yc;
@@ -169,6 +173,14 @@ void QQuickSvgParser::pathArc(QPainterPath &path,
rx = qAbs(rx);
ry = qAbs(ry);
+ // Avoid nans and division by zero.
+ if (qFuzzyIsNull(rx) || qFuzzyIsNull(ry)) {
+ // https://www.w3.org/TR/SVG/paths.html#ArcOutOfRangeParameters says:
+ // "If either rx or ry is 0, then this arc is treated as a straight line
+ // segment (a "lineto") joining the endpoints."
+ path.lineTo(x, y);
+ return;
+ }
sin_th = qSin(qDegreesToRadians(x_axis_rotation));
cos_th = qCos(qDegreesToRadians(x_axis_rotation));
diff --git a/src/quick/util/qquicktransition.cpp b/src/quick/util/qquicktransition.cpp
index 09d92ddb36..3c5e847702 100644
--- a/src/quick/util/qquicktransition.cpp
+++ b/src/quick/util/qquicktransition.cpp
@@ -107,14 +107,20 @@ protected:
static qsizetype animation_count(QQmlListProperty<QQuickAbstractAnimation> *list);
static QQuickAbstractAnimation* animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos);
static void clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list);
- QList<QQuickAbstractAnimation *> animations;
+ static void removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list);
+ static void replace_animation(
+ QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos,
+ QQuickAbstractAnimation *a);
+
+ QList<QPointer<QQuickAbstractAnimation>> animations;
};
void QQuickTransitionPrivate::append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a)
{
QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
q->d_func()->animations.append(a);
- a->setDisableUserControl();
+ if (a)
+ a->setDisableUserControl();
}
qsizetype QQuickTransitionPrivate::animation_count(QQmlListProperty<QQuickAbstractAnimation> *list)
@@ -132,10 +138,25 @@ QQuickAbstractAnimation* QQuickTransitionPrivate::animation_at(QQmlListProperty<
void QQuickTransitionPrivate::clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list)
{
QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
- while (q->d_func()->animations.size()) {
- QQuickAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
- q->d_func()->animations.removeAll(firstAnim);
- }
+ q->d_func()->animations.clear();
+}
+
+void QQuickTransitionPrivate::removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list)
+{
+ QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
+ q->d_func()->animations.removeLast();
+}
+
+void QQuickTransitionPrivate::replace_animation(
+ QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos, QQuickAbstractAnimation *a)
+{
+ QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
+ QQuickTransitionPrivate *d = q->d_func();
+ if (d->animations.length() <= pos)
+ d->animations.resize(pos + 1, nullptr);
+ d->animations[pos] = a;
+ if (a)
+ a->setDisableUserControl();
}
void QQuickTransitionInstance::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
@@ -240,15 +261,19 @@ QQuickTransitionInstance *QQuickTransition::prepare(QQuickStateOperation::Action
int start = d->reversed ? d->animations.size() - 1 : 0;
int end = d->reversed ? -1 : d->animations.size();
- QAbstractAnimationJob *anim = nullptr;
- for (int i = start; i != end;) {
- anim = d->animations.at(i)->transition(actions, after, direction, defaultTarget);
- if (anim) {
- if (d->animations.at(i)->threadingModel() == QQuickAbstractAnimation::RenderThread)
- anim = new QQuickAnimatorProxyJob(anim, d->animations.at(i));
- d->reversed ? group->prependAnimation(anim) : group->appendAnimation(anim);
- }
- d->reversed ? --i : ++i;
+ for (int i = start; i != end; d->reversed ? --i : ++i) {
+ QQuickAbstractAnimation *anim = d->animations.at(i);
+ if (!anim)
+ continue;
+
+ QAbstractAnimationJob *job = anim->transition(actions, after, direction, defaultTarget);
+ if (!job)
+ continue;
+
+ if (anim->threadingModel() == QQuickAbstractAnimation::RenderThread)
+ job = new QQuickAnimatorProxyJob(job, anim);
+
+ d->reversed ? group->prependAnimation(job) : group->appendAnimation(job);
}
group->setDirection(d->reversed ? QAbstractAnimationJob::Backward : QAbstractAnimationJob::Forward);
@@ -434,10 +459,14 @@ bool QQuickTransition::running() const
QQmlListProperty<QQuickAbstractAnimation> QQuickTransition::animations()
{
Q_D(QQuickTransition);
- return QQmlListProperty<QQuickAbstractAnimation>(this, &d->animations, QQuickTransitionPrivate::append_animation,
- QQuickTransitionPrivate::animation_count,
- QQuickTransitionPrivate::animation_at,
- QQuickTransitionPrivate::clear_animations);
+ return QQmlListProperty<QQuickAbstractAnimation>(
+ this, &d->animations,
+ QQuickTransitionPrivate::append_animation,
+ QQuickTransitionPrivate::animation_count,
+ QQuickTransitionPrivate::animation_at,
+ QQuickTransitionPrivate::clear_animations,
+ QQuickTransitionPrivate::replace_animation,
+ QQuickTransitionPrivate::removeLast_animation);
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
index 3268b5b11a..871acdf40d 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
@@ -208,6 +208,8 @@
\image qtquickcontrols-universal-foreground.png
\endtable
+ \endstyleproperty
+
\styleproperty {Universal.theme} {enumeration} {universal-theme-attached-prop}
\target universal-theme-attached-prop
This attached property holds whether the theme is light or dark. The property
diff --git a/src/quickcontrols/macos/qtquickcontrols2macosstyleplugin.cpp b/src/quickcontrols/macos/qtquickcontrols2macosstyleplugin.cpp
index ec67ffa4dc..6b1c12fdab 100644
--- a/src/quickcontrols/macos/qtquickcontrols2macosstyleplugin.cpp
+++ b/src/quickcontrols/macos/qtquickcontrols2macosstyleplugin.cpp
@@ -4,6 +4,7 @@
#include <QtQml/qqml.h>
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
#include <QtQuickControls2/qquickstyle.h>
+#include <private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
@@ -34,8 +35,10 @@ QString QtQuickControls2MacOSStylePlugin::name() const
return QStringLiteral("macOS");
}
-void QtQuickControls2MacOSStylePlugin::initializeTheme(QQuickTheme */*theme*/)
+void QtQuickControls2MacOSStylePlugin::initializeTheme(QQuickTheme *theme)
{
+ Q_ASSERT(theme);
+ theme->setUsePlatformPalette(true);
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/qt_attribution.json b/src/quickcontrols/material/qt_attribution.json
index 9d53b8b014..38f7bac6e7 100644
--- a/src/quickcontrols/material/qt_attribution.json
+++ b/src/quickcontrols/material/qt_attribution.json
@@ -6,6 +6,7 @@
"Files": "impl/ElevationEffect.qml",
"Description": "Shadow values for the elevation effect.",
"Homepage": "https://angularjs.org/",
+ "Comment": "no relevant PURL or CPE set, because the dependency is a single file",
"License": "MIT License",
"LicenseId": "MIT",
"LicenseFile": "LICENSE_ANGULARJS.txt",
diff --git a/src/quickcontrols/qquickattachedpropertypropagator.cpp b/src/quickcontrols/qquickattachedpropertypropagator.cpp
index 892d347809..57ea1fc782 100644
--- a/src/quickcontrols/qquickattachedpropertypropagator.cpp
+++ b/src/quickcontrols/qquickattachedpropertypropagator.cpp
@@ -98,7 +98,7 @@ static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *o
*/
auto popupItem = qobject_cast<QQuickPopupItem *>(objectWeAreAttachedTo);
if (popupItem) {
- qCDebug(lcAttached).noquote() << "- attachee belongs to popup item" << popupItem << "- checking if it has an attached object";
+ qCDebug(lcAttached).noquote() << "- attachee is a popup item" << popupItem << "- checking if it has an attached object";
auto popupItemPrivate = QQuickPopupItemPrivate::get(popupItem);
QQuickAttachedPropertyPropagator *popupAttached = attachedObject(ourAttachedType, popupItemPrivate->popup);
if (popupAttached) {
@@ -108,7 +108,7 @@ static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *o
qCDebug(lcAttached).noquote() << "- popup item does not have attached object";
}
} else {
- qCDebug(lcAttached).noquote() << "- attachee does not belong to a popup";
+ qCDebug(lcAttached).noquote() << "- attachee is not a popup item";
}
QQuickItem *item = qobject_cast<QQuickItem *>(objectWeAreAttachedTo);
diff --git a/src/quickcontrolsimpl/qquickiconlabel.cpp b/src/quickcontrolsimpl/qquickiconlabel.cpp
index 7d1561795b..612d98aa26 100644
--- a/src/quickcontrolsimpl/qquickiconlabel.cpp
+++ b/src/quickcontrolsimpl/qquickiconlabel.cpp
@@ -600,6 +600,21 @@ void QQuickIconLabel::componentComplete()
completeComponent(d->label);
QQuickItem::componentComplete();
d->layout();
+
+ // Any items declared as children of us will be parented before the label and icon image,
+ // so they will be drawn below us. This is not intuitive, so move us to the bottom of the pile.
+ const auto paintOrderChildItems = QQuickItemPrivate::get(this)->paintOrderChildItems();
+ const auto bottomMostFosterChildIt = std::find_if(paintOrderChildItems.constBegin(),
+ paintOrderChildItems.constEnd(), [d](QQuickItem *item) {
+ return item != d->label && item != d->image;
+ });
+ if (bottomMostFosterChildIt != paintOrderChildItems.constEnd()) {
+ const QQuickItem *bottomMostFosterChild = *bottomMostFosterChildIt;
+ if (d->label)
+ d->label->stackBefore(bottomMostFosterChild);
+ if (d->image)
+ d->image->stackBefore(bottomMostFosterChild);
+ }
}
void QQuickIconLabel::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
diff --git a/src/quickcontrolstestutils/dialogstestutils.cpp b/src/quickcontrolstestutils/dialogstestutils.cpp
index 4a795e42da..2732e6ac96 100644
--- a/src/quickcontrolstestutils/dialogstestutils.cpp
+++ b/src/quickcontrolstestutils/dialogstestutils.cpp
@@ -41,8 +41,8 @@ bool QQuickDialogTestUtils::verifyFileDialogDelegates(QQuickListView *fileDialog
return false;
}
- // Need to call absoluteFilePath on Windows; see comment in dialogtestutil.h.
- actualFiles.append(QFileInfo(delegate->file().toLocalFile()).absoluteFilePath());
+ // Need to call canonicalFilePath on Windows; see comment in dialogstestutils_p.h.
+ actualFiles.append(QFileInfo(delegate->file().toLocalFile()).canonicalFilePath());
}
if (actualFiles != expectedFiles) {
diff --git a/src/quickcontrolstestutils/dialogstestutils_p.h b/src/quickcontrolstestutils/dialogstestutils_p.h
index 963eb4d764..53665327e4 100644
--- a/src/quickcontrolstestutils/dialogstestutils_p.h
+++ b/src/quickcontrolstestutils/dialogstestutils_p.h
@@ -23,7 +23,7 @@
// We need these for Windows, because FolderListModel returns a lowercase drive letter; e.g.:
// "file:///c:/blah.txt", whereas other API returns "file:///C:/blah.txt".
#define COMPARE_URL(url1, url2) \
- QCOMPARE(QFileInfo(url1.toLocalFile()).absoluteFilePath(), QFileInfo(url2.toLocalFile()).absoluteFilePath());
+ QCOMPARE(QFileInfo(url1.toLocalFile()).canonicalFilePath(), QFileInfo(url2.toLocalFile()).canonicalFilePath());
// Store a copy of the arguments in case { ... } list initializer syntax is used as an argument,
// which could result in two different lists being created and passed to std::transform()
@@ -33,11 +33,11 @@
const QList<QUrl> actualUrlsCopy = actualUrls; \
QList<QString> actualPaths; \
std::transform(actualUrlsCopy.begin(), actualUrlsCopy.end(), std::back_insert_iterator(actualPaths), \
- [](const QUrl &url) { return QFileInfo(url.toLocalFile()).absoluteFilePath(); }); \
+ [](const QUrl &url) { return QFileInfo(url.toLocalFile()).canonicalFilePath(); }); \
const QList<QUrl> expectedUrlsCopy = expectedUrls; \
QList<QString> expectedPaths; \
std::transform(expectedUrlsCopy.begin(), expectedUrlsCopy.end(), std::back_insert_iterator(expectedPaths), \
- [](const QUrl &url) { return QFileInfo(url.toLocalFile()).absoluteFilePath(); }); \
+ [](const QUrl &url) { return QFileInfo(url.toLocalFile()).canonicalFilePath(); }); \
QCOMPARE(actualPaths, expectedPaths); \
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
index 5b5b2abdae..1ec04be2f7 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
@@ -94,6 +94,13 @@ QQuickAbstractColorPicker::QQuickAbstractColorPicker(QQuickAbstractColorPickerPr
setAcceptedMouseButtons(Qt::LeftButton);
}
+QQuickAbstractColorPicker::~QQuickAbstractColorPicker()
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (d->m_handle)
+ d->removeImplicitSizeListener(d->m_handle);
+}
+
QColor QQuickAbstractColorPicker::color() const
{
Q_D(const QQuickAbstractColorPicker);
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
index b29e488b3a..b2117dac15 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
@@ -43,6 +43,8 @@ class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickAbstractColorPicker : public
QML_UNCREATABLE("AbstractColorPicker is abstract.")
public:
+ ~QQuickAbstractColorPicker() override;
+
QColor color() const;
void setColor(const QColor &c);
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
index 594b148528..4cd0fa73af 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
@@ -29,25 +29,35 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
std::unique_ptr<QPlatformDialogHelper> dialogHelper;
switch (type) {
case QQuickDialogType::ColorDialog: {
- dialogHelper.reset(new QQuickPlatformColorDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformColorDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
#if QT_CONFIG(quick_listview)
case QQuickDialogType::FileDialog: {
- dialogHelper.reset(new QQuickPlatformFileDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFileDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
case QQuickDialogType::FolderDialog: {
- dialogHelper.reset(new QQuickPlatformFolderDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFolderDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
case QQuickDialogType::FontDialog: {
- dialogHelper.reset(new QQuickPlatformFontDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFontDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
#endif
case QQuickDialogType::MessageDialog: {
- dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformMessageDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
default:
diff --git a/src/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp
index 0aeda72143..a530917557 100644
--- a/src/quicklayouts/qquicklinearlayout.cpp
+++ b/src/quicklayouts/qquicklinearlayout.cpp
@@ -451,7 +451,8 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
if (d->m_recurRearrangeCounter++ == 2) {
// allow a recursive depth of two in order to respond to height-for-width
// (e.g QQuickText changes implicitHeight when its width gets changed)
- qWarning() << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations.";
+ qmlWarning(this)
+ << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations.";
return;
}
diff --git a/src/quicktemplates/qquickabstractbutton.cpp b/src/quicktemplates/qquickabstractbutton.cpp
index 61c13d749e..733fbf9bb2 100644
--- a/src/quicktemplates/qquickabstractbutton.cpp
+++ b/src/quicktemplates/qquickabstractbutton.cpp
@@ -15,6 +15,9 @@
#if QT_CONFIG(shortcut)
# include <QtGui/private/qshortcutmap_p.h>
#endif
+#if QT_CONFIG(accessibility)
+#include <QtGui/private/qaccessiblehelper_p.h>
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQuick/private/qquickevents_p_p.h>
@@ -1193,7 +1196,9 @@ void QQuickAbstractButton::buttonChange(ButtonChange change)
break;
case ButtonTextChange: {
const QString txt = text();
- maybeSetAccessibleName(txt);
+#if QT_CONFIG(accessibility)
+ maybeSetAccessibleName(qt_accStripAmp(txt));
+#endif
#if QT_CONFIG(shortcut)
setShortcut(QKeySequence::mnemonic(txt));
#endif
@@ -1232,7 +1237,7 @@ void QQuickAbstractButton::accessibilityActiveChanged(bool active)
Q_D(QQuickAbstractButton);
if (active) {
- maybeSetAccessibleName(text());
+ maybeSetAccessibleName(qt_accStripAmp(text()));
setAccessibleProperty("pressed", d->pressed);
setAccessibleProperty("checked", d->checked);
setAccessibleProperty("checkable", d->checkable);
diff --git a/src/quicktemplates/qquickcontainer.cpp b/src/quicktemplates/qquickcontainer.cpp
index 9d48f3b3a1..499fd621d6 100644
--- a/src/quicktemplates/qquickcontainer.cpp
+++ b/src/quicktemplates/qquickcontainer.cpp
@@ -299,15 +299,37 @@ void QQuickContainerPrivate::reorderItems()
if (!contentItem)
return;
- QList<QQuickItem *> siblings = effectiveContentItem(contentItem)->childItems();
-
- int to = 0;
- for (int i = 0; i < siblings.size(); ++i) {
- QQuickItem* sibling = siblings.at(i);
- if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
- continue;
- int index = contentModel->indexOf(sibling, nullptr);
- q->moveItem(index, to++);
+ // The item view eventually reparents all the items of the content model
+ // from the container. At this stage (during component complete), however, due
+ // to optimisation strategies in the item views, this doesn't happen when the
+ // visible area of the item view is less than the total content width of the items
+ // within the content model. This can cause issues while reordering. Thus, it's
+ // better to skip reordering the item within the content model once it's known
+ // that it will be reparented to the item views.
+ bool allowReorder = true;
+ if (!qobject_cast<QQuickFlickable *>(contentItem)) {
+ for (int index = 0; index < contentModel->count(); index++) {
+ if (const auto *item = qobject_cast<QQuickItem *>(contentModel->get(index))) {
+ const auto *parentItem = item->parentItem();
+ if (parentItem && !qobject_cast<QQuickItemView *>(parentItem->parentItem())) {
+ allowReorder = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if (allowReorder) {
+ QList<QQuickItem *> siblings = effectiveContentItem(contentItem)->childItems();
+ int to = 0;
+ for (int i = 0; i < siblings.size(); ++i) {
+ QQuickItem* sibling = siblings.at(i);
+ if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
+ continue;
+ const int index = contentModel->indexOf(sibling, nullptr);
+ if (index >= 0)
+ q->moveItem(index, to++);
+ }
}
}
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
index f53e9a9091..44a9a67cd3 100644
--- a/src/quicktemplates/qquickmenu.cpp
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -26,6 +26,8 @@
#include <private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickwindow_p.h>
@@ -437,6 +439,22 @@ bool QQuickMenuPrivate::prepareEnterTransition()
// the right, it flips on the other side of the parent menu.
allowHorizontalFlip = cascade && parentMenu;
+ // Enter transitions may want to animate the Menu's height based on its implicitHeight.
+ // The Menu's implicitHeight is typically based on the ListView's contentHeight,
+ // among other things. The docs for ListView's forceLayout function say:
+ // "Responding to changes in the model is usually batched to happen only once per frame."
+ // As e.g. NumberAnimation's from and to values are set before any polishes happen,
+ // any re-evaluation of their bindings happen too late, and the starting height can be
+ // out-dated when menu items are added after component completion
+ // (QQuickItemView::componentComplete does a layout, so items declared as children aren't
+ // affected by this). To account for this, we force a layout before the transition starts.
+ // We try to avoid unnecessary re-layouting if we can avoid it.
+ auto *contentItemAsListView = qobject_cast<QQuickListView *>(contentItem);
+ if (contentItemAsListView) {
+ if (QQuickItemViewPrivate::get(contentItemAsListView)->currentChanges.hasPendingChanges())
+ contentItemAsListView->forceLayout();
+ }
+
if (!QQuickPopupPrivate::prepareEnterTransition())
return false;
diff --git a/src/quicktemplates/qquickmenubar.cpp b/src/quicktemplates/qquickmenubar.cpp
index d8ecbb6626..5aa68556c2 100644
--- a/src/quicktemplates/qquickmenubar.cpp
+++ b/src/quicktemplates/qquickmenubar.cpp
@@ -247,7 +247,8 @@ QQuickMenu *QQuickMenuBarPrivate::menus_at(QQmlListProperty<QQuickMenu> *prop, q
void QQuickMenuBarPrivate::menus_clear(QQmlListProperty<QQuickMenu> *prop)
{
QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
- QQuickMenuBarPrivate::get(menuBar)->contentModel->clear();
+ for (int count = menuBar->count(); count > 0; count = menuBar->count())
+ menuBar->takeMenu(count - 1);
}
QPalette QQuickMenuBarPrivate::defaultPalette() const
diff --git a/src/quicktemplates/qquickmonthgrid.cpp b/src/quicktemplates/qquickmonthgrid.cpp
index 957c9b8f93..037f8cfaf7 100644
--- a/src/quicktemplates/qquickmonthgrid.cpp
+++ b/src/quicktemplates/qquickmonthgrid.cpp
@@ -45,7 +45,8 @@ QT_BEGIN_NAMESPACE
\snippet qtquickcontrols-monthgrid-localization.qml 1
- \sa DayOfWeekRow, WeekNumberColumn, CalendarModel
+ \sa DayOfWeekRow, WeekNumberColumn, CalendarModel,
+ {Qt Quick Controls - Event Calendar}
*/
/*!
diff --git a/src/quicktemplates/qquickoverlay.cpp b/src/quicktemplates/qquickoverlay.cpp
index 091b2f8fb3..c9d3cf9892 100644
--- a/src/quicktemplates/qquickoverlay.cpp
+++ b/src/quicktemplates/qquickoverlay.cpp
@@ -97,32 +97,9 @@ bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
return false;
}
-static QQuickItem *findRootOfOverlaySubtree(QQuickItem *source, const QQuickOverlay *overlay)
-{
- QQuickItem *sourceAncestor = source;
- while (sourceAncestor) {
- QQuickItem *parentItem = sourceAncestor->parentItem();
- if (parentItem == overlay)
- return sourceAncestor;
- sourceAncestor = parentItem;
- }
- // Not an ancestor of the overlay.
- return nullptr;
-}
-
bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target)
{
- Q_Q(const QQuickOverlay);
if (target) {
- // childMouseEventFilter will cause this function to get called for each active popup.
- // If any of those active popups block inputs, the delivery agent won't send the press event to source.
- // A popup will block input, if it's modal, and the item isn't an ancestor of the popup's popup item.
- // If source doesn't belong to a popup, but exists in an overlay subtree, it makes sense to not filter the event.
- const QList<QQuickItem *> childItems = paintOrderChildItems();
- if (childItems.indexOf(findRootOfOverlaySubtree(source, q))
- > childItems.indexOf(QQuickPopupPrivate::get(target)->popupItem))
- return false;
-
if (target->overlayEvent(source, event)) {
setMouseGrabberPopup(target);
return true;
@@ -166,17 +143,7 @@ bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickP
bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target)
{
- Q_Q(const QQuickOverlay);
if (target) {
- // childMouseEventFilter will cause this function to get called for each active popup.
- // If any of those active popups block inputs, the delivery agent won't send the press event to source.
- // A popup will block input, if it's modal, and the item isn't an ancestor of the popup's popup item.
- // If source doesn't belong to a popup, but exists in an overlay subtree, it makes sense to not filter the event.
- const QList<QQuickItem *> childItems = paintOrderChildItems();
- if (childItems.indexOf(findRootOfOverlaySubtree(source, q))
- > childItems.indexOf(QQuickPopupPrivate::get(target)->popupItem))
- return false;
-
setMouseGrabberPopup(nullptr);
if (target->overlayEvent(source, event)) {
setMouseGrabberPopup(nullptr);
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index deb5651391..207b0a365a 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -2803,6 +2803,20 @@ void QQuickPopup::mouseUngrabEvent()
d->handleUngrab();
}
+
+static QQuickItem *findRootOfOverlaySubtree(QQuickItem *source, const QQuickOverlay *overlay)
+{
+ QQuickItem *sourceAncestor = source;
+ while (sourceAncestor) {
+ QQuickItem *parentItem = sourceAncestor->parentItem();
+ if (parentItem == overlay)
+ return sourceAncestor;
+ sourceAncestor = parentItem;
+ }
+ // Not an ancestor of the overlay.
+ return nullptr;
+}
+
/*!
\internal
@@ -2818,6 +2832,17 @@ void QQuickPopup::mouseUngrabEvent()
bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event)
{
Q_D(QQuickPopup);
+
+ // The overlay will normally call this function for each active popup, assuming there is no active mouse grabber.
+ // If \a item doesn't belong to any of these popups, but exists in an overlay subtree, we shouldn't filter the event,
+ // since the item is supposed to be independent of any active popups.
+ auto *overlay = QQuickOverlay::overlay(d->window);
+ const QList<QQuickItem *> paintOrderChildItems = QQuickOverlayPrivate::get(overlay)->paintOrderChildItems();
+ const qsizetype targetItemPaintOrderIndex = paintOrderChildItems.indexOf(findRootOfOverlaySubtree(item, overlay));
+ const qsizetype popupItemPaintOrderIndex = paintOrderChildItems.indexOf(d->popupItem);
+ if (targetItemPaintOrderIndex > popupItemPaintOrderIndex)
+ return false;
+
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease:
@@ -2851,7 +2876,7 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event)
void QQuickPopup::touchEvent(QTouchEvent *event)
{
Q_D(QQuickPopup);
- d->handleTouchEvent(d->popupItem, event);
+ event->setAccepted(d->handleTouchEvent(d->popupItem, event));
}
void QQuickPopup::touchUngrabEvent()
diff --git a/src/quicktemplates/qquickshortcutcontext.cpp b/src/quicktemplates/qquickshortcutcontext.cpp
index 2d07eb44f9..7f04583fe4 100644
--- a/src/quicktemplates/qquickshortcutcontext.cpp
+++ b/src/quicktemplates/qquickshortcutcontext.cpp
@@ -57,8 +57,12 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context)
// so that actions within it can grab shortcuts.
if (auto *menu = qobject_cast<QQuickMenu *>(popup)) {
auto parentMenu = QQuickMenuPrivate::get(menu)->parentMenu;
- while (!obj && parentMenu)
+ while (parentMenu) {
obj = parentMenu->window();
+ if (obj)
+ break;
+ parentMenu = QQuickMenuPrivate::get(parentMenu)->parentMenu;
+ }
}
}
break;
diff --git a/src/quicktestutils/quick/visualtestutils_p.h b/src/quicktestutils/quick/visualtestutils_p.h
index c57868ee68..22e909471e 100644
--- a/src/quicktestutils/quick/visualtestutils_p.h
+++ b/src/quicktestutils/quick/visualtestutils_p.h
@@ -39,8 +39,16 @@ namespace QQuickVisualTestUtils
template<typename F>
void forEachStep(int steps, F &&func)
{
+ if (steps == 1) {
+ // that's odd usage, but cut to the chase then
+ func(qreal(1));
+ return;
+ }
+
for (int i = 0; i < steps; ++i) {
- const qreal progress = qreal(i) / steps;
+ // - 1 because that gives us {0, 0.5, 1} for progress (if steps == 3),
+ // rather than {0, 0.33, 0.66}.
+ const qreal progress = qreal(i) / (steps - 1);
func(progress);
}
}
diff --git a/tests/auto/core/qqmlsettings/CMakeLists.txt b/tests/auto/core/qqmlsettings/CMakeLists.txt
index 0d596a31dc..07606afc24 100644
--- a/tests/auto/core/qqmlsettings/CMakeLists.txt
+++ b/tests/auto/core/qqmlsettings/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qqmlsettings LANGUAGES C CXX ASM)
+ project(tst_qqmlsettings LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/core/qqmlstandardpaths/CMakeLists.txt b/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
index 6078c10755..de30007643 100644
--- a/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
+++ b/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qqmlstandardpaths LANGUAGES C CXX ASM)
+ project(tst_qqmlstandardpaths LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/core/qqmlsysteminformation/CMakeLists.txt b/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
index c69564634e..45c21fecd2 100644
--- a/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
+++ b/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qqmlsysteminformation LANGUAGES C CXX ASM)
+ project(tst_qqmlsysteminformation LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index a3dc327cc2..aec22d32a5 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -282,6 +282,7 @@ private slots:
void jsonStringifyHugeArray();
void tostringRecursionCheck();
+ void arrayJoinRecursionCheck();
void arrayIncludesWithLargeArray();
void printCircularArray();
void typedArraySet();
@@ -321,6 +322,8 @@ private slots:
void multiMatchingRegularExpression();
+ void evalInGlobalContext();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -5133,11 +5136,11 @@ void tst_QJSEngine::mathMinMax()
QJSValue result = engine.evaluate("var a = .5; Math.min(1, 2, 3.5 + a, '5')");
QCOMPARE(result.toNumber(), 1.0);
- QVERIFY(QV4::Value(QJSValuePrivate::asReturnedValue(&result)).isInteger());
+ QVERIFY(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&result)).isInteger());
result = engine.evaluate("var a = .5; Math.max('0', 1, 2, 3.5 + a)");
QCOMPARE(result.toNumber(), 4.0);
- QVERIFY(QV4::Value(QJSValuePrivate::asReturnedValue(&result)).isInteger());
+ QVERIFY(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&result)).isInteger());
}
void tst_QJSEngine::mathNegativeZero()
@@ -5523,6 +5526,20 @@ void tst_QJSEngine::tostringRecursionCheck()
}
main();
)js");
+
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
+}
+
+void tst_QJSEngine::arrayJoinRecursionCheck()
+{
+ QJSEngine engine;
+ auto value = engine.evaluate(R"js(
+ a=[0,1];
+ a[0]=a;
+ a+0
+ )js");
+
QVERIFY(value.isError());
QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
}
@@ -6435,6 +6452,14 @@ void tst_QJSEngine::multiMatchingRegularExpression()
QCOMPARE(result2.toString(), "4F15 9D7A D402 55D9 4A5B 7EB9 AAAC D740 8C79 245D "_L1);
}
+void tst_QJSEngine::evalInGlobalContext()
+{
+ QJSEngine myEngine;
+ const QJSValue fun = myEngine.globalObject().property(QLatin1String("eval"));
+ const QJSValue ret = fun.call({ QLatin1String("99") });
+ QCOMPARE(ret.toString(), QLatin1String("99"));
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index e65c1a3259..0511dcbae8 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -2820,7 +2820,7 @@ void tst_QJSValue::deleteFromDifferentThread()
std::unique_ptr<QThread> thread(QThread::create([&]() {
QMutexLocker locker(&mutex);
QJSValuePrivate::free(&jsval);
- QJSValuePrivate::setValue(&jsval, QV4::Encode::undefined());
+ QJSValuePrivate::setValue(&jsval, QV4::Value::fromReturnedValue(QV4::Encode::undefined()));
QVERIFY(storage.firstPage != nullptr);
condition.wakeOne();
}));
diff --git a/tests/auto/qml/qml/tst_qml.cpp b/tests/auto/qml/qml/tst_qml.cpp
index de884d8257..f80af89044 100644
--- a/tests/auto/qml/qml/tst_qml.cpp
+++ b/tests/auto/qml/qml/tst_qml.cpp
@@ -6,6 +6,8 @@
#include <QtCore/qprocess.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QTemporaryFile>
+
class tst_qml : public QQmlDataTest
{
Q_OBJECT
@@ -15,6 +17,7 @@ public:
private slots:
void initTestCase() override;
void nonWindow();
+ void extraPositionalArguments();
private:
QString qmlPath;
@@ -42,6 +45,38 @@ void tst_qml::nonWindow()
QCOMPARE(qml.exitCode(), 0); // Should not exit with code 2
}
+void tst_qml::extraPositionalArguments()
+{
+ QProcess qml;
+ QStringList args;
+
+ QTemporaryFile f;
+ QVERIFY(f.open());
+ QVERIFY(f.write(R"(import QtQml
+
+QtObject {
+ Component.onCompleted: {
+ let s = ""
+ for (let i = 2; i < Qt.application.arguments.length; ++i)
+ s += Qt.application.arguments[i].substring(1)
+ console.log(s)
+ Qt.quit()
+ }
+}
+)"));
+ f.flush();
+
+ args << f.fileName();
+ args << "--";
+ for (char c = 'a'; c <= 'z'; ++c)
+ args << u'-' + QString(c);
+
+ qml.start(qmlPath, args);
+ QVERIFY(qml.waitForFinished());
+ QVERIFY(qml.exitStatus() == QProcess::NormalExit && qml.exitCode() == 0);
+ QVERIFY(qml.readAllStandardError().contains("abcdefghijklmnopqrstuvwxyz"));
+}
+
QTEST_MAIN(tst_qml)
#include <tst_qml.moc>
diff --git a/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml b/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml
new file mode 100644
index 0000000000..f435d2ec4a
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ id: root
+
+ Item {
+ id: inner
+
+ Tumbler {
+ id: year
+
+ delegate: Rectangle {
+ required property var modelData
+ }
+ }
+
+ Tumbler {
+ id: month
+
+ delegate: Rectangle {
+ required property var modelData
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index be219273a8..bd95946036 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -67,6 +67,9 @@ private slots:
void scriptStringCachegenInteraction();
void saveableUnitPointer();
+
+ void crash_data();
+ void crash();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -115,6 +118,36 @@ static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr
return proc.exitCode() == 0;
}
+static bool generateCpp(const QString &qmlFileName, QByteArray *capturedStderr = nullptr)
+{
+#if defined(QTEST_CROSS_COMPILED)
+ QTest::qFail("You cannot call qmlcachegen on the target.", __FILE__, __LINE__);
+ return false;
+#endif
+ QProcess proc;
+ if (capturedStderr == nullptr)
+ proc.setProcessChannelMode(QProcess::ForwardedChannels);
+ proc.setProgram(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath)
+ + QLatin1String("/qmlcachegen"));
+ QTemporaryDir outputDir;
+ const QString outputFile = outputDir.filePath("output.cpp"_L1);
+ proc.setArguments(QStringList{ "--resource-path"_L1, "qrc:/qt/qml/Crashes/testFile.qml"_L1,
+ "-o"_L1, outputFile, qmlFileName });
+ proc.start();
+ if (!proc.waitForFinished())
+ return false;
+
+ if (capturedStderr)
+ *capturedStderr = proc.readAllStandardError();
+
+ if (!QFile::exists(outputFile))
+ return false;
+
+ if (proc.exitStatus() != QProcess::NormalExit)
+ return false;
+ return proc.exitCode() == 0;
+}
+
tst_qmlcachegen::tst_qmlcachegen()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
@@ -235,7 +268,8 @@ void tst_qmlcachegen::translationExpressionSupport()
"}");
- QVERIFY(generateCache(testFilePath));
+ QByteArray errors;
+ QVERIFY2(generateCache(testFilePath, &errors), errors.constData());
const QString cacheFilePath = testFilePath + QLatin1Char('c');
QVERIFY(QFile::exists(cacheFilePath));
@@ -837,6 +871,27 @@ void tst_qmlcachegen::saveableUnitPointer()
QCOMPARE(unit.flags, flags);
}
+void tst_qmlcachegen::crash_data()
+{
+ QTest::addColumn<QString>("fileName");
+
+ QTest::addRow("buggyFixSuggestion") << u"buggyFixSuggestion.qml"_s;
+}
+
+void tst_qmlcachegen::crash()
+{
+#if defined(QTEST_CROSS_COMPILED)
+ QSKIP("Cannot call qmlcachegen on cross-compiled target.");
+#endif
+
+ QFETCH(QString, fileName);
+ const QString filePath = testFile("crashes/" + fileName);
+
+ QFile file(filePath);
+ QVERIFY(file.exists());
+ QVERIFY(generateCpp(filePath));
+}
+
const QQmlScriptString &ScriptStringProps::undef() const
{
return m_undef;
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index d80783078f..c78acadd58 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -37,6 +37,7 @@ set(qml_files
BadType.qml
BaseMember.qml
BindingExpression.qml
+ Categorizer.qml
Cycle1.qml
Cycle2.qml
Cycle3.qml
@@ -161,6 +162,7 @@ set(qml_files
listlength.qml
math.qml
mathOperations.qml
+ mergeSideEffects.qml
methods.qml
modulePrefix.qml
moveRegVoid.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml b/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml
new file mode 100644
index 0000000000..83117f5d01
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml
@@ -0,0 +1,60 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: root
+
+ enum Parameters {
+ Length = 32,
+ Iterations = 2,
+
+ Category0 = 0xf0f,
+ Category1 = 0xf0f0,
+ Category2 = 0xf0f0f,
+ Maximum = 0xf0f0f0,
+ Mask = 0xabcdef
+ }
+
+ function randomNumber() : int {
+ return (Math.random() * Categorizer.Maximum);
+ }
+
+ property list<double> numbers: {
+ // Can't StoreElement on non-list-properties in 6.5.
+ // To keep the pragma Strict, slightly adjust the original code here.
+ return [
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ randomNumber(), randomNumber(), randomNumber(), randomNumber(),
+ ];
+ }
+
+ function sum() : list<double> {
+ var numbers = root.numbers;
+
+ var cat1Sum = 0;
+ var cat2Sum = 0;
+ var cat3Sum = 0;
+ var huge = 0;
+ for (var i = 0; i < Categorizer.Iterations; ++i) {
+ for (var j = 0; j < Categorizer.Length; ++j) {
+ var num = numbers[j] & Categorizer.Mask;
+ if (num < Categorizer.Category0)
+ cat1Sum += num;
+ else if (num < Categorizer.Category1)
+ cat2Sum += num;
+ else if (num < Categorizer.Category2)
+ cat3Sum += num;
+ else
+ huge += num;
+ }
+ }
+
+ return [cat1Sum, cat2Sum, cat3Sum, huge];
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mergeSideEffects.qml b/tests/auto/qml/qmlcppcodegen/data/mergeSideEffects.qml
new file mode 100644
index 0000000000..d4a8fec42e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mergeSideEffects.qml
@@ -0,0 +1,20 @@
+import QtQml
+
+QtObject {
+ property bool no: false
+ property list<int> a: [1]
+ property list<int> b: [2]
+
+ property int c: {
+ let numbers = a;
+ a = [3]; // create side effect affecting "numbers"
+
+ if (no) {
+ // Force two branches to be merged on "numbers"
+ numbers = b
+ }
+
+ // Side effect is still in effect
+ return numbers[0];
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index dae83e2c86..07d1d99f00 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -36,6 +36,7 @@ private slots:
void signalHandler();
void idAccess();
void globals();
+ void mergeSideEffects();
void multiLookup();
void enums();
void funcWithParams();
@@ -196,6 +197,7 @@ private slots:
void equalityTestsWithNullOrUndefined();
void basicBlocksWithBackJump();
void listOfInvisible();
+ void writeAndReturnTempArray();
};
void tst_QmlCppCodegen::initTestCase()
@@ -3110,6 +3112,16 @@ void tst_QmlCppCodegen::inaccessibleProperty()
QCOMPARE(o->property("c").toInt(), 5);
}
+void tst_QmlCppCodegen::mergeSideEffects()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mergeSideEffects.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("c").toInt(), 3);
+}
+
void tst_QmlCppCodegen::typePropagationLoop()
{
QQmlEngine engine;
@@ -3772,6 +3784,42 @@ void tst_QmlCppCodegen::basicBlocksWithBackJump()
QVERIFY(!expectingMessage);
}
+void tst_QmlCppCodegen::writeAndReturnTempArray()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Categorizer.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const QVariant numbers = object->property("numbers");
+ QCOMPARE(numbers.metaType(), QMetaType::fromType<QList<double>>());
+ const QList<double> numbersContent = numbers.value<QList<double>>();
+ QCOMPARE(numbersContent.length(), 32);
+ for (double number: std::as_const(numbersContent))
+ QVERIFY(number >= 0 && number < double(0xf0f0f0));
+
+ QList<double> expected { 0, 0, 0, 0};
+ for (int i = 0; i < 2; ++i) {
+ for (double number : std::as_const(numbersContent)) {
+ const int num = QJSNumberCoercion::toInteger((number)) & 0xabcdef;
+ if (num < 0xf0f)
+ expected[0] += num;
+ else if (num < 0xf0f0)
+ expected[1] += num;
+ else if (num < 0xf0f0f)
+ expected[2] += num;
+ else
+ expected[3] += num;
+ }
+ }
+
+ QList<double> sum;
+ QMetaObject::invokeMethod(object.data(), "sum", Q_RETURN_ARG(QList<double>, sum));
+ QCOMPARE(sum, expected);
+}
+
QTEST_MAIN(tst_QmlCppCodegen)
#include "tst_qmlcppcodegen.moc"
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 9633d5dd1c..9475ef409e 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -357,7 +357,7 @@ void tst_qmldiskcache::regenerateAfterChange()
QCOMPARE(quint32(obj->nBindings), quint32(2));
QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number);
- const QV4::Value value(testUnit->constants()[obj->bindingTable()->value.constantValueIndex]);
+ const auto value = QV4::Value::fromReturnedValue(testUnit->constants()[obj->bindingTable()->value.constantValueIndex]);
QCOMPARE(value.doubleValue(), double(42));
QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
@@ -1353,7 +1353,7 @@ void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation()
testCompiler.reset();
QVERIFY(testCompiler.writeTestFile(code));
- QVERIFY(testCompiler.loadTestFile());
+ QVERIFY2(testCompiler.loadTestFile(), qPrintable(testCompiler.lastErrorString));
const quintptr data1 = testCompiler.unitData();
QVERIFY(data1 != 0);
diff --git a/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Main.qml b/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Main.qml
new file mode 100644
index 0000000000..ee82e0c96b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Main.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import Other
+
+Item {
+ InlineComponentSearchInfiniteLoop_Other.a {}
+}
diff --git a/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Other.qml b/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Other.qml
new file mode 100644
index 0000000000..724639b75d
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/InlineComponentSearchInfiniteLoop_Other.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ id: root
+ PropertyChanges {
+ root.whatever: 1
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/locationUnqualifiedLookupAssign.qml b/tests/auto/qml/qmllint/data/locationUnqualifiedLookupAssign.qml
new file mode 100644
index 0000000000..b770a71a44
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/locationUnqualifiedLookupAssign.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ function f() {
+ console.log(a = 1)
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/requiredPropertyInGroupedPropertyScope.qml b/tests/auto/qml/qmllint/data/requiredPropertyInGroupedPropertyScope.qml
new file mode 100644
index 0000000000..ba8d37cb10
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/requiredPropertyInGroupedPropertyScope.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ id: root
+ required property int i
+
+ PropertyChanges {
+ root.width: 100
+ }
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 3bb3853d6a..882b97be83 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -513,7 +513,7 @@ void TestQmllint::dirtyQmlCode_data()
3, 1 } } };
QTest::newRow("invalidAliasTarget1") << QStringLiteral("invalidAliasTarget.qml")
<< Result { { Message {
- QStringLiteral("Invalid alias expression – an initalizer is needed."),
+ QStringLiteral("Invalid alias expression - an initalizer is needed."),
6, 18 } } };
QTest::newRow("invalidAliasTarget2") << QStringLiteral("invalidAliasTarget.qml")
<< Result { { Message {
@@ -1109,6 +1109,13 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("missingRequiredOnObjectDefinitionBinding")
<< QStringLiteral("missingRequiredPropertyOnObjectDefinitionBinding.qml")
<< Result{ { { uR"(Component is missing required property i from here)"_s, 4, 26 } } };
+ QTest::newRow("locationUnqualifiedLookupAssign")
+ << QStringLiteral("locationUnqualifiedLookupAssign.qml")
+ << Result{ { Message{ QStringLiteral("Unqualified access"), 5, 21 } } };
+ QTest::newRow("inlineComponentSearchInfiniteLoop")
+ << QStringLiteral("InlineComponentSearchInfiniteLoop_Main.qml")
+ << Result{ { { "InlineComponentSearchInfiniteLoop_Other.a was not found. "
+ "Did you add all import paths?"_L1, 5, 5 } } };
}
void TestQmllint::dirtyQmlCode()
@@ -1246,7 +1253,8 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("QQmlEasingEnums::Type") << QStringLiteral("animationEasing.qml");
QTest::newRow("ValidLiterals") << QStringLiteral("validLiterals.qml");
QTest::newRow("GoodModulePrefix") << QStringLiteral("goodModulePrefix.qml");
- QTest::newRow("required property in Component") << QStringLiteral("requiredPropertyInComponent.qml");
+ QTest::newRow("required_property_in_Component") << QStringLiteral("requiredPropertyInComponent.qml");
+ QTest::newRow("requiredPropertyInGroupedPropertyScope") << QStringLiteral("requiredPropertyInGroupedPropertyScope.qml");
QTest::newRow("bytearray") << QStringLiteral("bytearray.qml");
QTest::newRow("initReadonly") << QStringLiteral("initReadonly.qml");
QTest::newRow("connectionNoParent") << QStringLiteral("connectionNoParent.qml"); // QTBUG-97600
@@ -1305,9 +1313,6 @@ void TestQmllint::cleanQmlCode_data()
void TestQmllint::cleanQmlCode()
{
QFETCH(QString, filename);
-
- QJsonArray warnings;
-
runTest(filename, Result::clean());
}
@@ -1365,8 +1370,6 @@ void TestQmllint::compilerWarnings()
QFETCH(Result, result);
QFETCH(bool, enableCompilerWarnings);
- QJsonArray warnings;
-
auto categories = QQmlJSLogger::defaultCategories();
auto category = std::find(categories.begin(), categories.end(), qmlCompiler);
diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp
index f477956060..e00cfa58c0 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.cpp
+++ b/tests/auto/qml/qmltc/tst_qmltc.cpp
@@ -96,6 +96,8 @@
#include <QtTest/qsignalspy.h>
+#include <QtGui/qquaternion.h>
+
#include <QtCore/private/qobject_p.h>
#include <QtTest/private/qemulationdetector_p.h>
#include <QtCore/qtranslator.h>
diff --git a/tests/auto/qml/qqmlbinding/data/bindingInDeadContext.qml b/tests/auto/qml/qqmlbinding/data/bindingInDeadContext.qml
new file mode 100644
index 0000000000..3e86a1a1fd
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/bindingInDeadContext.qml
@@ -0,0 +1,18 @@
+import QtQml
+
+QtObject {
+ id: outer
+ objectName: "outer"
+
+ property Component c: QtObject {
+ id: inner1
+ objectName: inner2.objectName + "a"
+ }
+
+ property QtObject inner1: c.createObject()
+
+ property QtObject inner2: QtObject {
+ id: inner2
+ objectName: "a"
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml b/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml
new file mode 100644
index 0000000000..fb95305791
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/deleteStashedObject.qml
@@ -0,0 +1,51 @@
+pragma ComponentBehavior: Bound
+import QtQml
+
+QtObject {
+ id: root
+
+ property Component c: Component {
+ QtObject {
+ Component.onDestruction: {
+ console.log("dead")
+ timer.start()
+ }
+ }
+ }
+
+ property QtObject stashed: c.createObject()
+ property QtObject replacement: QtObject {}
+
+ onStashedChanged: {
+ if (stashed) {
+ page = stashed
+ console.log("alive")
+ binding.when = true
+ }
+ }
+
+ property QtObject page
+
+ property Binding binding: Binding {
+ id: binding
+ target: root
+ property: "page"
+ when: false
+ value: root.replacement
+ }
+
+ property Timer timer: Timer {
+ id: timer
+ interval: 10
+ onTriggered: {
+ console.log("before")
+ binding.when = false
+ console.log("after")
+ }
+ }
+
+ Component.onCompleted: {
+ console.log("destroy")
+ stashed.destroy();
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/multiValueTypeBinding.qml b/tests/auto/qml/qqmlbinding/data/multiValueTypeBinding.qml
new file mode 100644
index 0000000000..7f1ba9b886
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/multiValueTypeBinding.qml
@@ -0,0 +1,16 @@
+import QtQml
+
+QtObject {
+ property alias labelY: label.rect.y
+ property alias labelWidth: label.rect.width
+
+ labelY: { return 24 }
+ labelWidth: 9
+
+ property QtObject label: QtObject {
+ id: label
+
+ property rect rect
+ rect.x: { return 12 }
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 9503ec80c5..a7bb2fa722 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -32,6 +32,7 @@ private slots:
void disabledOnReadonlyProperty();
void delayed();
void bindingOverwriting();
+ void bindingInDeadContext();
void bindToQmlComponent();
void bindingDoesNoWeirdConversion();
void bindNaNToInt();
@@ -41,6 +42,8 @@ private slots:
void whenEvaluatedEarlyEnough();
void propertiesAttachedToBindingItself();
void toggleEnableProperlyRemembersValues();
+ void deleteStashedObject();
+ void multiValueTypeBinding();
private:
QQmlEngine engine;
@@ -472,6 +475,40 @@ void tst_qqmlbinding::bindingOverwriting()
QLoggingCategory::setFilterRules(QString());
}
+void tst_qqmlbinding::bindingInDeadContext()
+{
+ // We manually control the deletion order of the objects here.
+ // This is what some of our views also do. One way to prevent
+ // the engine from deleting objects is to parent them to the
+ // application.
+
+ QScopedPointer<QObject> o;
+ QScopedPointer<QObject> inner1;
+ {
+ QScopedPointer<QObject> inner2;
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("bindingInDeadContext.qml"));
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ o.reset(c.create());
+ QVERIFY(!o.isNull());
+ o->setParent(QCoreApplication::instance());
+
+ inner1.reset(o->property("inner1").value<QObject *>());
+ QVERIFY(inner1);
+ inner1->setParent(QCoreApplication::instance());
+
+ inner2.reset(o->property("inner2").value<QObject *>());
+ QVERIFY(inner2);
+ inner2->setParent(QCoreApplication::instance());
+ }
+
+ // The objectName binding did not get re-evaluated when inner2 died
+ // because the engine was gone already.
+ QCOMPARE(inner1->objectName(), "aa");
+}
+
void tst_qqmlbinding::bindToQmlComponent()
{
QQmlEngine engine;
@@ -654,6 +691,41 @@ void tst_qqmlbinding::toggleEnableProperlyRemembersValues()
}
}
+void tst_qqmlbinding::deleteStashedObject()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("deleteStashedObject.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "alive");
+ QTest::ignoreMessage(QtDebugMsg, "destroy");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QVERIFY(object->property("page").value<QObject *>() != nullptr);
+
+ QTest::ignoreMessage(QtDebugMsg, "dead");
+ QTest::ignoreMessage(QtDebugMsg, "before");
+ QTest::ignoreMessage(QtDebugMsg, "after");
+ QTRY_VERIFY(object->property("page").value<QObject *>() == nullptr);
+}
+
+void tst_qqmlbinding::multiValueTypeBinding()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("multiValueTypeBinding.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+
+ QObject *label = object->property("label").value<QObject *>();
+ QVERIFY(label);
+
+ QRectF rect = label->property("rect").toRectF();
+ QCOMPARE(rect.x(), 12);
+ QCOMPARE(rect.y(), 24);
+ QCOMPARE(rect.width(), 9);
+}
+
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 59703d5c36..20eaa36970 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -22,6 +22,9 @@
#include <qcolor.h>
#include <qsignalspy.h>
#include "lifecyclewatcher.h"
+
+#include <QtGui/qquaternion.h>
+
#include <algorithm>
using namespace Qt::StringLiterals;
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index a14f980766..d4ac14bfb4 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -852,13 +852,13 @@ void tst_qqmlcontext::contextLeak()
QVERIFY(ddata);
QQmlRefPointer<QQmlContextData> context = ddata->context;
QVERIFY(context);
- QVERIFY(!context->importedScripts().isNullOrUndefined());
- QCOMPARE(int(context->importedScripts().valueRef()->as<QV4::Object>()->getLength()), 1);
+ QV4::Scope scope(engine.handle());
+ QV4::PersistentValue scopedScripts = context->importedScripts();
+ QVERIFY(!scopedScripts.isNullOrUndefined());
+ QCOMPARE(int(scopedScripts.as<QV4::Object>()->getLength()), 1);
- QV4::Scope scope(ddata->jsWrapper.engine());
QV4::ScopedValue scriptContextWrapper(scope);
- scriptContextWrapper = context->importedScripts().valueRef()
- ->as<QV4::Object>()->get(uint(0));
+ scriptContextWrapper = scopedScripts.as<QV4::Object>()->get(uint(0));
scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext();
}
diff --git a/tests/auto/qml/qqmlecmascript/data/jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown.qml b/tests/auto/qml/qqmlecmascript/data/jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown.qml
new file mode 100644
index 0000000000..7249113fd0
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown.qml
@@ -0,0 +1,40 @@
+import QtQml
+import QtQuick
+
+Window {
+ id: root
+
+ Item {
+ id: child
+
+ Timer {
+ id: timer
+
+ property int fuel: 10
+
+ interval: 1
+ repeat: true
+ running: true
+ onTriggered: {
+ if (--fuel == 0)
+ running = false;
+ parent.state = parent.state === "inactive" ? "active" : "inactive";
+ }
+ }
+
+ states: [
+ State {
+ name: "active"
+ StateChangeScript {
+ script: root.active = true;
+ }
+ },
+ State {
+ name: "inactive"
+ StateChangeScript {
+ script: root.active = false;
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 1612848dfb..dcde9a62f1 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -26,6 +26,7 @@
#include <private/qv4objectiterator_p.h>
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmlvaluetypeproxybinding_p.h>
+#include <private/qqmltimer_p.h>
#include <QtCore/private/qproperty_p.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickitem_p.h>
@@ -200,6 +201,7 @@ private slots:
void assignSequenceTypes();
void sequenceSort_data();
void sequenceSort();
+ void sequenceConversionViaSequentialIterableFallback();
void dateParse();
void utcDate();
void negativeYear();
@@ -425,6 +427,8 @@ private slots:
void methodCallOnDerivedSingleton();
+ void jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
@@ -8251,6 +8255,19 @@ void tst_qqmlecmascript::sequenceSort()
QVERIFY(q.toBool());
}
+void tst_qqmlecmascript::sequenceConversionViaSequentialIterableFallback()
+{
+ QQmlEngine engine;
+ QSet<QString> mySet { {"test"}, {"test2"}, {"test3"} };
+ QJSManagedValue jsManagedVal(QVariant::fromValue(mySet), &engine);
+ QVERIFY(jsManagedVal.isArray());
+ // we can't rely on any order in the set
+ QSet<QString> result;
+ for (int i = 0, end = jsManagedVal.property("length").toInt(); i != end; ++i)
+ result.insert(jsManagedVal.property(i).toString());
+ QCOMPARE(result, mySet);
+}
+
void tst_qqmlecmascript::dateParse()
{
QQmlEngine engine;
@@ -10504,6 +10521,24 @@ void tst_qqmlecmascript::methodCallOnDerivedSingleton()
QVERIFY(singleton->m_okay);
}
+void tst_qqmlecmascript::jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown()
+{
+ QQmlEngine engine;
+
+ engine.handle()->memoryManager->aggressiveGC = true;
+
+ QQmlComponent c(&engine, testFileUrl("jittedJavaScriptExpressionDoesNotCrashOnExceptionBeingThrown.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY2(o, qPrintable(c.errorString()));
+
+ QQmlContext *context = qmlContext(o.data());
+ auto timer = qobject_cast<QQmlTimer*>(context->objectForName("timer"));
+ QVERIFY(timer);
+
+ QTRY_VERIFY(!timer->isRunning());
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index bf68de0d2f..0dfb78fef1 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -1581,8 +1581,10 @@ void tst_qqmlengine::stringToColor()
const QMetaType metaType(QMetaType::QColor);
QVariant color(metaType);
+ QV4::Scope scope(engine.handle());
+ QV4::ScopedValue colorString(scope, engine.handle()->newString(QStringLiteral("#abcdef")));
QVERIFY(QV4::ExecutionEngine::metaTypeFromJS(
- engine.handle()->newString(QStringLiteral("#abcdef"))->asReturnedValue(),
+ colorString,
metaType, color.data()));
QVERIFY(color.isValid());
QCOMPARE(color.metaType(), metaType);
diff --git a/tests/auto/qml/qqmllanguage/data/WithInline.qml b/tests/auto/qml/qqmllanguage/data/WithInline.qml
new file mode 100644
index 0000000000..3620574c8f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/WithInline.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ objectName: "outer"
+ component Inline: QtObject {
+ objectName: "inner"
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.errors.txt
new file mode 100644
index 0000000000..e072be7a4e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.errors.txt
@@ -0,0 +1 @@
+8:5:Cannot assign to non-existent property "root"
diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.qml b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.qml
new file mode 100644
index 0000000000..edb32af51c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.11.qml
@@ -0,0 +1,9 @@
+import QtQml
+QtObject {
+ id: root
+ component C : QtObject {
+ property int i
+ }
+ property C c: C {}
+ root.c.i: 4
+}
diff --git a/tests/auto/qml/qqmllanguage/data/urlWithFragment.qml b/tests/auto/qml/qqmllanguage/data/urlWithFragment.qml
new file mode 100644
index 0000000000..f68c816c10
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/urlWithFragment.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Loader {
+ source: 'WithInline.qml#0'
+ objectName: item?.objectName ?? "none"
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index df89dcff39..e793bae848 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -447,6 +447,8 @@ private slots:
void aliasOfBindableValueTypeProperty();
+ void urlWithFragment();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -633,6 +635,7 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("invalidGroupedProperty.8") << "invalidGroupedProperty.8.qml" << "invalidGroupedProperty.8.errors.txt" << false;
QTest::newRow("invalidGroupedProperty.9") << "invalidGroupedProperty.9.qml" << "invalidGroupedProperty.9.errors.txt" << false;
QTest::newRow("invalidGroupedProperty.10") << "invalidGroupedProperty.10.qml" << "invalidGroupedProperty.10.errors.txt" << false;
+ QTest::newRow("invalidGroupedProperty.11") << "invalidGroupedProperty.11.qml" << "invalidGroupedProperty.11.errors.txt" << false;
QTest::newRow("importNamespaceConflict") << "importNamespaceConflict.qml" << "importNamespaceConflict.errors.txt" << false;
QTest::newRow("importVersionMissing (builtin)") << "importVersionMissingBuiltIn.qml" << "importVersionMissingBuiltIn.errors.txt" << false;
@@ -8393,6 +8396,17 @@ void tst_qqmllanguage::aliasOfBindableValueTypeProperty()
QCOMPARE(bindable.metaType(), QMetaType::fromType<QPointF>());
}
+void tst_qqmllanguage::urlWithFragment()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("urlWithFragment.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->objectName(), "outer");
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/data/valuesOfInnerList.qml b/tests/auto/qml/qqmllistmodel/data/valuesOfInnerList.qml
new file mode 100644
index 0000000000..9d28111da4
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/valuesOfInnerList.qml
@@ -0,0 +1,17 @@
+import QtQml 2.15
+
+ListModel {
+ id: listModel
+
+ Component.onCompleted: {
+ const rawObj = { innerList: [1, 2, 3] }
+ listModel.append(rawObj)
+ const convertedObj = listModel.get(0)
+
+ console.log(rawObj.innerList)
+ console.log(Object.values(rawObj.innerList))
+
+ console.log(convertedObj.innerList)
+ console.log(Object.values(convertedObj.innerList))
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 4010be56b3..ba0e83238b 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -120,6 +120,7 @@ private slots:
void enumsInListElement();
void protectQObjectFromGC();
void deadModelData();
+ void valuesOfInnerList();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -2003,6 +2004,24 @@ void tst_qqmllistmodel::deadModelData()
}
}
+void tst_qqmllistmodel::valuesOfInnerList()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("valuesOfInnerList.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "[1,2,3]");
+ QTest::ignoreMessage(QtDebugMsg, "[1,2,3]");
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("QQmlListModel\\(0x[0-9a-f]*\\)"));
+
+ // Array with values of all the properties of QQmlListModel, one of which is "agent"
+ QTest::ignoreMessage(
+ QtDebugMsg, QRegularExpression("\\[.*QQmlListModelWorkerAgent\\(0x[0-9a-f]*\\).*\\]"));
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt
deleted file mode 100644
index de75f47c03..0000000000
--- a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt
+++ /dev/null
@@ -1 +0,0 @@
-1:1:module "org.qtproject.AutoTestQmlNestedPluginType.Nested" is not installed
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
index 35fff29a69..5148653a53 100644
--- a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
+++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
@@ -1,5 +1,8 @@
import org.qtproject.AutoTestQmlNestedPluginType.Nested 1.0
import org.qtproject.AutoTestQmlNestedPluginType 1.0
+import QtQml
MyNestedPluginType {
+ property Conflict conflict: Conflict {}
+ Component.onCompleted: console.log(conflict.value)
}
diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
index 4400c11996..47fba6049f 100644
--- a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
@@ -40,11 +40,13 @@ public:
{
Q_ASSERT(QLatin1String(uri) == "org.qtproject.AutoTestQmlNestedPluginType");
qmlRegisterType<MyPluginType>(uri, 1, 0, "MyPluginType");
+ qmlRegisterType<MyPluginType>(uri, 1, 0, "Conflict");
QString nestedUri(uri);
nestedUri += QLatin1String(".Nested");
qmlRegisterType<MyNestedPluginType>(nestedUri.toLatin1().constData(), 1, 0, "MyNestedPluginType");
+ qmlRegisterType<MyNestedPluginType>(nestedUri.toLatin1().constData(), 1, 0, "Conflict");
}
};
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index a4901aebad..ff12a150a5 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -453,18 +453,22 @@ void tst_qqmlmoduleplugin::importsNested_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("errorFile");
-
- // Note: no other test case should import the plugin used for this test, or the
- // wrong order test will pass spuriously
- QTest::newRow("wrongOrder") << "importsNested.1.qml" << "importsNested.1.errors.txt";
- QTest::newRow("missingImport") << "importsNested.3.qml" << "importsNested.3.errors.txt";
- QTest::newRow("invalidVersion") << "importsNested.4.qml" << "importsNested.4.errors.txt";
- QTest::newRow("correctOrder") << "importsNested.2.qml" << QString();
+ QTest::addColumn<bool>("expectGreeting");
+
+ // NB: The order is wrong in the sense that it tries to load the "Nested" URI first, which
+ // is not visible in the file system. However, in the process of loading the non-nested
+ // URI it can discover the types for the nested one and insert them in the right place.
+ // This used to be an error but doesn't have to be.
+ QTest::newRow("wrongOrder") << "importsNested.1.qml" << QString() << true;
+ QTest::newRow("missingImport") << "importsNested.3.qml" << "importsNested.3.errors.txt" << false;
+ QTest::newRow("invalidVersion") << "importsNested.4.qml" << "importsNested.4.errors.txt" << false;
+ QTest::newRow("correctOrder") << "importsNested.2.qml" << QString() << false;
}
void tst_qqmlmoduleplugin::importsNested()
{
QFETCH(QString, file);
QFETCH(QString, errorFile);
+ QFETCH(bool, expectGreeting);
// Note: because imports are cached between test case data rows (and the plugins remain loaded),
// these tests should really be run in new instances of the app...
@@ -481,6 +485,10 @@ void tst_qqmlmoduleplugin::importsNested()
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlNestedPluginType' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFile(file));
+
+ if (expectGreeting)
+ QTest::ignoreMessage(QtDebugMsg, "Hello");
+
QObject *obj = component.create();
if (errorFile.isEmpty()) {
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 396f876fe4..b64b39defe 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -1346,6 +1346,18 @@ void tst_qqmlproperty::read()
QCOMPARE(QQmlProperty::read(object.data(), "Foo.MyContainer.foo",
qmlContext(object.data())), QVariant(10));
}
+
+ // value type list
+ {
+ QQmlComponent component(&engine);
+ component.setData("import QtQml\nQtObject { property list<string> strings: [`a`, `b`] }", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != nullptr);
+
+ QQmlProperty p(object.data(), "strings", qmlContext(object.data()));
+ QStringList expected {"a", "b"};
+ QCOMPARE(p.read(), expected);
+ }
}
void tst_qqmlproperty::write()
diff --git a/tests/auto/qml/qqmlsettings/data/coexistence/Core.qml b/tests/auto/qml/qqmlsettings/data/coexistence/Core.qml
new file mode 100644
index 0000000000..ae786263fb
--- /dev/null
+++ b/tests/auto/qml/qqmlsettings/data/coexistence/Core.qml
@@ -0,0 +1,5 @@
+import QtQuick
+import QtCore
+
+Item {}
+
diff --git a/tests/auto/qml/qqmlsettings/data/coexistence/Labs.qml b/tests/auto/qml/qqmlsettings/data/coexistence/Labs.qml
new file mode 100644
index 0000000000..54cdfcdfb9
--- /dev/null
+++ b/tests/auto/qml/qqmlsettings/data/coexistence/Labs.qml
@@ -0,0 +1,8 @@
+import QtQuick
+import Qt.labs.settings as L
+
+Item {
+ id: root
+ Core {}
+ property L.Settings settings: L.Settings {}
+}
diff --git a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
index 88d8c58e2c..00757fec07 100644
--- a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
+++ b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
@@ -32,6 +32,7 @@ private slots:
void siblings();
void initial();
void noApplicationIdentifiersSet();
+ void coexistence();
};
// ### Replace keyValueMap("foo", "bar") with QVariantMap({{"foo", "bar"}})
@@ -492,6 +493,18 @@ void tst_QQmlSettings::noApplicationIdentifiersSet()
QVERIFY(!settings.value("success").toBool());
}
+
+void tst_QQmlSettings::coexistence()
+{
+ QQmlEngine engine;
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*QML Settings: The Settings type from Qt.labs.settings is deprecated and will be removed in a future release. Please use the one from QtCore instead."
+ ));
+ QQmlComponent component(&engine, testFileUrl("coexistence/Labs.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root.data());
+}
+
QTEST_MAIN(tst_QQmlSettings)
#include "tst_qqmlsettings.moc"
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
index a862af35d2..d90631fb38 100644
--- a/tests/auto/qml/qqmltablemodel/data/TestModel.qml
+++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
@@ -3,8 +3,6 @@
import Qt.labs.qmlmodels 1.0
-import "TestUtils.js" as TestUtils
-
TableModel {
id: testModel
objectName: "testModel"
diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
deleted file mode 100644
index 83ac1b80a8..0000000000
--- a/tests/auto/qml/qqmltablemodel/data/TestUtils.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-function testModelRoleDataProvider(index, role, cellData) {
- switch (role) {
- case "display":
- switch (index.column) {
- case 0:
- return cellData.name
- case 1:
- return cellData.age
- }
- break
- case "name":
- return cellData.name
- case "age":
- return cellData.age
- }
- return cellData
-}
diff --git a/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml b/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml
new file mode 100644
index 0000000000..d8b380928c
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml
@@ -0,0 +1,7 @@
+import QtQml
+import Qt.example.qobjectSingleton 1.0
+
+QtObject {
+ property int someValue: MyApi.someProperty
+ property int doneSomething: MyApi.doSomething()
+}
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index cb4610d01b..8ef73de70c 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -4,6 +4,7 @@
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlapplicationengine.h>
#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
@@ -50,6 +51,7 @@ private slots:
void signalHandlersAreCompatible();
void loadTypeOnShutdown();
void floodTypeLoaderEventQueue();
+ void loadLocalTypesAfterRemoteFails();
private:
void checkSingleton(const QString & dataDirectory);
@@ -829,6 +831,65 @@ void tst_QQMLTypeLoader::floodTypeLoaderEventQueue()
}
}
+class SingletonTypeExample : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
+
+public:
+ explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE int doSomething()
+ {
+ setSomeProperty(5);
+ return m_someProperty;
+ }
+
+ int someProperty() const { return m_someProperty; }
+ void setSomeProperty(int val) {
+ if (m_someProperty != val) {
+ m_someProperty = val;
+ emit somePropertyChanged(val);
+ }
+ }
+
+signals:
+ void somePropertyChanged(int newValue);
+
+private:
+ int m_someProperty = 0;
+};
+
+class HttpUrlInterceptor : public QQmlAbstractUrlInterceptor
+{
+public:
+ QUrl intercept(const QUrl &path, DataType type) override
+ {
+ QUrl result = path;
+ if (path.scheme() == "http" && type == QmldirFile)
+ result.setFragment("qmldir");
+ return result;
+ }
+};
+
+void tst_QQMLTypeLoader::loadLocalTypesAfterRemoteFails()
+{
+ std::unique_ptr<SingletonTypeExample> example = std::make_unique<SingletonTypeExample>();
+ qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
+
+ HttpUrlInterceptor interceptor;
+ QQmlEngine engine;
+ engine.addUrlInterceptor(&interceptor);
+ engine.addImportPath(QString("http:/127.0.0.1/"));
+
+ QQmlComponent component(&engine, testFileUrl("qobjectSingletonUser.qml"));
+ QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> object(component.create());
+ QCOMPARE(object->property("someValue").toInt(), 5);
+ QCOMPARE(object->property("doneSomething").toInt(), 5);
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
index 930ac11fbe..ad475cf1a1 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
+++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
@@ -12,6 +12,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include "testtypes.h"
+#include <QtGui/qquaternion.h>
+
QT_BEGIN_NAMESPACE
extern int qt_defaultDpi(void);
QT_END_NAMESPACE
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 1f9dfb7fd4..cb157d1a8c 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -14,6 +14,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include "testtypes.h"
+#include <QtGui/qquaternion.h>
+
QT_BEGIN_NAMESPACE
extern int qt_defaultDpi(void);
QT_END_NAMESPACE
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST b/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST
index 74d29b4ac3..cb290fe641 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST
@@ -1,7 +1,3 @@
# QTBUG-95938
[dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch]
opensuse-leap
-
-# QTBUG-95938
-[dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse]
-opensuse-leap
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
index 556acbc828..6331a2d390 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
@@ -43,6 +43,7 @@ void tst_MouseAreaInterop::createView(QScopedPointer<QQuickView> &window, const
window.reset(new QQuickView);
window->setSource(testFileUrl(fileName));
QTRY_COMPARE(window->status(), QQuickView::Ready);
+ window.data()->setFlag(Qt::FramelessWindowHint);
QQuickViewTestUtils::centerOnScreen(window.data());
QQuickViewTestUtils::moveMouseAway(window.data());
@@ -58,6 +59,10 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse
createView(windowPtr, "dragTakeOverFromSibling.qml");
QQuickView * window = windowPtr.data();
+ const QPoint pos = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
+ window->setFramePosition(pos);
+ QTRY_COMPARE(window->framePosition(), pos);
+
QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
QVERIFY(handler);
QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
@@ -108,6 +113,10 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch
QQuickView * window = windowPtr.data();
auto devPriv = QPointingDevicePrivate::get(touchDevice);
+ const QPoint pos = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
+ window->setFramePosition(pos);
+ QTRY_COMPARE(window->framePosition(), pos);
+
QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
QVERIFY(handler);
QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index b3b2259319..bff6ca9733 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -394,14 +394,25 @@ void tst_HoverHandler::movingItemWithHoverHandler()
QTRY_COMPARE(paddleHH->isHovered(), true);
// TODO check the cursor shape after fixing QTBUG-53987
+ const auto &deliveryTargets =
+ QQuickPointerHandlerPrivate::deviceDeliveryTargets(QPointingDevice::primaryPointingDevice());
+ const auto targetsCount = deliveryTargets.size();
+ qCDebug(lcPointerTests) << "deviceDeliveryTargets before paddle movement" << deliveryTargets;
paddle->setX(100);
QTRY_COMPARE(paddleHH->isHovered(), false);
+ // QQuickDeliveryAgentPrivate::deliverHoverEvent() clears the deviceDeliveryTargets list,
+ // and then each HoverHandler's QQuickPointerHandler::handlePointerEvent() adds itself again.
+ // As long as we visit the same handlers each time, the list should not grow. (QTBUG-135975)
+ qCDebug(lcPointerTests) << "deviceDeliveryTargets after paddle movement" << deliveryTargets;
+ QCOMPARE(deliveryTargets.size(), targetsCount);
paddle->setX(p.x() - paddle->width() / 2);
QTRY_COMPARE(paddleHH->isHovered(), true);
+ QCOMPARE(deliveryTargets.size(), targetsCount);
paddle->setX(540);
QTRY_COMPARE(paddleHH->isHovered(), false);
+ QCOMPARE(deliveryTargets.size(), targetsCount);
}
void tst_HoverHandler::margin() // QTBUG-85303
diff --git a/tests/auto/quick/qquickanimations/data/animationInstantiator.qml b/tests/auto/quick/qquickanimations/data/animationInstantiator.qml
new file mode 100644
index 0000000000..48206073d0
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/animationInstantiator.qml
@@ -0,0 +1,40 @@
+import QtQuick
+
+Item {
+ id: root
+ property int v: 0
+
+ Transition {
+ id: transition
+ NumberAnimation {}
+ }
+
+ Instantiator {
+ id: instantiator
+
+ delegate: NumberAnimation {}
+
+ onObjectAdded: (index, object) => {
+ transition.animations.splice(index, 0, object)
+ }
+
+ onObjectRemoved: (index, object) => {
+ // NB: This is a logic error. The indices of other entries change when we remove one
+ // from the middle, but Instantiator will still report the old indices.
+ // We still want to do this, in order ot exercise the code that nulls dangling
+ // pointers.
+ transition.animations.splice(index, 1)
+ }
+ }
+
+ Timer {
+ interval: 2
+ running: true
+ repeat: true
+ onTriggered: {
+ instantiator.model = (++v % 2) ? 0 : 10
+ }
+ }
+}
+
+
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index ce5473c8a5..083efc9213 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -102,6 +102,7 @@ private slots:
void restartNestedAnimationGroupWhenDirty();
void targetsDeletedNotRemoved();
void alwaysRunToEndSetFalseRestartBug();
+ void animationInstantiator();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -2331,6 +2332,16 @@ void tst_qquickanimations::alwaysRunToEndSetFalseRestartBug()
QCOMPARE(sequential.isRunning(), false);
}
+void tst_qquickanimations::animationInstantiator()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("animationInstantiator.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_VERIFY(o->property("v").toInt() > 10);
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquickapplication/BLACKLIST b/tests/auto/quick/qquickapplication/BLACKLIST
deleted file mode 100644
index b8bc4363f1..0000000000
--- a/tests/auto/quick/qquickapplication/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[state]
-opensuse-leap # QTBUG-122031
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index 0bac4baaea..aed74dce10 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -165,7 +165,8 @@ void tst_qquickapplication::state()
// Otherwise, the application can only be in two states, Active and Inactive. These are
// triggered by window activation.
window.show();
- window.requestActivate();
+ if (QGuiApplication::platformName().toLower() != QLatin1String("xcb"))
+ window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QGuiApplication::focusWindow(), &window);
QCOMPARE(Qt::ApplicationState(item->property("state").toInt()), Qt::ApplicationActive);
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index a686534d20..e463fc0b45 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -1,6 +1,3 @@
-#QTBUG-53863
-[populateTransitions]
-opensuse-42.1
# QTBUG-75202
[contentHeightWithDelayRemove]
macos ci
diff --git a/tests/auto/quick/qquickloader/data/invalidateContext.qml b/tests/auto/quick/qquickloader/data/invalidateContext.qml
new file mode 100644
index 0000000000..b5a28f5d0f
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/invalidateContext.qml
@@ -0,0 +1,28 @@
+import QtQuick
+
+Item {
+ Loader {
+ active: loaderActive
+ sourceComponent: comp
+ }
+
+ Component {
+ id: comp
+ Column {
+ Repeater {
+ id: repeater
+ model: cppModel
+
+ Component.onCompleted: console.log("Repeater constructed");
+ Component.onDestruction: console.log("Repeater destroyed");
+
+ delegate: Text {
+ text: {
+ console.log("updating text");
+ return display + rootData.getValue();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index 389f037166..afc49fe74d 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -117,6 +117,8 @@ private slots:
void stackOverflow();
void stackOverflow2();
void boundComponent();
+
+ void invalidateContext();
};
Q_DECLARE_METATYPE(QList<QQmlError>)
@@ -1585,6 +1587,60 @@ void tst_QQuickLoader::boundComponent()
QCOMPARE(o->objectName(), QStringLiteral("loaded"));
}
+class CppModel : public QAbstractListModel
+{
+public:
+ virtual int rowCount(const QModelIndex &) const override {return 1;}
+ virtual QVariant data(const QModelIndex &, int) const override { return "test"; }
+
+ void reset () {beginResetModel(); endResetModel();}
+};
+
+class RootData : public QObject
+{
+ Q_OBJECT
+public:
+ RootData() : object(std::make_unique<QObject>())
+ {
+ object->setObjectName("objectName");
+ }
+
+ Q_INVOKABLE QString getValue() const { return object->objectName(); }
+
+ void deleteObject() { object.reset(); }
+
+private:
+ std::unique_ptr<QObject> object;
+};
+
+void tst_QQuickLoader::invalidateContext()
+{
+ CppModel model;
+ RootData rootData;
+
+ QQmlEngine engine;
+ QQmlContext *rootContext = engine.rootContext();
+ rootContext->setContextProperty("cppModel", &model);
+ rootContext->setContextProperty("loaderActive", true);
+ rootContext->setContextProperty("rootData", &rootData);
+
+ QQmlComponent component(&engine, testFileUrl("invalidateContext.qml"));\
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "Repeater constructed");
+ QTest::ignoreMessage(QtDebugMsg, "updating text");
+ QScopedPointer<QObject> o(component.create());
+
+ QTest::ignoreMessage(QtDebugMsg, "Repeater destroyed");
+ rootContext->setContextProperty("loaderActive", false);
+
+ // The object should never be used anymore, as loader is inactive
+ rootData.deleteObject();
+
+ // Should not trigger any re-evaluation (which would crash in getValue())
+ model.reset();
+}
+
QTEST_MAIN(tst_QQuickLoader)
#include "tst_qquickloader.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickableWithScale.qml b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickableWithScale.qml
new file mode 100644
index 0000000000..9af4c36241
--- /dev/null
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickableWithScale.qml
@@ -0,0 +1,27 @@
+import QtQuick
+
+Rectangle {
+ id: root
+ width: 240
+ height: 320
+
+ property bool gestureStarted: false
+
+ Flickable {
+ id: flick
+ anchors.fill: parent
+ anchors.margins: 50
+ clip: true
+ contentWidth: width
+ contentHeight: height * 2
+
+ scale: 0.5
+
+ MultiPointTouchArea {
+ anchors.fill: parent
+ onGestureStarted: (gesture) => {
+ root.gestureStarted = true
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index 81e57bfd12..6fe27ebe6f 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -40,6 +40,7 @@ private slots:
void inFlickable();
void inFlickable2();
void inFlickableWithPressDelay();
+ void inFlickableWithScale();
void inMouseArea();
void mouseAsTouchpoint();
void invisible();
@@ -860,6 +861,35 @@ void tst_QQuickMultiPointTouchArea::inFlickableWithPressDelay() // QTBUG-78818
QTRY_VERIFY(!flickable->isMoving());
}
+// QTBUG-131886
+void tst_QQuickMultiPointTouchArea::inFlickableWithScale()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inFlickableWithScale.qml")));
+
+ QQuickFlickable *flickable = window.rootObject()->findChild<QQuickFlickable*>();
+ QVERIFY(flickable != nullptr);
+ QQuickMultiPointTouchArea *mpta = window.rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta != nullptr);
+
+ QSignalSpy gestureStartedSpy(mpta, &QQuickMultiPointTouchArea::gestureStarted);
+
+ // press and drag over dragThreshold emits gestureStarted even when scaled
+ QPoint p1(120, 160); // size is 240 x 320, press in the center
+ QTest::touchEvent(&window, device).press(0, p1);
+ QQuickTouchUtils::flush(&window);
+
+ p1 += QPoint(0, dragThreshold);
+ QTest::touchEvent(&window, device).move(0, p1);
+ QQuickTouchUtils::flush(&window);
+
+ QCOMPARE(gestureStartedSpy.size(), 1);
+
+ QTest::touchEvent(&window, device).release(0, p1);
+ QQuickTouchUtils::flush(&window);
+}
+
// QTBUG-31047
void tst_QQuickMultiPointTouchArea::inMouseArea()
{
diff --git a/tests/auto/quick/qquickshape/data/changeElementsImperatively.qml b/tests/auto/quick/qquickshape/data/changeElementsImperatively.qml
new file mode 100644
index 0000000000..129be7222a
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/changeElementsImperatively.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import QtQuick.Shapes
+
+Shape {
+ width: 640
+ height: 480
+
+ Component.onCompleted: {
+ bottomPathLine.x = 20
+ }
+
+ ShapePath {
+ PathLine {}
+ PathLine {
+ id: bottomPathLine
+ }
+ PathArc {}
+ PathLine {}
+ PathArc {}
+ }
+}
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index a9e15939d3..c9e7af5ee7 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -62,6 +62,7 @@ private slots:
void multilineDataTypes_data();
void multilineDataTypes();
void multilineStronglyTyped();
+ void changeElementsImperatively();
private:
QVector<QPolygonF> m_lowPolyLogo;
@@ -674,6 +675,13 @@ void tst_QQuickShape::multilineStronglyTyped()
}
}
+void tst_QQuickShape::changeElementsImperatively()
+{
+ QQuickView window;
+ // Shouldn't crash.
+ QVERIFY(QQuickTest::showView(window, testFileUrl("changeElementsImperatively.qml")));
+}
+
QTEST_MAIN(tst_QQuickShape)
#include "tst_qquickshape.moc"
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index a4e9c44eab..3c64442e2b 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -1,5 +1,4 @@
-[fontSizeMode]
-opensuse-42.1
+
[contentSize]
windows gcc
msvc-2019
diff --git a/tests/auto/quick/qquicktextedit/data/readOnly.qml b/tests/auto/quick/qquicktextedit/data/readOnly.qml
index 085adba5fb..4aea6a78ab 100644
--- a/tests/auto/quick/qquicktextedit/data/readOnly.qml
+++ b/tests/auto/quick/qquicktextedit/data/readOnly.qml
@@ -1,12 +1,38 @@
-import QtQuick 2.0
+import QtQuick
Rectangle {
property variant myInput: input
width: 800; height: 600; color: "blue"
- TextEdit { id: input; focus: true
+ property bool activateSelectAllShortcut: false
+ property bool activateCopyShortcut: false
+ property bool activatePageupShortcut: false
+ property bool acceptShortcutOverride: true
+
+ Shortcut {
+ sequences: [StandardKey.SelectAll]
+ onActivated: { activateSelectAllShortcut = true }
+ }
+
+ Shortcut {
+ sequences: [StandardKey.Copy]
+ onActivated: { activateCopyShortcut = true }
+ }
+
+ Shortcut {
+ sequences: [StandardKey.MoveToPreviousPage]
+ onActivated: { activatePageupShortcut = true }
+ }
+
+ TextEdit {
+ id: input
+
+ focus: true
readOnly: true
text: "I am the very model of a modern major general.\n"
+ Keys.onShortcutOverride: event => {
+ event.accepted = acceptShortcutOverride
+ }
}
}
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 399627c4f1..31945b3776 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -3256,11 +3256,53 @@ void tst_qquicktextedit::readOnly()
QString initial = edit->text();
for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
simulateKey(&window, k);
+
simulateKey(&window, Qt::Key_Return);
simulateKey(&window, Qt::Key_Space);
simulateKey(&window, Qt::Key_Escape);
QCOMPARE(edit->text(), initial);
+ // Read-only shall support Selection and Copy by default
+ QCOMPARE(window.rootObject()->property("acceptShortcutOverride").toBool(), true);
+ simulateKeys(&window, QKeySequence(QKeySequence::SelectAll));
+ QCOMPARE(window.rootObject()->property("activateSelectAllShortcut").toBool(), false);
+ QCOMPARE(edit->cursorPosition(), edit->text().size());
+ QCOMPARE(edit->selectedText(), edit->text());
+#ifndef QT_NO_CLIPBOARD
+ // Copy the selected text into the clipboard
+ if (PlatformQuirks::isClipboardAvailable()) {
+ if (auto *clipBoard = QGuiApplication::clipboard()) {
+ simulateKeys(&window, QKeySequence(QKeySequence::Copy));
+ QCOMPARE(window.rootObject()->property("activateCopyShortcut").toBool(), false);
+ QCOMPARE(clipBoard->text(), edit->text());
+ clipBoard->clear();
+ }
+ }
+#endif
+ edit->setCursorPosition(0);
+
+ window.rootObject()->setProperty("acceptShortcutOverride", false);
+ QCOMPARE(window.rootObject()->property("acceptShortcutOverride").toBool(), false);
+ simulateKeys(&window, QKeySequence(QKeySequence::SelectAll));
+ QCOMPARE(window.rootObject()->property("activateSelectAllShortcut").toBool(), true);
+ QCOMPARE(edit->cursorPosition(), 0);
+ QCOMPARE(edit->selectedText(), "");
+#ifndef QT_NO_CLIPBOARD
+ // Copy the selected text into the clipboard
+ if (PlatformQuirks::isClipboardAvailable()) {
+ if (auto *clipBoard = QGuiApplication::clipboard()) {
+ simulateKeys(&window, QKeySequence(QKeySequence::Copy));
+ QCOMPARE(window.rootObject()->property("activateCopyShortcut").toBool(), true);
+ QCOMPARE(clipBoard->text(), "");
+ }
+ }
+#endif
+ simulateKeys(&window, QKeySequence(QKeySequence::MoveToPreviousPage));
+ QCOMPARE(window.rootObject()->property("activatePageupShortcut").toBool(), true);
+
+ window.rootObject()->setProperty("acceptShortcutOverride", true);
+ QCOMPARE(window.rootObject()->property("acceptShortcutOverride").toBool(), true);
+
edit->setCursorPosition(3);
edit->setReadOnly(false);
QCOMPARE(edit->isReadOnly(), false);
diff --git a/tests/auto/quick/qquicktreeview/data/editdelegate.qml b/tests/auto/quick/qquicktreeview/data/editdelegate.qml
new file mode 100644
index 0000000000..f8f908807f
--- /dev/null
+++ b/tests/auto/quick/qquicktreeview/data/editdelegate.qml
@@ -0,0 +1,91 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ width: 800
+ height: 600
+
+ property alias treeView: treeView
+ property alias textInput: textInput
+
+ TextInput {
+ id: textInput
+ width: 100
+ height: 10
+ }
+
+ TreeView {
+ id: treeView
+ anchors.fill: parent
+ clip: true
+
+ property Item editItem: null
+ property var editIndex
+
+ selectionModel: ItemSelectionModel {}
+
+ delegate: Rectangle {
+ id: root
+ implicitWidth: 100
+ implicitHeight: 50
+ clip: true
+ color: current || selected ? "lightgreen" : "white"
+
+ property alias text: label.text
+
+ readonly property real indent: 20
+ readonly property real padding: 5
+
+ // Assigned to by TreeView:
+ required property TreeView treeView
+ required property bool isTreeNode
+ required property bool expanded
+ required property int hasChildren
+ required property int depth
+ required property bool current
+ required property bool selected
+ required property bool editing
+
+ Text {
+ id: indicator
+ visible: root.isTreeNode && root.hasChildren
+ x: padding + (root.depth * root.indent)
+ text: root.expanded ? "▼" : "▶"
+ }
+
+ Text {
+ id: label
+ x: padding + (root.isTreeNode ? (root.depth + 1) * root.indent : 0)
+ width: root.width - root.padding - x
+ clip: true
+ text: model.display
+ }
+
+ TableView.editDelegate: TextInput {
+ id: editRoot
+ anchors.fill: parent
+ text: display
+ horizontalAlignment: TextInput.AlignHCenter
+ verticalAlignment: TextInput.AlignVCenter
+ activeFocusOnTab: true
+
+ required property bool editing
+
+ Component.onCompleted: {
+ treeView.editItem = editRoot
+ treeView.editIndex = treeView.index(row, column)
+ selectAll()
+ }
+
+ Component.onDestruction: {
+ treeView.editItem = null
+ treeView.editIndex = treeView.index(-1, -1)
+ }
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktreeview/testmodel.cpp b/tests/auto/quick/qquicktreeview/testmodel.cpp
index a6a756ba68..231df4c5b0 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.cpp
+++ b/tests/auto/quick/qquicktreeview/testmodel.cpp
@@ -129,7 +129,6 @@ bool TestModel::insertRows(int position, int rows, const QModelIndex &parent)
return true;
}
-
void insertColumnsRecursive(TreeItem *item, int row, int pos, int cols)
{
for (int col = 0; col < cols; col++)
@@ -158,3 +157,11 @@ bool TestModel::insertColumns(int position, int cols, const QModelIndex &parent)
endInsertColumns();
return true;
}
+
+Qt::ItemFlags TestModel::flags(const QModelIndex &index) const
+{
+ if (m_editableIndices.contains(index))
+ return Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable;
+
+ return Qt::ItemIsSelectable|Qt::ItemIsEnabled;
+}
diff --git a/tests/auto/quick/qquicktreeview/testmodel.h b/tests/auto/quick/qquicktreeview/testmodel.h
index 182211e050..b7d10fb468 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.h
+++ b/tests/auto/quick/qquicktreeview/testmodel.h
@@ -36,11 +36,14 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
int maxDepth() { return 4; }
bool insertRows(int position, int rows, const QModelIndex &parent) override;
bool insertColumns(int position, int cols, const QModelIndex &parent) override;
+ QList<QModelIndex> m_editableIndices;
+
private:
QScopedPointer<TreeItem> m_rootItem;
int m_columnCount = 5;
diff --git a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
index e16797c7fa..e0ddc3d731 100644
--- a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
+++ b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
@@ -7,6 +7,7 @@
#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquicktreeview_p.h>
#include <QtQuick/private/qquicktreeview_p_p.h>
+#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
@@ -88,6 +89,10 @@ private slots:
void sortTreeModel();
void sortTreeModelDynamic_data();
void sortTreeModelDynamic();
+ void editUsingEditTriggers_data();
+ void editUsingEditTriggers();
+ void editOnNonEditableCell_data();
+ void editOnNonEditableCell();
};
tst_qquicktreeview::tst_qquicktreeview()
@@ -1204,6 +1209,335 @@ void tst_qquicktreeview::sortTreeModelDynamic()
}
}
+void tst_qquicktreeview::editUsingEditTriggers_data()
+{
+ QTest::addColumn<QQuickTreeView::EditTriggers>("editTriggers");
+ QTest::addColumn<bool>("interactive");
+
+ QTest::newRow("NoEditTriggers") << QQuickTreeView::EditTriggers(QQuickTreeView::NoEditTriggers);
+ QTest::newRow("SingleTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::SingleTapped);
+ QTest::newRow("DoubleTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::DoubleTapped);
+ QTest::newRow("SelectedTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::SelectedTapped);
+ QTest::newRow("EditKeyPressed") << QQuickTreeView::EditTriggers(QQuickTreeView::EditKeyPressed);
+ QTest::newRow("AnyKeyPressed") << QQuickTreeView::EditTriggers(QQuickTreeView::AnyKeyPressed);
+ QTest::newRow("DoubleTapped | EditKeyPressed")
+ << QQuickTreeView::EditTriggers(QQuickTreeView::DoubleTapped | QQuickTreeView::EditKeyPressed);
+ QTest::newRow("SingleTapped | AnyKeyPressed")
+ << QQuickTreeView::EditTriggers(QQuickTreeView::SingleTapped | QQuickTreeView::AnyKeyPressed);
+}
+
+void tst_qquicktreeview::editUsingEditTriggers()
+{
+ // Check that you can start to edit in treeView
+ // using the available edit triggers.
+ QFETCH(QQuickTreeView::EditTriggers, editTriggers);
+ LOAD_TREEVIEW("editdelegate.qml");
+
+ TestModel testModel;
+ treeView->setModel(QVariant::fromValue(&testModel));
+ treeView->forceActiveFocus();
+ treeView->expand(0);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->editTriggers(), QQuickTreeView::EditKeyPressed);
+ treeView->setEditTriggers(editTriggers);
+
+ const char kEditItem[] = "editItem";
+ const char kEditIndex[] = "editIndex";
+
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+
+ const QPoint cell1(0, 0);
+ const QPoint cell2(1, 1);
+ const QModelIndex index1 = treeView->modelIndex(cell1);
+ const QModelIndex index2 = treeView->modelIndex(cell2);
+ const auto item1 = treeView->itemAtCell(cell1);
+ const auto item2 = treeView->itemAtCell(cell2);
+ QVERIFY(item1);
+ QVERIFY(item2);
+
+ testModel.m_editableIndices = { index1, index2 };
+
+ QQuickWindow *window = treeView->window();
+
+ const QPoint localPos = QPoint(item1->width() - 1, item1->height() - 1);
+ const QPoint localPosOutside = QPoint(treeView->contentWidth() + 10, treeView->contentHeight() + 10);
+ const QPoint tapPos1 = window->contentItem()->mapFromItem(item1, localPos).toPoint();
+ const QPoint tapPos2 = window->contentItem()->mapFromItem(item2, localPos).toPoint();
+ const QPoint tapOutsideContentItem = window->contentItem()->mapFromItem(item2, localPosOutside).toPoint();
+
+ if (editTriggers & QQuickTreeView::SingleTapped) {
+ // edit cell 1
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+ const auto editItem1 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem1);
+ QVERIFY(editItem1->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+
+ // edit cell 2 (without closing the previous edit session first)
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index2);
+ const auto editItem2 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem2);
+ QVERIFY(editItem2->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index2);
+
+ // single tap outside content item should close the editor
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapOutsideContentItem);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index2);
+ }
+
+ if (editTriggers & QQuickTreeView::DoubleTapped) {
+ // edit cell 1
+ QTest::mouseDClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+ const auto editItem1 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem1);
+ QVERIFY(editItem1->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+
+ // edit cell 2 (without closing the previous edit session first)
+ QTest::mouseDClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index2);
+ const auto editItem2 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem2);
+ QVERIFY(editItem2->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index2);
+
+ // single tap outside the edit item should close the editor
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+
+ if (!(editTriggers & QQuickTreeView::SingleTapped)) {
+ // single tap on a cell should not open the editor
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ // single tap outside content item should make sure editing ends
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapOutsideContentItem);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::SelectedTapped) {
+ // select cell first, then tap on it
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::Select);
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+ const auto editItem1 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem1);
+ QVERIFY(editItem1->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+
+ // tap on a non-selected cell. This should close the editor, and move
+ // the current index, but not begin to edit the cell.
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index2);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+
+ // tap on a non-selected cell while no editor is active
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index2);
+
+ // tap on the current cell. This alone should not start an edit (unless it's also selected)
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::EditKeyPressed) {
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_Return);
+ const auto editItem1 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem1);
+ QVERIFY(editItem1->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+
+ // Pressing escape should close the editor
+ QTest::keyClick(window, Qt::Key_Escape);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+
+ // Pressing Enter to open the editor again
+ QTest::keyClick(window, Qt::Key_Enter);
+ const auto editItem2 = treeView->property(kEditItem).value<QQuickItem *>();
+ QVERIFY(editItem2);
+ QVERIFY(editItem2->hasActiveFocus());
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+
+ // single tap outside the edit item should close the editor
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::AnyKeyPressed) {
+ // Pressing key x should start to edit. And in case of AnyKeyPressed, we
+ // also replay the key event to the focus object.
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_X);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+ auto textInput1 = treeView->property(kEditItem).value<QQuickTextInput *>();
+ QVERIFY(textInput1);
+ QVERIFY(textInput1->hasActiveFocus());
+ QCOMPARE(textInput1->text(), "x");
+
+ // Pressing escape should close the editor
+ QTest::keyClick(window, Qt::Key_Escape);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+
+ // Pressing a modifier key alone should not open the editor
+ QTest::keyClick(window, Qt::Key_Shift);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QTest::keyClick(window, Qt::Key_Control);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QTest::keyClick(window, Qt::Key_Alt);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QTest::keyClick(window, Qt::Key_Meta);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+
+ // Pressing enter should also start to edit. But this is a
+ // special case, we don't replay enter into the focus object.
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_Enter);
+ QCOMPARE(treeView->selectionModel()->currentIndex(), index1);
+ QCOMPARE(treeView->property(kEditIndex).value<QModelIndex>(), index1);
+ auto textInput2 = treeView->property(kEditItem).value<QQuickTextInput *>();
+ QVERIFY(textInput2);
+ QVERIFY(textInput2->hasActiveFocus());
+
+ if (!(editTriggers & QQuickTreeView::SingleTapped)) {
+ // single tap outside the edit item should close the editor
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos2);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ // single tap outside content item should make sure editing ends
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapOutsideContentItem);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers == QQuickTreeView::NoEditTriggers) {
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QTest::mouseDClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_Return);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QTest::keyClick(window, Qt::Key_Enter);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QTest::keyClick(window, Qt::Key_X);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+}
+
+void tst_qquicktreeview::editOnNonEditableCell_data()
+{
+ QTest::addColumn<QQuickTreeView::EditTriggers>("editTriggers");
+
+ QTest::newRow("SingleTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::SingleTapped);
+ QTest::newRow("DoubleTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::DoubleTapped);
+ QTest::newRow("SelectedTapped") << QQuickTreeView::EditTriggers(QQuickTreeView::SelectedTapped);
+ QTest::newRow("EditKeyPressed") << QQuickTreeView::EditTriggers(QQuickTreeView::EditKeyPressed);
+ QTest::newRow("AnyKeyPressed") << QQuickTreeView::EditTriggers(QQuickTreeView::EditKeyPressed);
+}
+
+void tst_qquicktreeview::editOnNonEditableCell()
+{
+ // Check that the user cannot edit a non-editable cell from the edit triggers.
+ // Note: we don't want TreeView to print out warnings in this case, since
+ // the user is not doing anything wrong. We only want to print out warnings if
+ // the application is calling edit() explicitly on a cell that cannot be edited
+ // (separate test below).
+ QFETCH(QQuickTreeView::EditTriggers, editTriggers);
+ LOAD_TREEVIEW("editdelegate.qml");
+
+ TestModel testModel;
+ treeView->setModel(QVariant::fromValue(&testModel));
+ treeView->forceActiveFocus();
+ treeView->expand(0);
+
+ WAIT_UNTIL_POLISHED;
+
+ const char kEditItem[] = "editItem";
+ const char kEditIndex[] = "editIndex";
+
+ const QPoint cell(1, 1);
+ const QModelIndex index1 = treeView->modelIndex(cell);
+ const auto item = treeView->itemAtCell(cell);
+ QVERIFY(item);
+
+ QQuickWindow *window = treeView->window();
+
+ const QPoint localPos = QPoint(item->width() - 1, item->height() - 1);
+ const QPoint tapPos = window->contentItem()->mapFromItem(item, localPos).toPoint();
+
+ if (editTriggers & QQuickTreeView::SingleTapped) {
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::DoubleTapped) {
+ QTest::mouseDClick(window, Qt::LeftButton, Qt::NoModifier, tapPos);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::SelectedTapped) {
+ // select cell first, then tap on it
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::EditKeyPressed) {
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_Enter);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QTest::keyClick(window, Qt::Key_Return);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+
+ if (editTriggers & QQuickTreeView::AnyKeyPressed) {
+ treeView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ QTest::keyClick(window, Qt::Key_X);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ QTest::keyClick(window, Qt::Key_Enter);
+ QVERIFY(!treeView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!treeView->property(kEditIndex).value<QModelIndex>().isValid());
+ }
+}
+
QTEST_MAIN(tst_qquicktreeview)
#include "tst_qquicktreeview.moc"
diff --git a/tests/auto/quickcontrols/accessibility/data/accessibleName/button.qml b/tests/auto/quickcontrols/accessibility/data/accessibleName/button.qml
new file mode 100644
index 0000000000..c985e72b78
--- /dev/null
+++ b/tests/auto/quickcontrols/accessibility/data/accessibleName/button.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Button {
+ text: "Hello world"
+ id: button
+}
diff --git a/tests/auto/quickcontrols/accessibility/data/accessibleName/button2.qml b/tests/auto/quickcontrols/accessibility/data/accessibleName/button2.qml
new file mode 100644
index 0000000000..5f1ceabea2
--- /dev/null
+++ b/tests/auto/quickcontrols/accessibility/data/accessibleName/button2.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Button {
+ text: "Th&is && that"
+ id: button
+}
diff --git a/tests/auto/quickcontrols/accessibility/data/accessibleName/button3.qml b/tests/auto/quickcontrols/accessibility/data/accessibleName/button3.qml
new file mode 100644
index 0000000000..d41d2ee1eb
--- /dev/null
+++ b/tests/auto/quickcontrols/accessibility/data/accessibleName/button3.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Button {
+ text: "Hello world"
+ Accessible.name: "Explicitly set accessible name"
+ id: button
+}
diff --git a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
index 40ee98691d..3aa6b63d06 100644
--- a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
+++ b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
@@ -31,6 +31,8 @@ private slots:
void override();
void ordering();
+
+ void accessibleName();
private:
QQmlEngine engine;
};
@@ -274,6 +276,45 @@ void tst_accessibility::ordering()
#endif
}
+void tst_accessibility::accessibleName()
+{
+#if QT_CONFIG(accessibility)
+ if (!QAccessible::isActive()) {
+ QPlatformAccessibility *accessibility = platformAccessibility();
+ if (!accessibility)
+ QSKIP("No QPlatformAccessibility available.");
+ accessibility->setActive(true);
+ }
+
+ QQmlComponent component(&engine);
+
+ // verify that accessible name matches the button text if none was set explicitly
+ component.loadUrl(testFileUrl("accessibleName/button.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
+ QAccessibleInterface *buttonAcc = QAccessible::queryAccessibleInterface(object.get());
+ QVERIFY(buttonAcc);
+ QCOMPARE(buttonAcc->text(QAccessible::Name), "Hello world");
+
+ // verify that ampersand ('&') for mnemonic and to escape literal ampersand in button
+ // text are not contained in accessible name
+ component.loadUrl(testFileUrl("accessibleName/button2.qml"));
+ QScopedPointer<QObject> object2(component.create());
+ QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
+ QAccessibleInterface *button2Acc = QAccessible::queryAccessibleInterface(object2.get());
+ QVERIFY(button2Acc);
+ QCOMPARE(button2Acc->text(QAccessible::Name), "This & that");
+
+ // verify that explicitly set accesible name is used
+ component.loadUrl(testFileUrl("accessibleName/button3.qml"));
+ QScopedPointer<QObject> object3(component.create());
+ QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
+ QAccessibleInterface *button3Acc = QAccessible::queryAccessibleInterface(object3.get());
+ QVERIFY(button3Acc);
+ QCOMPARE(button3Acc->text(QAccessible::Name), "Explicitly set accessible name");
+#endif
+}
+
QTEST_MAIN(tst_accessibility)
#include "tst_accessibility.moc"
diff --git a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
index 74bbf2954e..bf61a4fc7d 100644
--- a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_basic LANGUAGES C CXX ASM)
+ project(tst_basic LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
index e3d38e1cf3..95c6d9f540 100644
--- a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_fusion LANGUAGES C CXX ASM)
+ project(tst_fusion LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
index c296a5b1a4..67560bf225 100644
--- a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_imagine LANGUAGES C CXX ASM)
+ project(tst_imagine LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
index 4e0cb72e68..55c16a2735 100644
--- a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_ios LANGUAGES C CXX ASM)
+ project(tst_ios LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
index 3202601316..f33ef26c78 100644
--- a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_macos LANGUAGES C CXX ASM)
+ project(tst_macos LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/material/CMakeLists.txt b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
index 2f14b5253d..f59a1a09e8 100644
--- a/tests/auto/quickcontrols/controls/material/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_material LANGUAGES C CXX ASM)
+ project(tst_material LANGUAGES C CXX)
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
index f39975ab15..2ac791ccb9 100644
--- a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_universal LANGUAGES C CXX ASM)
+ project(tst_universal LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
index 1d6ab1fc57..f1c1a4d1d7 100644
--- a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_windows LANGUAGES C CXX ASM)
+ project(tst_windows LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/cursor/CMakeLists.txt b/tests/auto/quickcontrols/cursor/CMakeLists.txt
index 6d0fa9eed6..85f39ba90a 100644
--- a/tests/auto/quickcontrols/cursor/CMakeLists.txt
+++ b/tests/auto/quickcontrols/cursor/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_cursor LANGUAGES C CXX ASM)
+ project(tst_cursor LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/customization/CMakeLists.txt b/tests/auto/quickcontrols/customization/CMakeLists.txt
index 79dd408257..cca6dfe5a5 100644
--- a/tests/auto/quickcontrols/customization/CMakeLists.txt
+++ b/tests/auto/quickcontrols/customization/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_customization LANGUAGES C CXX ASM)
+ project(tst_customization LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/designer/CMakeLists.txt b/tests/auto/quickcontrols/designer/CMakeLists.txt
index 9ad0512f80..3457961f54 100644
--- a/tests/auto/quickcontrols/designer/CMakeLists.txt
+++ b/tests/auto/quickcontrols/designer/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_designer LANGUAGES C CXX ASM)
+ project(tst_designer LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/focus/CMakeLists.txt b/tests/auto/quickcontrols/focus/CMakeLists.txt
index 87200bf05c..9db0b48bd6 100644
--- a/tests/auto/quickcontrols/focus/CMakeLists.txt
+++ b/tests/auto/quickcontrols/focus/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_focus LANGUAGES C CXX ASM)
+ project(tst_focus LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/font/CMakeLists.txt b/tests/auto/quickcontrols/font/CMakeLists.txt
index 9eaee341d0..4266cfc756 100644
--- a/tests/auto/quickcontrols/font/CMakeLists.txt
+++ b/tests/auto/quickcontrols/font/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_font LANGUAGES C CXX ASM)
+ project(tst_font LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/palette/CMakeLists.txt b/tests/auto/quickcontrols/palette/CMakeLists.txt
index fd4ee2d680..747ea76226 100644
--- a/tests/auto/quickcontrols/palette/CMakeLists.txt
+++ b/tests/auto/quickcontrols/palette/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_palette LANGUAGES C CXX ASM)
+ project(tst_palette LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/platform/CMakeLists.txt b/tests/auto/quickcontrols/platform/CMakeLists.txt
index a2f77fdd9c..5f6d64491f 100644
--- a/tests/auto/quickcontrols/platform/CMakeLists.txt
+++ b/tests/auto/quickcontrols/platform/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_platform LANGUAGES C CXX ASM)
+ project(tst_platform LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt b/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
index 8bc7b26b31..cf73a05132 100644
--- a/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
+++ b/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_pointerhandlers LANGUAGES C CXX ASM)
+ project(tst_pointerhandlers LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/pressandhold/CMakeLists.txt b/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
index 8d8c967a56..9f4a889916 100644
--- a/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
+++ b/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_pressandhold LANGUAGES C CXX ASM)
+ project(tst_pressandhold LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt b/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
index a24d019827..4e40383d9c 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickapplicationwindow LANGUAGES C CXX ASM)
+ project(tst_qquickapplicationwindow LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
index a02ab9c595..8ef7122d29 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
@@ -891,7 +891,7 @@ void tst_QQuickApplicationWindow::layoutLayout()
QCOMPARE(headerChild->width(), qreal(window->width()));
QVERIFY(header->height() > 0);
- QQuickItem *footerChild = header->findChild<QQuickItem*>();
+ QQuickItem *footerChild = footer->findChild<QQuickItem*>();
QVERIFY(footerChild);
QCOMPARE(footer->x(), 0.0);
QCOMPARE(footer->y(), content->height());
diff --git a/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt b/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
index efd06f15ff..4bbed44111 100644
--- a/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickcolor LANGUAGES C CXX ASM)
+ project(tst_qquickcolor LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt b/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt
index c79e508cd1..c04b31a415 100644
--- a/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickcontainer/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickcontainer LANGUAGES C CXX ASM)
+ project(tst_qquickcontainer LANGUAGES C CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml b/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml
new file mode 100644
index 0000000000..9074904026
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQml.Models
+
+Window {
+ width: 640
+ height: 480
+
+ property alias navigationBar: navigationbar
+
+ component NavigationBar: Container {
+ id: container
+
+ property alias model: repeater.model
+ property alias listView: list
+
+ implicitHeight: 20
+
+ contentItem: Item {
+ width: parent.width
+ height: parent.height
+
+ ListView {
+ id: list
+ model: container.contentModel
+ snapMode: ListView.SnapToItem
+ orientation: ListView.Horizontal
+ interactive: width < contentWidth
+ clip: interactive
+ spacing: 20
+
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ left: parent.left
+ }
+ }
+ }
+
+ Repeater {
+ id: repeater
+ delegate: Text {
+ text: modelData
+ }
+ }
+ }
+
+ NavigationBar {
+ id: navigationbar
+ width: 100
+ model: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp
index ce95f2cc37..b61717e4c9 100644
--- a/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp
+++ b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp
@@ -8,6 +8,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
using namespace QQuickVisualTestUtils;
using namespace QQuickControlsTestUtils;
@@ -22,6 +24,7 @@ public:
private slots:
void zeroSize_data();
void zeroSize();
+ void skipReorderContentModelItem();
};
tst_qquickcontainer::tst_qquickcontainer()
@@ -102,6 +105,34 @@ void tst_qquickcontainer::zeroSize()
QCOMPARE(QQuickItemPrivate::get(text4)->culled, isItemView);
}
+void tst_qquickcontainer::skipReorderContentModelItem()
+{
+ QQuickControlsApplicationHelper helper(this, "skipReorderContentModelItem.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ centerOnScreen(helper.window);
+ helper.window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(helper.window));
+
+ const auto *container = helper.window->property("navigationBar").value<QQuickContainer *>();
+ QVERIFY(container);
+ const auto *listView = container->property("listView").value<QQuickListView *>();
+ QVERIFY(listView);
+ const auto *contentModel = container->property("contentModel").value<QQmlObjectModel *>();
+ QVERIFY(contentModel);
+
+ QCOMPARE(listView->count(), contentModel->count());
+
+ int totalWidth = listView->spacing() * (contentModel->count() - 1);
+ for (int index = 0; index < contentModel->count(); index++)
+ totalWidth += qobject_cast<QQuickText *>(listView->itemAtIndex(index))->width();
+ QVERIFY(totalWidth > listView->width());
+
+ for (int index = 0; index < listView->count(); index++) {
+ const auto *textItem = qobject_cast<QQuickText *>(listView->itemAtIndex(index));
+ QCOMPARE(textItem->text().toInt(), index + 1);
+ }
+}
+
QTEST_MAIN(tst_qquickcontainer)
#include "tst_qquickcontainer.moc"
diff --git a/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt b/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
index a43b14f1fa..51686657ca 100644
--- a/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickcontrol LANGUAGES C CXX ASM)
+ project(tst_qquickcontrol LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt b/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
index 2515a8d30e..bb78889d4f 100644
--- a/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickdrawer LANGUAGES C CXX ASM)
+ project(tst_qquickdrawer LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt b/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
index 1925c85dc8..2f0c1c2285 100644
--- a/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickheaderview LANGUAGES C CXX ASM)
+ project(tst_qquickheaderview LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt b/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
index 58f8a8f6f5..0620eaa28e 100644
--- a/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickiconimage LANGUAGES C CXX ASM)
+ project(tst_qquickiconimage LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt b/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
index 48ede72c13..63ee07ca02 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickiconlabel LANGUAGES C CXX ASM)
+ project(tst_qquickiconlabel LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickiconlabel/data/childPaintOrder.qml b/tests/auto/quickcontrols/qquickiconlabel/data/childPaintOrder.qml
new file mode 100644
index 0000000000..b26d36fac6
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickiconlabel/data/childPaintOrder.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+
+AbstractButton {
+ id: control
+ width: 200
+ height: 200
+ text: "Button"
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png"
+ icon.color: enabled ? "transparent" : "red"
+
+ contentItem: IconLabel {
+ id: iconLabel
+ objectName: "iconLabel"
+ display: control.display
+ icon: control.icon
+ text: control.text
+ font: control.font
+
+ Text {
+ objectName: "childText"
+ color: "red"
+ anchors.centerIn: parent
+ text: "Should be in front"
+ font.bold: true
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp b/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
index 8d42cf5faf..cc11acee1d 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
+++ b/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
@@ -12,13 +12,16 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickimage_p_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickicon_p.h>
#include <QtQuickControls2Impl/private/qquickiconimage_p.h>
#include <QtQuickControls2Impl/private/qquickiconlabel_p.h>
#include <QtQuickControls2Impl/private/qquickiconlabel_p_p.h>
+#include <QtQuickControls2Impl/private/qquickmnemoniclabel_p.h>
+using namespace QQuickViewTestUtils;
using namespace QQuickVisualTestUtils;
class tst_qquickiconlabel : public QQmlDataTest
@@ -35,6 +38,7 @@ private slots:
void emptyIconSource();
void colorChanges();
void iconSourceContext();
+ void childPaintOrder();
};
tst_qquickiconlabel::tst_qquickiconlabel()
@@ -340,6 +344,22 @@ void tst_qquickiconlabel::iconSourceContext()
}
+// QTBUG-133924: check that items declared as children of us are rendered on top of the label and icon image.
+void tst_qquickiconlabel::childPaintOrder()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("childPaintOrder.qml")));
+
+ auto *iconLabel = window.findChild<QQuickItem *>("iconLabel");
+ QVERIFY(iconLabel);
+ auto *iconLabelPrivate = static_cast<QQuickIconLabelPrivate *>(QQuickItemPrivate::get(iconLabel));
+
+ auto *childText = window.findChild<QQuickItem *>("childText");
+ QVERIFY(childText);
+ const QList<QQuickItem *> expectedPaintOrder = { iconLabelPrivate->label, iconLabelPrivate->image, childText };
+ QCOMPARE(iconLabelPrivate->paintOrderChildItems(), expectedPaintOrder);
+}
+
QTEST_MAIN(tst_qquickiconlabel)
#include "tst_qquickiconlabel.moc"
diff --git a/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
index 1985784386..d1b8f8b16e 100644
--- a/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickimaginestyle LANGUAGES C CXX ASM)
+ project(tst_qquickimaginestyle LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
index 0afeaf2fb3..a74a9d1a54 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickmaterialstyle LANGUAGES C CXX ASM)
+ project(tst_qquickmaterialstyle LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
index 69a2d95ab3..862aa15488 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
@@ -1325,4 +1325,33 @@ TestCase {
tryCompare(busyIndicator.contentItem, "visible", true)
}
+
+ Component {
+ id: labelInPopupComponent
+
+ Popup {
+ id: popupInstance
+
+ property alias label: label
+
+ Material.theme: Material.Dark
+
+ Label {
+ id: label
+ text: "Should have a dark theme"
+ }
+ }
+ }
+
+ function test_popupPropagatesToChildItem() {
+ let popup = createTemporaryObject(labelInPopupComponent, testCase)
+ verify(popup)
+
+ compare(popup.Material.theme, Material.Dark)
+ compare(popup.label.Material.theme, Material.Dark)
+
+ popup.Material.theme = Material.Light
+ compare(popup.Material.theme, Material.Light)
+ compare(popup.label.Material.theme, Material.Light)
+ }
}
diff --git a/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt b/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
index 36c518aa9c..56364c5a63 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickmaterialstyleconf LANGUAGES C CXX ASM)
+ project(tst_qquickmaterialstyleconf LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt b/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
index 0277c88446..4b4e89d545 100644
--- a/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickmenu LANGUAGES C CXX ASM)
+ project(tst_qquickmenu LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickmenu/data/animationOnHeight.qml b/tests/auto/quickcontrols/qquickmenu/data/animationOnHeight.qml
new file mode 100644
index 0000000000..e01e764efd
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenu/data/animationOnHeight.qml
@@ -0,0 +1,70 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Templates as T
+
+ApplicationWindow {
+ width: 600
+ height: 400
+
+ Action {
+ id: copyAction
+ text: "Copy"
+ }
+
+ // Based on FluentWinUI3's Menu.
+ T.Menu {
+ id: menu
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ delegate: MenuItem { }
+
+ contentItem: ListView {
+ implicitHeight: contentHeight
+ model: menu.contentModel
+ interactive: Window.window
+ ? contentHeight + menu.topPadding + menu.bottomPadding > menu.height
+ : false
+ currentIndex: menu.currentIndex
+ spacing: 4
+ clip: true
+
+ ScrollIndicator.vertical: ScrollIndicator {}
+ }
+
+ enter: Transition {
+ NumberAnimation {
+ property: "height"
+ from: menu.implicitHeight * 0.33
+ to: menu.implicitHeight
+ easing.type: Easing.OutCubic
+ duration: 1
+ }
+ }
+
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 30
+ border.width: 1
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: "transparent"
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: "transparent"
+ }
+
+ Action {
+ text: "Cut"
+ }
+ }
+
+ Component.onCompleted: {
+ menu.addAction(copyAction)
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml b/tests/auto/quickcontrols/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml
index bfa8f66be5..ebd0ab26b3 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml
@@ -9,7 +9,6 @@ ApplicationWindow {
Menu {
id: menu
- visible: true
contentItem: FocusScope {
implicitHeight: flickable.height
diff --git a/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml
index 1156d30aef..9299edb77d 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml
@@ -15,7 +15,7 @@ Window {
id: menu
anchors.centerIn: parent
height: 100
- visible: true
+
Repeater {
model: 10
delegate: MenuItem {
diff --git a/tests/auto/quickcontrols/qquickmenu/data/shortcutInNestedSubMenuAction.qml b/tests/auto/quickcontrols/qquickmenu/data/shortcutInNestedSubMenuAction.qml
new file mode 100644
index 0000000000..02cb7d15c4
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenu/data/shortcutInNestedSubMenuAction.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: root
+ width: 400
+ height: 400
+
+ property int triggeredCount
+
+ Menu {
+ objectName: "topLevelMenu"
+
+ Menu {
+ objectName: "subMenu"
+
+ Menu {
+ objectName: "subSubMenu"
+
+ Action {
+ shortcut: StandardKey.Copy
+ onTriggered: ++root.triggeredCount
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
index 041c3d7ab9..578d5e84b4 100644
--- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
@@ -15,6 +15,7 @@
#include <QtQml/qqmlcontext.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuickTest/quicktest.h>
@@ -95,6 +96,8 @@ private slots:
void customMenuUseRepeaterAsTheContentItem();
void invalidUrlInImgTag();
void mousePropagationWithinPopup();
+ void shortcutInNestedSubMenuAction();
+ void animationOnHeight();
};
tst_QQuickMenu::tst_QQuickMenu()
@@ -2365,6 +2368,41 @@ void tst_QQuickMenu::mousePropagationWithinPopup()
}
}
+void tst_QQuickMenu::shortcutInNestedSubMenuAction()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("shortcutInNestedSubMenuAction.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ // Shouldn't result in an infinite loop.
+ QTest::keyClick(window, Qt::Key_C, Qt::ControlModifier);
+ QCOMPARE(window->property("triggeredCount").value<int>(), 1);
+}
+
+void tst_QQuickMenu::animationOnHeight()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("animationOnHeight.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *menu = window->findChild<QQuickMenu *>();
+ QVERIFY(menu);
+ menu->popup(QPointF(100, 100));
+ QTRY_VERIFY(menu->isOpened());
+
+ // After adding the Action, the contentHeight of the Menu's ListView should
+ // be not be greater than its height; it shouldn't be possible to scroll.
+ auto *listView = qobject_cast<QQuickListView *>(menu->contentItem());
+ const qreal itemHeight = menu->itemAt(0)->height();
+ QCOMPARE(listView->contentHeight(), itemHeight * 2 + listView->spacing());
+ QCOMPARE_GE(listView->height(), listView->contentHeight());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu)
#include "tst_qquickmenu.moc"
diff --git a/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt b/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
index cee75be2f9..0796160031 100644
--- a/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickmenubar LANGUAGES C CXX ASM)
+ project(tst_qquickmenubar LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/clearMenus.qml b/tests/auto/quickcontrols/qquickmenubar/data/clearMenus.qml
new file mode 100644
index 0000000000..6844902fb6
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenubar/data/clearMenus.qml
@@ -0,0 +1,35 @@
+import QtQuick
+import QtQuick.Templates
+
+Item {
+ property int v: 0
+
+ MenuBar {
+ id: menuBar
+ Menu {}
+ }
+
+ Instantiator {
+ id: instantiator
+
+ delegate: Menu {}
+
+ onObjectAdded: (index, object) => {
+ menuBar.menus.push(object)
+ }
+
+ onObjectRemoved: (index, object) => {
+ menuBar.menus.length = 0;
+ }
+ }
+
+ Timer {
+ running: v < 2
+ interval: 1
+ repeat: true
+
+ onTriggered: {
+ instantiator.model = (++v % 2) ? 0 : 2
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
index ee593e1701..7cb842d5a7 100644
--- a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
@@ -36,6 +36,7 @@ private slots:
void altNavigation();
void addRemove();
void checkHighlightWhenMenuDismissed();
+ void clearMenus();
private:
QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -771,6 +772,16 @@ void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
QVERIFY(!dynamicMenuBarItem->isHighlighted());
}
+void tst_qquickmenubar::clearMenus()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("clearMenus.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("v").toInt(), 2);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_qquickmenubar)
#include "tst_qquickmenubar.moc"
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt b/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
index e8a4e733b8..43ac4fca05 100644
--- a/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickninepatchimage LANGUAGES C CXX ASM)
+ project(tst_qquickninepatchimage LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt b/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt
index e5d9da4dc9..969604455c 100644
--- a/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickpopup LANGUAGES C CXX ASM)
+ project(tst_qquickpopup LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt b/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
index 81eb78c906..a0f9d12503 100644
--- a/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickpopup LANGUAGES C CXX ASM)
+ project(tst_qquickpopup LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickpopup/data/propagateTouchEvents.qml b/tests/auto/quickcontrols/qquickpopup/data/propagateTouchEvents.qml
new file mode 100644
index 0000000000..01c7fe39de
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/propagateTouchEvents.qml
@@ -0,0 +1,17 @@
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ id: rootItem
+ width: 100
+ height: 100
+
+ property alias tapHandler: tHandler
+
+ Popup {
+ id: popupItem
+ width: 50
+ height: 50
+ background: Item { TapHandler { id: tHandler } }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index 966f908e80..6dc260faea 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -30,6 +30,7 @@
#include <QtQuickTemplates2/private/qquickdrawer_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquicktextedit_p.h>
+#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuick/private/qquickdroparea_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
@@ -105,6 +106,7 @@ private slots:
void doubleClickInMouseArea();
void pointerEventsNotBlockedForNonPopupChildrenOfOverlayWithHigherZ();
void resetHoveredStateForItemsWithinPopup();
+ void propagateTouchEvents();
private:
QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -2364,6 +2366,14 @@ void tst_QQuickPopup::pointerEventsNotBlockedForNonPopupChildrenOfOverlayWithHig
QCOMPARE(lowerMouseAreaSpy.count(), 0);
QCOMPARE(upperMouseAreaSpy.count(), 1);
+ // Verify that the upper mouse area is hovered, even when a mouse press happens on top of a modal dialog
+ upperMouseArea->setHoverEnabled(true);
+ QTest::mouseMove(window, button->mapToScene(button->boundingRect().center() - QPoint(1,1)).toPoint());
+ QVERIFY(!upperMouseArea->hovered());
+ upperMouseArea->setEnabled(true);
+ QTest::mouseMove(window, button->mapToScene(button->boundingRect().center() + QPoint(1,1)).toPoint());
+ QVERIFY(upperMouseArea->hovered());
+
popup->close();
}
@@ -2401,6 +2411,34 @@ void tst_QQuickPopup::resetHoveredStateForItemsWithinPopup()
QTRY_VERIFY(!controlItem->isHovered());
}
+void tst_QQuickPopup::propagateTouchEvents()
+{
+ QQuickApplicationHelper helper(this, "propagateTouchEvents.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ popup->open();
+ QTRY_VERIFY(popup->isOpened());
+
+ auto *tapHandler = window->property("tapHandler").value<QQuickTapHandler *>();
+ QVERIFY(tapHandler);
+ QSignalSpy tapSpy(tapHandler, &QQuickTapHandler::tapped);
+ // check for the item within the popup for touch events
+ QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(10, 10));
+ QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(10, 10));
+ QCOMPARE(tapSpy.count(), 1);
+
+ // touch outside to close the popup window
+ QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(90, 90));
+ QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(90, 90));
+ QCOMPARE(tapSpy.count(), 1);
+
+ QTRY_VERIFY(!popup->isOpened());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
index 4255850e30..525f56bdea 100644
--- a/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickstyle LANGUAGES C CXX ASM)
+ project(tst_qquickstyle LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt b/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
index f64b896e5c..01dcd5b942 100644
--- a/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquicktextarea LANGUAGES C CXX ASM)
+ project(tst_qquicktextarea LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt b/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
index 50755b7f1c..7f4cac7f34 100644
--- a/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquicktextfield LANGUAGES C CXX ASM)
+ project(tst_qquicktextfield LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt b/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
index 94d4096586..063a90a358 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquicktreeviewdelegate LANGUAGES C CXX ASM)
+ project(tst_qquicktreeviewdelegate LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
index a7f8ebfd6f..2122fac1db 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickuniversalstyle LANGUAGES C CXX ASM)
+ project(tst_qquickuniversalstyle LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt b/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
index cfe781950d..86d100b1be 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_qquickuniversalstyleconf LANGUAGES C CXX ASM)
+ project(tst_qquickuniversalstyleconf LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/revisions/CMakeLists.txt b/tests/auto/quickcontrols/revisions/CMakeLists.txt
index 8c51f85d20..46f0734f68 100644
--- a/tests/auto/quickcontrols/revisions/CMakeLists.txt
+++ b/tests/auto/quickcontrols/revisions/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_revisions LANGUAGES C CXX ASM)
+ project(tst_revisions LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/sanity/CMakeLists.txt b/tests/auto/quickcontrols/sanity/CMakeLists.txt
index 6c99b5b7c7..e301e72d44 100644
--- a/tests/auto/quickcontrols/sanity/CMakeLists.txt
+++ b/tests/auto/quickcontrols/sanity/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_sanity LANGUAGES C CXX ASM)
+ project(tst_sanity LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/snippets/CMakeLists.txt b/tests/auto/quickcontrols/snippets/CMakeLists.txt
index cfb2fb4560..08b55c2645 100644
--- a/tests/auto/quickcontrols/snippets/CMakeLists.txt
+++ b/tests/auto/quickcontrols/snippets/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_snippets LANGUAGES C CXX ASM)
+ project(tst_snippets LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/styleimports/CMakeLists.txt b/tests/auto/quickcontrols/styleimports/CMakeLists.txt
index 4d19521f2e..e2f4e8d7a9 100644
--- a/tests/auto/quickcontrols/styleimports/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimports/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_styleimports LANGUAGES C CXX ASM)
+ project(tst_styleimports LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt b/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
index 6e7f22da64..adec66b532 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_styleimportscompiletimematerial LANGUAGES C CXX ASM)
+ project(tst_styleimportscompiletimematerial LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
index 6e56e6a96e..ddb9a64413 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_styleimportscompiletimeqmlonly LANGUAGES C CXX ASM)
+ project(tst_styleimportscompiletimeqmlonly LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickcontrols/translation/CMakeLists.txt b/tests/auto/quickcontrols/translation/CMakeLists.txt
index 745262cdcc..228fdf25e2 100644
--- a/tests/auto/quickcontrols/translation/CMakeLists.txt
+++ b/tests/auto/quickcontrols/translation/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_translation LANGUAGES C CXX ASM)
+ project(tst_translation LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index a42ea91b6d..6b81b9abbb 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -104,9 +104,13 @@ private:
QStringList tempSubDirExpectedVisibleFiles(DelegateOrderPolicy order) const;
QTemporaryDir tempDir;
+ QString tempDirCanonicalPath;
QScopedPointer<QFile> tempFile1;
+ QString tempFile1CanonicalPath;
QScopedPointer<QFile> tempFile2;
+ QString tempFile2CanonicalPath;
QDir tempSubDir;
+ QString tempSubDirCanonicalPath;
QDir tempSubSubDir;
QScopedPointer<QFile> tempSubFile1;
QScopedPointer<QFile> tempSubFile2;
@@ -114,6 +118,7 @@ private:
QTemporaryDir largeTempDir;
QStringList largeTempDirPaths;
QDir largeTempDirLargeSubDir;
+ QString largeTempDirLargeSubDirCanonicalPath;
const int largeTempDirLargeSubDirIndex = 80;
QDir oldCurrentDir;
@@ -125,8 +130,8 @@ private:
QStringList tst_QQuickFileDialogImpl::tempDirExpectedVisibleFiles(DelegateOrderPolicy order) const
{
return order == ShowDirectoriesFirst
- ? QStringList { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }
- : QStringList { tempFile1->fileName(), tempFile2->fileName(), tempSubDir.path() };
+ ? QStringList { tempSubDirCanonicalPath, tempFile1CanonicalPath, tempFile2CanonicalPath }
+ : QStringList { tempFile1CanonicalPath, tempFile2CanonicalPath, tempSubDirCanonicalPath };
}
QStringList tst_QQuickFileDialogImpl::tempSubDirExpectedVisibleFiles(DelegateOrderPolicy order) const
@@ -150,12 +155,17 @@ void tst_QQuickFileDialogImpl::initTestCase()
qputenv("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE", "1");
+ // Ensure that each test starts off in the temporary directory.
+ oldCurrentDir = QDir::current();
+ QDir::setCurrent(tempDir.path());
+
QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+ tempDirCanonicalPath = QFileInfo(tempDir.path()).canonicalFilePath();
// QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
// and then calls qRun() for each style, and qRun() calls initTestCase().
// So, we need to check if we've already made the temporary directory.
// Note that this is only necessary if the test is run with more than one style.
- if (!QDir(tempDir.path()).isEmpty())
+ if (!QDir(tempDirCanonicalPath).isEmpty())
return;
/*
@@ -169,29 +179,35 @@ void tst_QQuickFileDialogImpl::initTestCase()
├── file1.txt (tempFile1)
└── file2.txt (tempFile2)
*/
- tempSubDir = QDir(tempDir.path());
+ tempSubDir = QDir(tempDirCanonicalPath);
QVERIFY2(tempSubDir.mkdir("sub-dir"), qPrintable(QString::fromLatin1(
"Failed to make sub-directory \"sub-dir\" in %1. Permissions are: %2")
- .arg(tempDir.path()).arg(QDebug::toString(QFileInfo(tempDir.path()).permissions()))));
+ .arg(tempDirCanonicalPath).arg(QDebug::toString(QFileInfo(tempDirCanonicalPath).permissions()))));
QVERIFY(tempSubDir.cd("sub-dir"));
+ tempSubDirCanonicalPath = tempSubDir.canonicalPath();
+ QVERIFY(!tempSubDirCanonicalPath.isEmpty());
- tempSubSubDir = QDir(tempSubDir.path());
+ tempSubSubDir = QDir(tempSubDirCanonicalPath);
QVERIFY2(tempSubSubDir.mkdir("sub-sub-dir"), qPrintable(QString::fromLatin1(
"Failed to make sub-directory \"sub-sub-dir\" in %1. Permissions are: %2")
- .arg(tempSubDir.path()).arg(QDebug::toString(QFileInfo(tempSubDir.path()).permissions()))));
+ .arg(tempSubDirCanonicalPath).arg(QDebug::toString(QFileInfo(tempSubDirCanonicalPath).permissions()))));
QVERIFY(tempSubSubDir.cd("sub-sub-dir"));
- tempSubFile1.reset(new QFile(tempSubDir.path() + "/sub-file1.txt"));
+ tempSubFile1.reset(new QFile(tempSubDirCanonicalPath + "/sub-file1.txt"));
QVERIFY(tempSubFile1->open(QIODevice::ReadWrite));
- tempSubFile2.reset(new QFile(tempSubDir.path() + "/sub-file2.txt"));
+ tempSubFile2.reset(new QFile(tempSubDirCanonicalPath + "/sub-file2.txt"));
QVERIFY(tempSubFile2->open(QIODevice::ReadWrite));
- tempFile1.reset(new QFile(tempDir.path() + "/file1.txt"));
+ tempFile1.reset(new QFile(tempDirCanonicalPath + "/file1.txt"));
QVERIFY(tempFile1->open(QIODevice::ReadWrite));
+ tempFile1CanonicalPath = QFileInfo(tempFile1->fileName()).canonicalFilePath();
+ QVERIFY(!tempFile1CanonicalPath.isEmpty());
- tempFile2.reset(new QFile(tempDir.path() + "/file2.txt"));
+ tempFile2.reset(new QFile(tempDirCanonicalPath + "/file2.txt"));
QVERIFY(tempFile2->open(QIODevice::ReadWrite));
+ tempFile2CanonicalPath = QFileInfo(tempFile2->fileName()).canonicalFilePath();
+ QVERIFY(!tempFile1CanonicalPath.isEmpty());
/*
Create another temporary directory that contains a large amount of folders.
@@ -199,11 +215,12 @@ void tst_QQuickFileDialogImpl::initTestCase()
QVERIFY2(largeTempDir.isValid(), qPrintable(largeTempDir.errorString()));
const static int largeFileCount = 100;
const QDir largeTempDirectory(largeTempDir.path());
+ const QString largeTempDirectoryCanonicalPath = largeTempDirectory.canonicalPath();
for (int i = 0; i < largeFileCount; ++i) {
// Pad with zeroes so that the directories are ordered as we expect.
const QString dirName = QString::fromLatin1("dir%1").arg(i, 3, 10, QLatin1Char('0'));
QVERIFY(largeTempDirectory.mkdir(dirName));
- largeTempDirPaths.append(largeTempDirectory.filePath(dirName));
+ largeTempDirPaths.append(largeTempDirectoryCanonicalPath + '/' + dirName);
}
// ... and within one of those folders, more folders.
@@ -213,10 +230,7 @@ void tst_QQuickFileDialogImpl::initTestCase()
const QDir largeTempSubDirectory = QDir(largeTempDirLargeSubDir.path());
for (int i = 0; i < largeFileCount; ++i)
QVERIFY(largeTempSubDirectory.mkdir(QString::fromLatin1("sub-dir%1").arg(i, 3, 10, QLatin1Char('0'))));
-
- // Ensure that each test starts off in the temporary directory.
- oldCurrentDir = QDir::current();
- QDir::setCurrent(tempDir.path());
+ largeTempDirLargeSubDirCanonicalPath = largeTempDirLargeSubDir.canonicalPath();
}
void tst_QQuickFileDialogImpl::init()
@@ -322,7 +336,8 @@ void tst_QQuickFileDialogImpl::defaults()
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
QQuickFileDialogImpl *quickDialog = dialogHelper.window()->findChild<QQuickFileDialogImpl*>();
QVERIFY(quickDialog);
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(QDir().absolutePath()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(QDir().absolutePath()),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
QCOMPARE(quickDialog->title(), QString());
}
@@ -340,9 +355,10 @@ void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
QQuickFileDialogDelegate *delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, delegate));
- COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2CanonicalPath));
QVERIFY(clickButton(delegate));
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempFile2->fileName()), 2);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempFile2CanonicalPath), 2);
QCOMPARE(dialogSelectedFileChangedSpy.size(), 1);
QCOMPARE(dialogCurrentFileChangedSpy.size(), 1);
@@ -353,9 +369,9 @@ void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
QVERIFY(openButton);
QVERIFY(clickButton(openButton));
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
- COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) });
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
QCOMPARE(dialogSelectedFileChangedSpy.size(), 1);
QCOMPARE(dialogCurrentFileChangedSpy.size(), 1);
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
@@ -371,12 +387,12 @@ void tst_QQuickFileDialogImpl::chooseFileViaDoubleClick()
// Select the delegate by double-clicking.
QQuickFileDialogDelegate *delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, delegate));
- COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()))
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2CanonicalPath))
QVERIFY(doubleClickButton(delegate));
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()))
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) })
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()))
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) })
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2CanonicalPath))
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) })
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath))
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) })
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
}
@@ -390,7 +406,8 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
// on the current item when we set it in setFileDialogListViewCurrentIndex(),
// which can make the TextField's visibility check
// below fail due to it being hidden when it loses activeFocus.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -401,14 +418,14 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
// Enter the path to the file in the text edit.
- enterText(dialogHelper.window(), tempFile2->fileName());
- QCOMPARE(breadcrumbBar->textField()->text(), tempFile2->fileName());
+ enterText(dialogHelper.window(), tempFile2CanonicalPath);
+ QCOMPARE(breadcrumbBar->textField()->text(), tempFile2CanonicalPath);
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) });
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
}
@@ -420,16 +437,17 @@ void tst_QQuickFileDialogImpl::chooseFileViaEnter()
OPEN_QUICK_DIALOG();
// Before moving down, the first delegate in the view should be selected and have focus.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Select the first file in the view by navigating with the down key.
QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1CanonicalPath));
// Select the delegate by pressing enter.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile1->fileName()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) });
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile1CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile1CanonicalPath) });
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
QCOMPARE(dialogHelper.dialog->result(), QQuickFileDialog::Accepted);
@@ -441,8 +459,8 @@ void tst_QQuickFileDialogImpl::bindCurrentFolder_data()
QTest::addColumn<QUrl>("expectedFolder");
QTest::addColumn<QStringList>("expectedVisibleFiles");
- const auto currentDirUrl = QUrl::fromLocalFile(QDir::current().path());
- const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path());
+ const auto currentDirUrl = QUrl::fromLocalFile(QDir::current().canonicalPath());
+ const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDirCanonicalPath);
const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
const QStringList currentDirFiles = tempDirExpectedVisibleFiles(ShowDirectoriesFirst);
@@ -470,7 +488,8 @@ void tst_QQuickFileDialogImpl::bindCurrentFolder()
// Even waiting for ListView polish and that the FolderListModel's status is ready aren't enough
// on Windows, apparently, as sometimes there just aren't any delegates by the time we do the check.
// So, we use QTRY_VERIFY2 each time we call this function just to be safe.
- QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage),
+ qPrintable(failureMessage));
// Check that the breadcrumb bar is correct by constructing the expected files from the expectedFolder.
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
@@ -487,10 +506,11 @@ void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
// Select the delegate by clicking once.
QQuickFileDialogDelegate *delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, delegate));
- COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDirCanonicalPath));
QVERIFY(clickButton(delegate));
// The selectedFile should change, but not currentFolder.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Click the "Open" button. The dialog should navigate to that directory, but still be open.
QVERIFY(dialogHelper.quickDialog->footer());
@@ -500,7 +520,7 @@ void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
QVERIFY(openButton);
QVERIFY(clickButton(openButton));
COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDirCanonicalPath));
QVERIFY(dialogHelper.dialog->isVisible());
dialogHelper.dialog->close();
@@ -530,14 +550,14 @@ void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
QQuickFileDialogDelegate *subDirDelegate = nullptr;
const int subDirIndex = showDirsFirst ? 0 : 2;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, subDirIndex, subDirDelegate));
- COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDirCanonicalPath));
QVERIFY(doubleClickButton(subDirDelegate));
const QStringList expectedVisibleFiles = tempSubDirExpectedVisibleFiles(showDirsFirst ? ShowDirectoriesFirst : ShowFilesFirst);
QString failureMessage;
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
// The first file in the directory should now be selected.
const QUrl firstFileUrl = showDirsFirst ? QUrl::fromLocalFile(tempSubSubDir.path()) : QUrl::fromLocalFile(tempSubFile1->fileName());
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), firstFileUrl, 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDirCanonicalPath), firstFileUrl, 0);
// Since we only chose a folder, the dialog should still be open.
QVERIFY(dialogHelper.dialog->isVisible());
@@ -552,7 +572,8 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
OPEN_QUICK_DIALOG();
// See comment in chooseFileViaTextEdit for why we check for this.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
@@ -564,8 +585,8 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
// Enter the path to the folder in the text edit.
- enterText(dialogHelper.window(), tempSubDir.path());
- QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir.path());
+ enterText(dialogHelper.window(), tempSubDirCanonicalPath);
+ QCOMPARE(breadcrumbBar->textField()->text(), tempSubDirCanonicalPath);
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
@@ -574,7 +595,7 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the directory should be selected, which is "sub-sub-dir".
// Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
- VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
+ VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDirCanonicalPath), QUrl::fromLocalFile(tempSubSubDir.path()));
VERIFY_DELEGATE_CURRENT(QUrl::fromLocalFile(tempSubSubDir.path()), 0)
QVERIFY(dialogHelper.dialog->isVisible());
@@ -590,7 +611,8 @@ void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
OPEN_QUICK_DIALOG();
// The first delegate in the view should be selected and have focus.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Select the delegate by pressing enter.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
@@ -598,7 +620,8 @@ void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the new directory should be selected, which is "sub-sub-dir".
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubSubDir.path()), 0);
// Since we only chose a folder, the dialog should still be open.
QVERIFY(dialogHelper.dialog->isVisible());
@@ -613,7 +636,8 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
OPEN_QUICK_DIALOG();
// See comment in chooseFileViaTextEdit for why we check for this.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -624,14 +648,14 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
// Enter the path to the file in the text edit.
- enterText(dialogHelper.window(), tempFile2->fileName());
- QCOMPARE(breadcrumbBar->textField()->text(), tempFile2->fileName());
+ enterText(dialogHelper.window(), tempFile2CanonicalPath);
+ QCOMPARE(breadcrumbBar->textField()->text(), tempFile2CanonicalPath);
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) });
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
// Check that the text edit is hidden and breadcrumbs are shown instead.
@@ -647,12 +671,12 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
// The text edit should show the directory that contains the last file that was selected.
- QCOMPARE(breadcrumbBar->textField()->text(), tempDir.path());
+ QCOMPARE(breadcrumbBar->textField()->text(), tempDirCanonicalPath);
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
// Enter the path to the folder in the text edit.
- enterText(dialogHelper.window(), tempSubDir.path());
- QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir.path());
+ enterText(dialogHelper.window(), tempSubDirCanonicalPath);
+ QCOMPARE(breadcrumbBar->textField()->text(), tempSubDirCanonicalPath);
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
@@ -661,7 +685,7 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the directory should be selected, which is "sub-sub-dir".
// Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
- VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
+ VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDirCanonicalPath), QUrl::fromLocalFile(tempSubSubDir.path()));
VERIFY_DELEGATE_CURRENT(QUrl::fromLocalFile(tempSubSubDir.path()), 0)
// Close the dialog.
@@ -676,7 +700,8 @@ void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
OPEN_QUICK_DIALOG();
// See comment in chooseFileViaTextEdit for why we check for this.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -744,12 +769,12 @@ void tst_QQuickFileDialogImpl::goUp()
// Open the dialog. Start off in "sub-dir".
FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDirCanonicalPath) }});
OPEN_QUICK_DIALOG();
// Go up a directory via the button next to the breadcrumb bar.
- qCDebug(lcTest) << "going up to" << tempDir.path() << "- files in that dir:\n"
- << QQuickFileDialogImplPrivate::get(dialogHelper.quickDialog)->fileList(QDir(tempDir.path()));
+ qCDebug(lcTest) << "going up to" << tempDirCanonicalPath << "- files in that dir:\n"
+ << QQuickFileDialogImplPrivate::get(dialogHelper.quickDialog)->fileList(QDir(tempDirCanonicalPath));
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
auto barListView = qobject_cast<QQuickListView*>(breadcrumbBar->contentItem());
@@ -762,19 +787,21 @@ void tst_QQuickFileDialogImpl::goUp()
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
tempDirExpectedVisibleFiles(showDirsFirst ? ShowDirectoriesFirst : ShowFilesFirst), failureMessage), qPrintable(failureMessage));
int expectedCurrentIndex = showDirsFirst ? 0 : 2;
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), expectedCurrentIndex);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), expectedCurrentIndex);
// Go up a directory via the keyboard shortcut.
- QDir tempParentDir(tempDir.path());
+ QDir tempParentDir(tempDirCanonicalPath);
QVERIFY(tempParentDir.cdUp());
const auto filesInTempParentDir = QQuickFileDialogImplPrivate::get(dialogHelper.quickDialog)->fileList(tempParentDir);
qCDebug(lcTest) << "going up to" << tempParentDir.path() << "- files in that dir:\n" << filesInTempParentDir;
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
// Ubuntu on QEMU arm shows no files in /tmp even if there are.
if (!filesInTempParentDir.isEmpty()) {
- expectedCurrentIndex = filesInTempParentDir.indexOf(QFileInfo(tempDir.path()));
+ expectedCurrentIndex = filesInTempParentDir.indexOf(QFileInfo(tempDirCanonicalPath));
QVERIFY(expectedCurrentIndex != -1);
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempParentDir.path()), QUrl::fromLocalFile(tempDir.path()), expectedCurrentIndex);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempParentDir.path()),
+ QUrl::fromLocalFile(tempDirCanonicalPath), expectedCurrentIndex);
}
}
@@ -782,10 +809,11 @@ void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
{
// Open the dialog.
FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDirCanonicalPath) }});
OPEN_QUICK_DIALOG();
// See comment in chooseFileViaTextEdit for why we check for this.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -800,7 +828,7 @@ void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
QVERIFY(QQuickTest::qWaitForPolish(barListView));
QVERIFY(clickButton(breadcrumbBar->upButton()));
// The path should have changed to the parent directory.
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
// The text edit should be hidden when it loses focus.
QVERIFY(!breadcrumbBar->textField()->hasActiveFocus());
QVERIFY(!breadcrumbBar->textField()->isVisible());
@@ -869,7 +897,8 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
// Open the dialog.
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
OPEN_QUICK_DIALOG();
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath),
+ QUrl::fromLocalFile(tempSubDirCanonicalPath), 0);
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -983,8 +1012,8 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
QTRY_VERIFY(!comboBox->popup()->isVisible());
// Use QTRY_VERIFY2 here to fix a failure on QEMU armv7 (QT_QPA_PLATFORM=offscreen).
// Not sure why it's necessary.
- QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, { tempSubDir.path() }, failureMessage), qPrintable(failureMessage));
-
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ { tempSubDirCanonicalPath }, failureMessage), qPrintable(failureMessage));
// Open the popup again.
QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos);
QTRY_VERIFY(comboBox->popup()->isOpened());
@@ -1014,8 +1043,8 @@ void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
QQuickFileDialogDelegate *subDirDelegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, subDirDelegate));
QVERIFY(doubleClickButton(subDirDelegate));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDirCanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDirCanonicalPath));
// Open the ComboBox's popup.
const QQuickComboBox *comboBox = dialogHelper.quickDialog->findChild<QQuickComboBox*>();
@@ -1222,10 +1251,10 @@ void tst_QQuickFileDialogImpl::fileMode()
// Select the first file (not a directory).
QQuickFileDialogDelegate *tempFile1Delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 1, tempFile1Delegate));
- COMPARE_URL(tempFile1Delegate->file(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URL(tempFile1Delegate->file(), QUrl::fromLocalFile(tempFile1CanonicalPath));
QVERIFY(clickButton(tempFile1Delegate));
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) });
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile1CanonicalPath) });
// All modes should support opening an existing file, so the accept button should be enabled.
QVERIFY(dialogHelper.quickDialog->footer());
@@ -1238,21 +1267,21 @@ void tst_QQuickFileDialogImpl::fileMode()
// Only the OpenFiles mode should allow multiple files to be selected, however.
QQuickFileDialogDelegate *tempFile2Delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, tempFile2Delegate));
- COMPARE_URL(tempFile2Delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URL(tempFile2Delegate->file(), QUrl::fromLocalFile(tempFile2CanonicalPath));
QTest::keyPress(dialogHelper.window(), Qt::Key_Shift);
QVERIFY(clickButton(tempFile2Delegate));
QTest::keyRelease(dialogHelper.window(), Qt::Key_Shift);
if (fileMode == QQuickFileDialog::OpenFiles) {
// currentFile() always points to the first file in the list of selected files.
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1CanonicalPath));
const QList<QUrl> expectedSelectedFiles = {
- QUrl::fromLocalFile(tempFile1->fileName()), QUrl::fromLocalFile(tempFile2->fileName()) };
+ QUrl::fromLocalFile(tempFile1CanonicalPath), QUrl::fromLocalFile(tempFile2CanonicalPath) };
COMPARE_URLS(dialogHelper.dialog->currentFiles(), expectedSelectedFiles);
} else {
// OpenFile and SaveFile dialogs should have tempFile2 selected since it was clicked,
// but the shift should do nothing, so tempFile1 should no longer be selected.
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) });
}
// Get the text edit visible with Ctrl+L.
@@ -1275,18 +1304,18 @@ void tst_QQuickFileDialogImpl::fileMode()
// For OpenFile(s), we do what Qt Quick Dialogs 1.x did, and restore the previous (valid) dir path.
// The currentFile(s) should remain unchanged too.
QVERIFY(dialogHelper.dialog->isVisible());
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- QCOMPARE(breadcrumbBar->textField()->text(), tempDir.path());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ QCOMPARE(breadcrumbBar->textField()->text(), tempDirCanonicalPath);
// Should be unchanged from the last time.
if (fileMode == QQuickFileDialog::OpenFiles) {
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1CanonicalPath));
const QList<QUrl> expectedSelectedFiles = {
- QUrl::fromLocalFile(tempFile1->fileName()), QUrl::fromLocalFile(tempFile2->fileName()) };
+ QUrl::fromLocalFile(tempFile1CanonicalPath), QUrl::fromLocalFile(tempFile2CanonicalPath) };
COMPARE_URLS(dialogHelper.dialog->currentFiles(), expectedSelectedFiles);
} else { // OpenFile
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2CanonicalPath));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2CanonicalPath) });
}
}
}
@@ -1371,7 +1400,7 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
QFETCH(QQuickFileDialog::FileMode, fileMode);
// Open the dialog.
- const auto tempFile1Url = QUrl::fromLocalFile(tempFile1->fileName());
+ const auto tempFile1Url = QUrl::fromLocalFile(tempFile1CanonicalPath);
const QVariantMap initialProperties = {
{ "tempFile1Url", QVariant::fromValue(tempFile1Url) },
{ "fileMode", QVariant::fromValue(fileMode) }
@@ -1384,12 +1413,12 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
OPEN_QUICK_DIALOG();
// The selected file should be what we set.
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath), tempFile1Url, 1);
// Select the next file in the view by navigating with the down key.
// We know it already has focus, as VERIFY_FILE_SELECTED_AND_FOCUSED checks that.
QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
- const auto tempFile2Url = QUrl::fromLocalFile(tempFile2->fileName());
+ const auto tempFile2Url = QUrl::fromLocalFile(tempFile2CanonicalPath);
COMPARE_URL(dialogHelper.quickDialog->selectedFile(), tempFile2Url);
COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
@@ -1402,10 +1431,10 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
QCOMPARE(dialogHelper.dialog->result(), QQuickFileDialog::Accepted);
// Set a different initial selectedFile and re-open.
- dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1->fileName()));
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1CanonicalPath));
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath), tempFile1Url, 1);
// Close it.
dialogHelper.dialog->close();
@@ -1414,7 +1443,7 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
// Try to set an invalid selectedFile; it should be a no-op for modes other than SaveFile,
// and the previous selectedFile should still be selected.
- const QString invalidPath = tempDir.path() + "/does-not-exist.txt";
+ const QString invalidPath = tempDirCanonicalPath + "/does-not-exist.txt";
if (fileMode != QQuickFileDialog::SaveFile) {
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(QLatin1String(".*QML FileDialog: Cannot set ")
+ invalidPath + QLatin1String(" as a selected file because it doesn't exist")));
@@ -1422,7 +1451,7 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(invalidPath));
QVERIFY(dialogHelper.openDialog());
if (fileMode != QQuickFileDialog::SaveFile) {
- VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDirCanonicalPath), tempFile1Url, 1);
} else {
QTRY_COMPARE(dialogHelper.fileDialogListView->currentIndex(), -1);
}
@@ -1474,7 +1503,7 @@ void tst_QQuickFileDialogImpl::selectExistingFileShouldWarnUserWhenFileModeEqual
{
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
dialogHelper.dialog->setFileMode(QQuickFileDialog::SaveFile);
- dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1->fileName()));
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1CanonicalPath));
OPEN_QUICK_DIALOG();
QQuickTest::qWaitForPolish(dialogHelper.window());
@@ -1554,8 +1583,8 @@ void tst_QQuickFileDialogImpl::selectExistingFileShouldWarnUserWhenFileModeEqual
void tst_QQuickFileDialogImpl::fileNameTextFieldOnlyChangesWhenSelectingFiles()
{
const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
- const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path());
- const auto tempFile11Url = QUrl::fromLocalFile(tempFile1->fileName());
+ const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDirCanonicalPath);
+ const auto tempFile11Url = QUrl::fromLocalFile(tempFile1CanonicalPath);
const QVariantMap initialProperties = {
{ "tempFile1Url", QVariant::fromValue(tempSubFile1Url) },
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
index 01116aa7e9..5450db8e37 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
@@ -68,13 +68,18 @@ private slots:
private:
QTemporaryDir tempDir;
+ QString tempDirCanonicalPath;
QScopedPointer<QFile> tempFile1;
+ QString tempFile1CanonicalPath;
QScopedPointer<QFile> tempFile2;
+ QString tempFile2CanonicalPath;
QDir tempSubDir1;
+ QString tempSubDir1CanonicalPath;
QDir tempSubSubDir;
QScopedPointer<QFile> tempSubFile1;
QScopedPointer<QFile> tempSubFile2;
QDir tempSubDir2;
+ QString tempSubDir2CanonicalPath;
QDir oldCurrentDir;
};
@@ -88,11 +93,12 @@ void tst_QQuickFolderDialogImpl::initTestCase()
QQmlDataTest::initTestCase();
QVERIFY(tempDir.isValid());
+ tempDirCanonicalPath = QFileInfo(tempDir.path()).canonicalFilePath();
// QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
// and then calls qRun() for each style, and qRun() calls initTestCase().
// So, we need to check if we've already made the temporary directory.
// Note that this is only necessary if the test is run with more than one style.
- if (!QDir(tempDir.path()).isEmpty())
+ if (!QDir(tempDirCanonicalPath).isEmpty())
return;
/*
@@ -107,39 +113,43 @@ void tst_QQuickFolderDialogImpl::initTestCase()
├── file1.txt
└── file2.txt
*/
- tempSubDir1 = QDir(tempDir.path());
+ tempSubDir1 = QDir(tempDirCanonicalPath);
QVERIFY2(tempSubDir1.mkdir("sub-dir-1"), qPrintable(QString::fromLatin1(
"Failed to make sub-directory \"sub-dir-1\" in %1. Permissions are: %2")
- .arg(tempDir.path(), QDebug::toString(QFileInfo(tempDir.path()).permissions()))));
+ .arg(tempDirCanonicalPath, QDebug::toString(QFileInfo(tempDirCanonicalPath).permissions()))));
QVERIFY(tempSubDir1.cd("sub-dir-1"));
+ tempSubDir1CanonicalPath = tempSubDir1.canonicalPath();
+ QVERIFY(!tempSubDir1CanonicalPath.isEmpty());
- tempSubSubDir = QDir(tempSubDir1.path());
+ tempSubSubDir = QDir(tempSubDir1CanonicalPath);
QVERIFY2(tempSubSubDir.mkdir("sub-sub-dir"), qPrintable(QString::fromLatin1(
"Failed to make sub-directory \"sub-sub-dir\" in %1. Permissions are: %2")
- .arg(tempSubDir1.path(), QDebug::toString(QFileInfo(tempSubDir1.path()).permissions()))));
+ .arg(tempSubDir1CanonicalPath, QDebug::toString(QFileInfo(tempSubDir1CanonicalPath).permissions()))));
QVERIFY(tempSubSubDir.cd("sub-sub-dir"));
- tempSubFile1.reset(new QFile(tempSubDir1.path() + "/sub-file1.txt"));
+ tempSubFile1.reset(new QFile(tempSubDir1CanonicalPath + "/sub-file1.txt"));
QVERIFY(tempSubFile1->open(QIODevice::ReadWrite));
- tempSubFile2.reset(new QFile(tempSubDir1.path() + "/sub-file2.txt"));
+ tempSubFile2.reset(new QFile(tempSubDir1CanonicalPath + "/sub-file2.txt"));
QVERIFY(tempSubFile2->open(QIODevice::ReadWrite));
- tempSubDir2 = QDir(tempDir.path());
+ tempSubDir2 = QDir(tempDirCanonicalPath);
QVERIFY2(tempSubDir2.mkdir("sub-dir-2"), qPrintable(QString::fromLatin1(
"Failed to make sub-directory \"sub-dir-2\" in %1. Permissions are: %2")
- .arg(tempDir.path(), QDebug::toString(QFileInfo(tempDir.path()).permissions()))));
+ .arg(tempDirCanonicalPath, QDebug::toString(QFileInfo(tempDirCanonicalPath).permissions()))));
QVERIFY(tempSubDir2.cd("sub-dir-2"));
+ tempSubDir2CanonicalPath = tempSubDir2.canonicalPath();
+ QVERIFY(!tempSubDir2CanonicalPath.isEmpty());
- tempFile1.reset(new QFile(tempDir.path() + "/file1.txt"));
+ tempFile1.reset(new QFile(tempDirCanonicalPath + "/file1.txt"));
QVERIFY(tempFile1->open(QIODevice::ReadWrite));
- tempFile2.reset(new QFile(tempDir.path() + "/file2.txt"));
+ tempFile2.reset(new QFile(tempDirCanonicalPath + "/file2.txt"));
QVERIFY(tempFile2->open(QIODevice::ReadWrite));
// Ensure that each test starts off in the temporary directory.
oldCurrentDir = QDir::current();
- QDir::setCurrent(tempDir.path());
+ QDir::setCurrent(tempDirCanonicalPath);
}
void tst_QQuickFolderDialogImpl::cleanupTestCase()
@@ -171,9 +181,9 @@ void tst_QQuickFolderDialogImpl::defaults()
QVERIFY(quickDialog);
QTRY_VERIFY(quickDialog->isOpened());
QVERIFY(quickDialog);
- COMPARE_URL(quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
+ COMPARE_URL(quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
COMPARE_URL(quickDialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
- COMPARE_URL(dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
+ COMPARE_URL(dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
COMPARE_URL(dialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
QCOMPARE(quickDialog->title(), QString());
}
@@ -223,13 +233,13 @@ void tst_QQuickFolderDialogImpl::chooseFolderViaStandardButtons()
QVERIFY(folderDialogListView);
QQuickFileDialogDelegate *delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(folderDialogListView, 1, delegate));
- COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir2.path()));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
QVERIFY(clickButton(delegate));
// currentFolder shouldn't change just from a single click.
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- COMPARE_URL(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
- COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
+ COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
// Only selectedFile-related signals should be emitted.
QCOMPARE(signalHelper.dialogSelectedFolderChangedSpy.size(), 1);
QCOMPARE(signalHelper.quickDialogSelectedFolderChangedSpy.size(), 1);
@@ -243,8 +253,8 @@ void tst_QQuickFolderDialogImpl::chooseFolderViaStandardButtons()
QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
QVERIFY(openButton);
QVERIFY(clickButton(openButton));
- COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
- COMPARE_URL(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
+ COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
QCOMPARE(signalHelper.dialogSelectedFolderChangedSpy.size(), 1);
QCOMPARE(signalHelper.quickDialogSelectedFolderChangedSpy.size(), 1);
QCOMPARE(signalHelper.dialogCurrentFolderChangedSpy.size(), 0);
@@ -259,11 +269,11 @@ void tst_QQuickFolderDialogImpl::bindCurrentFolder_data()
QTest::addColumn<QUrl>("expectedFolder");
QTest::addColumn<QStringList>("expectedVisibleFiles");
- const auto currentDirUrl = QUrl::fromLocalFile(QDir::current().path());
- const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir1.path());
+ const auto currentDirUrl = QUrl::fromLocalFile(QDir::current().canonicalPath());
+ const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir1CanonicalPath);
const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
- const QStringList currentDirFiles = { tempSubDir1.path(), tempSubDir2.path() };
+ const QStringList currentDirFiles = { tempSubDir1CanonicalPath, tempSubDir2CanonicalPath };
const QStringList tempSubDirFiles = { tempSubSubDir.path() };
// Setting the folder to "sub-dir-1" should result in "sub-sub-dir" being visible.
@@ -317,9 +327,9 @@ void tst_QQuickFolderDialogImpl::changeFolderViaDoubleClick()
QVERIFY(folderDialogListView);
QQuickFileDialogDelegate *delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(folderDialogListView, 1, delegate));
- COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir2.path()))
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir2CanonicalPath))
QVERIFY(doubleClickButton(delegate));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2.path()))
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath))
COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl());
// selectedFolder is set to the folder when clicked and then set to an empty URL after
// the double click.
@@ -354,13 +364,13 @@ void tst_QQuickFolderDialogImpl::changeFolderViaTextEdit()
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
// Enter the path to the folder in the text edit.
- enterText(dialogHelper.window(), tempSubDir2.path());
- QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir2.path());
+ enterText(dialogHelper.window(), tempSubDir2CanonicalPath);
+ QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir2CanonicalPath);
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
// We changed into a directory with no folders, so the selected folder should be invalid.
COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl());
COMPARE_URL(dialogHelper.quickDialog->selectedFolder(), QUrl());
@@ -382,19 +392,19 @@ void tst_QQuickFolderDialogImpl::changeFolderViaEnter()
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
// The first delegate in the view should be selected and have focus.
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
auto folderDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("folderDialogListView");
QVERIFY(folderDialogListView);
QQuickFileDialogDelegate *subDir1Delegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(folderDialogListView, 0, subDir1Delegate));
- COMPARE_URL(subDir1Delegate->file(), QUrl::fromLocalFile(tempSubDir1.path()));
+ COMPARE_URL(subDir1Delegate->file(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
QVERIFY(subDir1Delegate->hasActiveFocus());
// Select the delegate by pressing enter.
const FolderDialogSignalHelper signalHelper(dialogHelper);
QVERIFY2(signalHelper.errorMessage.isEmpty(), signalHelper.errorMessage);
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubSubDir.path()));
QCOMPARE(signalHelper.dialogSelectedFolderChangedSpy.size(), 1);
QCOMPARE(signalHelper.quickDialogSelectedFolderChangedSpy.size(), 1);
@@ -448,7 +458,7 @@ void tst_QQuickFolderDialogImpl::goUp()
{
// Open the dialog, starting off in "sub-dir-1".
FolderDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1CanonicalPath) }});
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
QVERIFY(dialogHelper.openDialog());
@@ -462,9 +472,9 @@ void tst_QQuickFolderDialogImpl::goUp()
if (QQuickTest::qIsPolishScheduled(barListView))
QVERIFY(QQuickTest::qWaitForPolish(barListView));
QVERIFY(clickButton(breadcrumbBar->upButton()));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
// The previous directory that we were in should now be selected (matches e.g. Windows and Ubuntu).
- COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
+ COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
auto folderDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("folderDialogListView");
QVERIFY(folderDialogListView);
QQuickFileDialogDelegate *subDirDelegate = nullptr;
@@ -474,17 +484,17 @@ void tst_QQuickFolderDialogImpl::goUp()
// Go up a directory via the keyboard shortcut.
const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
- QDir tempParentDir(tempDir.path());
+ QDir tempParentDir(tempDirCanonicalPath);
QVERIFY(tempParentDir.cdUp());
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempParentDir.path()));
- COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
}
void tst_QQuickFolderDialogImpl::goUpWhileTextEditHasFocus()
{
// Open the dialog, starting off in "sub-dir-1".
FolderDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1CanonicalPath) }});
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
QVERIFY(dialogHelper.openDialog());
@@ -499,7 +509,7 @@ void tst_QQuickFolderDialogImpl::goUpWhileTextEditHasFocus()
QVERIFY(QQuickTest::qWaitForPolish(barListView));
QVERIFY(clickButton(breadcrumbBar->upButton()));
// The path should have changed to the parent directory.
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
// The text edit should be hidden when it loses focus.
QVERIFY(!breadcrumbBar->textField()->hasActiveFocus());
QVERIFY(!breadcrumbBar->textField()->isVisible());
@@ -574,7 +584,7 @@ void tst_QQuickFolderDialogImpl::clickOnBreadcrumb()
{
// Open the dialog, starting off in "sub-dir-1".
FolderDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir1CanonicalPath) }});
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
QVERIFY(dialogHelper.openDialog());
@@ -595,10 +605,10 @@ void tst_QQuickFolderDialogImpl::clickOnBreadcrumb()
QTRY_VERIFY(findViewDelegateItem(breadcrumbBarListView, breadcrumbBarListView->count() - 3, breadcrumbDelegate));
QVERIFY(clickButton(breadcrumbDelegate));
- QCOMPARE(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- QCOMPARE(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- QCOMPARE(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
- QCOMPARE(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1.path()));
+ QCOMPARE(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ QCOMPARE(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ QCOMPARE(dialogHelper.quickDialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
+ QCOMPARE(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempSubDir1CanonicalPath));
}
void tst_QQuickFolderDialogImpl::keyAndShortcutHandling()
@@ -771,13 +781,13 @@ void tst_QQuickFolderDialogImpl::itemsDisabledWhenNecessary()
{
// Open the dialog.
FolderDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(tempSubDir2.path()) }});
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir2CanonicalPath) }});
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir2CanonicalPath));
// We opened it in a folder that has no files, so the Open button should be disabled.
QVERIFY(dialogHelper.quickDialog->footer());
@@ -792,8 +802,8 @@ void tst_QQuickFolderDialogImpl::itemsDisabledWhenNecessary()
QVERIFY(breadcrumbBar);
QVERIFY(clickButton(breadcrumbBar->upButton()));
QCOMPARE(openButton->isEnabled(), true);
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDirCanonicalPath));
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
diff --git a/tests/auto/quickwidgets/qquickwidget/BLACKLIST b/tests/auto/quickwidgets/qquickwidget/BLACKLIST
index 095e9ee484..f5ca6141f6 100644
--- a/tests/auto/quickwidgets/qquickwidget/BLACKLIST
+++ b/tests/auto/quickwidgets/qquickwidget/BLACKLIST
@@ -3,3 +3,4 @@ opensuse-42.3
opensuse-leap
[enterLeave]
macos
+windows
diff --git a/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt b/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
index 1ace458afb..18c983f087 100644
--- a/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
+++ b/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(qmlscenegrabber LANGUAGES C CXX)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
#####################################################################
## qmlscenegrabber Binary:
#####################################################################
diff --git a/tests/benchmarks/quickcontrols/creationtime/CMakeLists.txt b/tests/benchmarks/quickcontrols/creationtime/CMakeLists.txt
index b719e8148f..f877fedb1f 100644
--- a/tests/benchmarks/quickcontrols/creationtime/CMakeLists.txt
+++ b/tests/benchmarks/quickcontrols/creationtime/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_creationtime LANGUAGES C CXX ASM)
+ project(tst_creationtime LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/benchmarks/quickcontrols/objectcount/CMakeLists.txt b/tests/benchmarks/quickcontrols/objectcount/CMakeLists.txt
index 65fdf74a2c..251b60059f 100644
--- a/tests/benchmarks/quickcontrols/objectcount/CMakeLists.txt
+++ b/tests/benchmarks/quickcontrols/objectcount/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_objectcount LANGUAGES C CXX ASM)
+ project(tst_objectcount LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/buttons/CMakeLists.txt b/tests/manual/quickcontrols/buttons/CMakeLists.txt
index 048645b43f..31d01abc7c 100644
--- a/tests/manual/quickcontrols/buttons/CMakeLists.txt
+++ b/tests/manual/quickcontrols/buttons/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(buttons LANGUAGES C CXX ASM)
+ project(buttons LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/fonts/CMakeLists.txt b/tests/manual/quickcontrols/fonts/CMakeLists.txt
index 476b7f1a7a..4ef977e518 100644
--- a/tests/manual/quickcontrols/fonts/CMakeLists.txt
+++ b/tests/manual/quickcontrols/fonts/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(fonts LANGUAGES C CXX ASM)
+ project(fonts LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/gifs/CMakeLists.txt b/tests/manual/quickcontrols/gifs/CMakeLists.txt
index 674806e145..deb982dc18 100644
--- a/tests/manual/quickcontrols/gifs/CMakeLists.txt
+++ b/tests/manual/quickcontrols/gifs/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tst_gifs LANGUAGES C CXX ASM)
+ project(tst_gifs LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/headerview/CMakeLists.txt b/tests/manual/quickcontrols/headerview/CMakeLists.txt
index fd00f693c8..1973f1a2fd 100644
--- a/tests/manual/quickcontrols/headerview/CMakeLists.txt
+++ b/tests/manual/quickcontrols/headerview/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(headerview LANGUAGES C CXX ASM)
+ project(headerview LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/material/CMakeLists.txt b/tests/manual/quickcontrols/material/CMakeLists.txt
index 8c18341bbe..232771b88e 100644
--- a/tests/manual/quickcontrols/material/CMakeLists.txt
+++ b/tests/manual/quickcontrols/material/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(material LANGUAGES C CXX ASM)
+ project(material LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/qquickdialog/CMakeLists.txt b/tests/manual/quickcontrols/qquickdialog/CMakeLists.txt
index 232dae7ffa..d2322ed934 100644
--- a/tests/manual/quickcontrols/qquickdialog/CMakeLists.txt
+++ b/tests/manual/quickcontrols/qquickdialog/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(qquickdialog LANGUAGES C CXX ASM)
+ project(qquickdialog LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/screenshots/CMakeLists.txt b/tests/manual/quickcontrols/screenshots/CMakeLists.txt
index 32d465b9d4..ece360c1c0 100644
--- a/tests/manual/quickcontrols/screenshots/CMakeLists.txt
+++ b/tests/manual/quickcontrols/screenshots/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(screenshots LANGUAGES C CXX ASM)
+ project(screenshots LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/styles-cover-flow/CMakeLists.txt b/tests/manual/quickcontrols/styles-cover-flow/CMakeLists.txt
index b2fa66bf44..8412eeab4d 100644
--- a/tests/manual/quickcontrols/styles-cover-flow/CMakeLists.txt
+++ b/tests/manual/quickcontrols/styles-cover-flow/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(styles-cover-flow LANGUAGES C CXX ASM)
+ project(styles-cover-flow LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/styles/CMakeLists.txt b/tests/manual/quickcontrols/styles/CMakeLists.txt
index 479fcfdc22..05aebfe89f 100644
--- a/tests/manual/quickcontrols/styles/CMakeLists.txt
+++ b/tests/manual/quickcontrols/styles/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(styles LANGUAGES C CXX ASM)
+ project(styles LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/systemtrayicon/CMakeLists.txt b/tests/manual/quickcontrols/systemtrayicon/CMakeLists.txt
index b8913ec986..4a415c767a 100644
--- a/tests/manual/quickcontrols/systemtrayicon/CMakeLists.txt
+++ b/tests/manual/quickcontrols/systemtrayicon/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(systemtrayicon LANGUAGES C CXX ASM)
+ project(systemtrayicon LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/testbench/CMakeLists.txt b/tests/manual/quickcontrols/testbench/CMakeLists.txt
index 9cbf26f324..5ed82ac800 100644
--- a/tests/manual/quickcontrols/testbench/CMakeLists.txt
+++ b/tests/manual/quickcontrols/testbench/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(testbench LANGUAGES C CXX ASM)
+ project(testbench LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickcontrols/viewinqwidget/CMakeLists.txt b/tests/manual/quickcontrols/viewinqwidget/CMakeLists.txt
index 7deba0df55..848d2171a0 100644
--- a/tests/manual/quickcontrols/viewinqwidget/CMakeLists.txt
+++ b/tests/manual/quickcontrols/viewinqwidget/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(viewinqwidget LANGUAGES C CXX ASM)
+ project(viewinqwidget LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/quickdialogs/dialogs/CMakeLists.txt b/tests/manual/quickdialogs/dialogs/CMakeLists.txt
index 65d641f333..d2a20d96e9 100644
--- a/tests/manual/quickdialogs/dialogs/CMakeLists.txt
+++ b/tests/manual/quickdialogs/dialogs/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(dialogs LANGUAGES C CXX ASM)
+ project(dialogs LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/righttoleft/layoutdirection/CMakeLists.txt b/tests/manual/righttoleft/layoutdirection/CMakeLists.txt
index 7c1e38e79a..f442f17012 100644
--- a/tests/manual/righttoleft/layoutdirection/CMakeLists.txt
+++ b/tests/manual/righttoleft/layoutdirection/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(layoutdirection LANGUAGES C CXX ASM)
+ project(layoutdirection LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt b/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt
index 69fb0a05a8..cc8a04693a 100644
--- a/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt
+++ b/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(layoutmirroring LANGUAGES C CXX ASM)
+ project(layoutmirroring LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/righttoleft/textalignment/CMakeLists.txt b/tests/manual/righttoleft/textalignment/CMakeLists.txt
index fa0c90f79c..f04cb76184 100644
--- a/tests/manual/righttoleft/textalignment/CMakeLists.txt
+++ b/tests/manual/righttoleft/textalignment/CMakeLists.txt
@@ -3,7 +3,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(textalignment LANGUAGES C CXX ASM)
+ project(textalignment LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/tableview/abstracttablemodel/CMakeLists.txt b/tests/manual/tableview/abstracttablemodel/CMakeLists.txt
index 7f49c14567..7c6c62d61c 100644
--- a/tests/manual/tableview/abstracttablemodel/CMakeLists.txt
+++ b/tests/manual/tableview/abstracttablemodel/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tableview_abstracttablemodel LANGUAGES C CXX ASM)
+ project(tableview_abstracttablemodel LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/tableview/listmodel/CMakeLists.txt b/tests/manual/tableview/listmodel/CMakeLists.txt
index 0f5c27ca87..ffadc2ea47 100644
--- a/tests/manual/tableview/listmodel/CMakeLists.txt
+++ b/tests/manual/tableview/listmodel/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tableview_listmodel LANGUAGES C CXX ASM)
+ project(tableview_listmodel LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/tableview/storagemodel/CMakeLists.txt b/tests/manual/tableview/storagemodel/CMakeLists.txt
index 11ca5971ba..589cf7f3bc 100644
--- a/tests/manual/tableview/storagemodel/CMakeLists.txt
+++ b/tests/manual/tableview/storagemodel/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(tableview_storage LANGUAGES C CXX ASM)
+ project(tableview_storage LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/tableview/tablemodel/form/CMakeLists.txt b/tests/manual/tableview/tablemodel/form/CMakeLists.txt
index 1943079f9f..f355677a44 100644
--- a/tests/manual/tableview/tablemodel/form/CMakeLists.txt
+++ b/tests/manual/tableview/tablemodel/form/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(form LANGUAGES C CXX ASM)
+ project(form LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/tableview/tablemodel/json/CMakeLists.txt b/tests/manual/tableview/tablemodel/json/CMakeLists.txt
index 279fae77b6..aeb6253447 100644
--- a/tests/manual/tableview/tablemodel/json/CMakeLists.txt
+++ b/tests/manual/tableview/tablemodel/json/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(json LANGUAGES C CXX ASM)
+ project(json LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tests/manual/treeview/sidebyside/CMakeLists.txt b/tests/manual/treeview/sidebyside/CMakeLists.txt
index 818cb73bab..7907735cd7 100644
--- a/tests/manual/treeview/sidebyside/CMakeLists.txt
+++ b/tests/manual/treeview/sidebyside/CMakeLists.txt
@@ -5,7 +5,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
- project(treeview_sidebyside LANGUAGES C CXX ASM)
+ project(treeview_sidebyside LANGUAGES C CXX)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 51d46ced53..992a6a0cf6 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -302,6 +302,9 @@ static void getAppFlags(int argc, char **argv)
{
#ifdef QT_GUI_LIB
for (int i=0; i<argc; i++) {
+ if (strcmp(argv[i], "--"))
+ return; // After "--", arguments are interpreted as positional and not as options.
+
if (!strcmp(argv[i], "--apptype") || !strcmp(argv[i], "-a") || !strcmp(argv[i], "-apptype")) {
applicationType = QmlApplicationTypeUnknown;
if (i+1 < argc) {
diff --git a/tools/shared/qqmltoolingsettings.cpp b/tools/shared/qqmltoolingsettings.cpp
index d153f7daec..8a6a84df46 100644
--- a/tools/shared/qqmltoolingsettings.cpp
+++ b/tools/shared/qqmltoolingsettings.cpp
@@ -105,12 +105,21 @@ bool QQmlToolingSettings::search(const QString &path)
break;
}
- if (const QString iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, u"%1.ini"_s.arg(m_toolName)); !iniFile.isEmpty()) {
- if (read(iniFile)) {
- for (const QString &dir : std::as_const(dirs))
- m_seenDirectories[dir] = iniFile;
- return true;
+ // First try to locate settings file with the standard name
+ QString iniFile =
+ QStandardPaths::locate(QStandardPaths::GenericConfigLocation, settingsFileName);
+ // If not found, try alternate name format
+ if (iniFile.isEmpty()) {
+ iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation,
+ u"%1.ini"_s.arg(m_toolName));
+ }
+
+ // If a valid settings file was found, read it and update directory cache
+ if (!iniFile.isEmpty() && read(iniFile)) {
+ for (const QString &dir : std::as_const(dirs)) {
+ m_seenDirectories[dir] = iniFile;
}
+ return true;
}
// No INI file found anywhere, record the failure so we won't have to traverse the entire