aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--.tag2
-rw-r--r--examples/quick/positioners/positioners-attachedproperties.qml28
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp6
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml6
-rw-r--r--src/3rdparty/masm/assembler/ARM64Assembler.h2
-rw-r--r--src/3rdparty/masm/assembler/AbstractMacroAssembler.h2
-rw-r--r--src/3rdparty/masm/assembler/AssemblerBuffer.h3
-rw-r--r--src/3rdparty/masm/assembler/LinkBuffer.h19
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARMv7.h1
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerMIPS.h1
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86.h1
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86_64.h1
-rw-r--r--src/3rdparty/masm/assembler/RepatchBuffer.h181
-rw-r--r--src/3rdparty/masm/assembler/X86Assembler.h4
-rw-r--r--src/3rdparty/masm/stubs/ExecutableAllocator.h14
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp18
-rw-r--r--src/3rdparty/masm/wtf/PageBlock.cpp2
-rw-r--r--src/3rdparty/masm/wtf/PageBlock.h5
-rw-r--r--src/3rdparty/masm/yarr/Yarr.h1
-rw-r--r--src/3rdparty/masm/yarr/YarrJIT.cpp3
-rw-r--r--src/imports/labsmodels/qqmltablemodelcolumn_p.h2
-rw-r--r--src/imports/layouts/qquicklinearlayout.cpp4
-rw-r--r--src/imports/layouts/qquickstacklayout.cpp28
-rw-r--r--src/imports/layouts/qquickstacklayout_p.h3
-rw-r--r--src/imports/statemachine/signaltransition.cpp10
-rw-r--r--src/imports/testlib/quicktestevent.cpp5
-rw-r--r--src/particles/qquickimageparticle.cpp32
-rw-r--r--src/particles/qquickimageparticle_p.h3
-rw-r--r--src/particles/qquickitemparticle.cpp1
-rw-r--r--src/particles/qquickparticlesystem_p.h2
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp6
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp8
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp22
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h5
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro2
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp (renamed from src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp)2
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp41
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h3
-rw-r--r--src/qml/animations/qanimationgroupjob.cpp62
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h3
-rw-r--r--src/qml/animations/qanimationjobutil_p.h4
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob.cpp14
-rw-r--r--src/qml/common/common.pri6
-rw-r--r--src/qml/common/qqmljsfixedpoolarray_p.h2
-rw-r--r--src/qml/common/qv4compileddata_p.h375
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp193
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h4
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h4
-rw-r--r--src/qml/compiler/qv4codegen.cpp11
-rw-r--r--src/qml/compiler/qv4compiler.cpp43
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp5
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h7
-rw-r--r--src/qml/configure.pri2
-rw-r--r--src/qml/debugger/debugger.pri1
-rw-r--r--src/qml/debugger/qqmldebug.cpp13
-rw-r--r--src/qml/debugger/qqmldebugconnector.cpp2
-rw-r--r--src/qml/debugger/qqmldebugconnector_p.h1
-rw-r--r--src/qml/debugger/qqmldebugserver.cpp49
-rw-r--r--src/qml/debugger/qqmldebugserver_p.h1
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs3
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc15
-rw-r--r--src/qml/doc/src/javascript/finetuning.qdoc9
-rw-r--r--src/qml/doc/src/javascript/imports.qdoc13
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc10
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc4
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/signals.qdoc2
-rw-r--r--src/qml/inlinecomponentutils_p.h56
-rw-r--r--src/qml/jit/qv4assemblercommon.cpp7
-rw-r--r--src/qml/jit/qv4assemblercommon_p.h4
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp4
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h4
-rw-r--r--src/qml/jit/qv4baselinejit.cpp5
-rw-r--r--src/qml/jit/qv4baselinejit_p.h4
-rw-r--r--src/qml/jsapi/qjsvalue.h1
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp6
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp58
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h13
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp38
-rw-r--r--src/qml/jsruntime/qv4engine.cpp40
-rw-r--r--src/qml/jsruntime/qv4engine_p.h13
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp61
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h2
-rw-r--r--src/qml/jsruntime/qv4function.cpp3
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp21
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4global_p.h16
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp170
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h38
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp113
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h48
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4object.cpp59
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp4
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h5
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp13
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp24
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h2
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp5
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp5
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp5
-rw-r--r--src/qml/memory/qv4mm.cpp2
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp8
-rw-r--r--src/qml/qml/qqmlbinding.cpp17
-rw-r--r--src/qml/qml/qqmlcomponent.cpp24
-rw-r--r--src/qml/qml/qqmlcomponentattached_p.h1
-rw-r--r--src/qml/qml/qqmlcontext.cpp24
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp4
-rw-r--r--src/qml/qml/qqmlengine.cpp16
-rw-r--r--src/qml/qml/qqmlengine_p.h4
-rw-r--r--src/qml/qml/qqmlexpression.cpp4
-rw-r--r--src/qml/qml/qqmlfileselector.cpp2
-rw-r--r--src/qml/qml/qqmlimport.cpp15
-rw-r--r--src/qml/qml/qqmlincubator.cpp16
-rw-r--r--src/qml/qml/qqmlirloader.cpp18
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp17
-rw-r--r--src/qml/qml/qqmlmetaobject.cpp23
-rw-r--r--src/qml/qml/qqmlmetatype.cpp12
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp122
-rw-r--r--src/qml/qml/qqmlprivate.h39
-rw-r--r--src/qml/qml/qqmlproperty.cpp12
-rw-r--r--src/qml/qml/qqmlproperty_p.h2
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp122
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h10
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp13
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h126
-rw-r--r--src/qml/qml/qqmlpropertycachemethodarguments_p.h2
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h105
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp85
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp8
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp13
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h13
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp133
-rw-r--r--src/qml/qml/qqmltypedata.cpp45
-rw-r--r--src/qml/qml/qqmltypeloader.cpp4
-rw-r--r--src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp2
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp13
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp27
-rw-r--r--src/qml/qtqmlglobal_p.h22
-rw-r--r--src/qml/types/qqmlconnections.cpp13
-rw-r--r--src/qml/util/qqmlpropertymap.cpp2
-rw-r--r--src/qmldebug/qmldebug.pro1
-rw-r--r--src/qmldebug/qqmldebugclient.cpp2
-rw-r--r--src/qmldebug/qqmldebugmessageclient.cpp2
-rw-r--r--src/qmldebug/qqmldebugtranslationclient.cpp2
-rw-r--r--src/qmldebug/qqmlenginedebugclient.cpp2
-rw-r--r--src/qmldebug/qqmlinspectorclient.cpp2
-rw-r--r--src/qmldebug/qqmlpreviewclient.cpp2
-rw-r--r--src/qmldebug/qqmlprofilerevent_p.h1
-rw-r--r--src/qmldebug/qqmlprofilereventreceiver.cpp49
-rw-r--r--src/qmldebug/qqmlprofilereventreceiver_p.h3
-rw-r--r--src/qmldebug/qv4debugclient.cpp2
-rw-r--r--src/qmlmodels/qqmlabstractdelegatecomponent.cpp2
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp18
-rw-r--r--src/qmlmodels/qqmladaptormodel_p.h6
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp27
-rw-r--r--src/qmlmodels/qqmlinstantiator.cpp2
-rw-r--r--src/qmlmodels/qqmllistcompositor_p.h5
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp46
-rw-r--r--src/qmltest/quicktest.cpp15
-rw-r--r--src/qmltest/quicktestresult.cpp3
-rw-r--r--src/qmltyperegistrar/qmltypes.prf20
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp10
-rw-r--r--src/quick/accessible/qaccessiblequickitem_p.h3
-rw-r--r--src/quick/designer/qquickdesignersupportproperties.cpp35
-rw-r--r--src/quick/designer/qquickdesignersupportproperties_p.h7
-rw-r--r--src/quick/doc/images/containmentMask-circle.gifbin0 -> 9926 bytes
-rw-r--r--src/quick/doc/images/containmentMask-shape.gifbin0 -> 11261 bytes
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml69
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml69
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml76
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml73
-rw-r--r--src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.pngbin0 -> 1454 bytes
-rw-r--r--src/quick/doc/snippets/qml/externaldrag.qml20
-rw-r--r--src/quick/doc/snippets/qml/item/childrenRect.qml68
-rw-r--r--src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml71
-rw-r--r--src/quick/doc/snippets/qml/item/containmentMask-shape.qml80
-rw-r--r--src/quick/doc/src/concepts/input/focus.qdoc2
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc64
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc27
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc67
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp2
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp2
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp2
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp108
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h1
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp18
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp29
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp2
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp60
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp2
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp2
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp15
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp2
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp29
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h2
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp5
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp11
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp5
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp14
-rw-r--r--src/quick/items/qquickdroparea.cpp1
-rw-r--r--src/quick/items/qquickevents.cpp14
-rw-r--r--src/quick/items/qquickflickable.cpp146
-rw-r--r--src/quick/items/qquickflickable_p_p.h6
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickimage.cpp10
-rw-r--r--src/quick/items/qquickimagebase.cpp8
-rw-r--r--src/quick/items/qquickitem.cpp189
-rw-r--r--src/quick/items/qquickitemanimation.cpp38
-rw-r--r--src/quick/items/qquickitemanimation_p_p.h8
-rw-r--r--src/quick/items/qquickitemsmodule.cpp2
-rw-r--r--src/quick/items/qquickitemview.cpp8
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp10
-rw-r--r--src/quick/items/qquickitemviewtransition_p.h4
-rw-r--r--src/quick/items/qquicklistview.cpp102
-rw-r--r--src/quick/items/qquickloader.cpp32
-rw-r--r--src/quick/items/qquickloader_p_p.h1
-rw-r--r--src/quick/items/qquickmousearea.cpp6
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp33
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h7
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp5
-rw-r--r--src/quick/items/qquickpincharea.cpp22
-rw-r--r--src/quick/items/qquickscalegrid.cpp2
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp8
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h1
-rw-r--r--src/quick/items/qquickstateoperations.cpp2
-rw-r--r--src/quick/items/qquicktableview.cpp118
-rw-r--r--src/quick/items/qquicktableview_p_p.h2
-rw-r--r--src/quick/items/qquicktext.cpp45
-rw-r--r--src/quick/items/qquicktext_p.h2
-rw-r--r--src/quick/items/qquicktextcontrol.cpp6
-rw-r--r--src/quick/items/qquicktextedit.cpp22
-rw-r--r--src/quick/items/qquicktextedit_p.h2
-rw-r--r--src/quick/items/qquicktextinput.cpp108
-rw-r--r--src/quick/items/qquicktextinput_p.h2
-rw-r--r--src/quick/items/qquicktextinput_p_p.h12
-rw-r--r--src/quick/items/qquickwindow.cpp53
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h4
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp2
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp143
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp2
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp78
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp15
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h1
-rw-r--r--src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp10
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp15
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp16
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp4
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode_p.h1
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp5
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp13
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp3
-rw-r--r--src/quick/scenegraph/shaders/outlinedtext.vert9
-rw-r--r--src/quick/scenegraph/shaders/outlinedtext_core.vert9
-rw-r--r--src/quick/scenegraph/shaders/styledtext.vert7
-rw-r--r--src/quick/scenegraph/shaders/styledtext_core.vert7
-rw-r--r--src/quick/scenegraph/shaders/textmask.vert7
-rw-r--r--src/quick/scenegraph/shaders/textmask_core.vert7
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsbbin1469 -> 1579 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsbbin1344 -> 1436 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsbbin1351 -> 1433 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsbbin845 -> 926 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsbbin2174 -> 2266 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert9
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsbbin2623 -> 2743 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsbbin1437 -> 1528 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag.qsbbin1740 -> 1834 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert7
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert.qsbbin2175 -> 2294 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsbbin1158 -> 1236 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag.qsbbin1469 -> 1561 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert7
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert.qsbbin1933 -> 2047 bytes
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp2
-rw-r--r--src/quick/util/qquickanimation.cpp16
-rw-r--r--src/quick/util/qquickanimation_p_p.h10
-rw-r--r--src/quick/util/qquickanimator.cpp8
-rw-r--r--src/quick/util/qquickanimator_p_p.h8
-rw-r--r--src/quick/util/qquickanimatorjob.cpp11
-rw-r--r--src/quick/util/qquickanimatorjob_p.h11
-rw-r--r--src/quick/util/qquickpath.cpp7
-rw-r--r--src/quick/util/qquickpropertychanges.cpp35
-rw-r--r--src/quick/util/qquickshortcut.cpp90
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp2
-rw-r--r--src/quick/util/qquickstate.cpp5
-rw-r--r--src/quick/util/qquickstategroup.cpp9
-rw-r--r--src/quick/util/qquickstyledtext.cpp6
-rw-r--r--src/quick/util/qquicktimeline.cpp5
-rw-r--r--src/quickshapes/qquickshape.cpp15
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp10
-rw-r--r--src/quickwidgets/qquickwidget.cpp7
-rw-r--r--tests/auto/cmake/qtquickcompiler/main.cpp2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/takeGive.qml3
-rw-r--r--tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp4
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST3
-rw-r--r--tests/auto/qml/animation/qpauseanimationjob/BLACKLIST3
-rw-r--r--tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp42
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml15
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml16
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp148
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp12
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp4
-rw-r--r--tests/auto/qml/ecmascripttests/testcase.pro2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp140
-rw-r--r--tests/auto/qml/qmlcachegen/data/posthocrequired.qml5
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp27
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp133
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/foreign.pro1
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro7
-rw-r--r--tests/auto/qml/qqmlapplicationengine/data/Required.qml6
-rw-r--r--tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml3
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp18
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp3
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp2
-rw-r--r--tests/auto/qml/qqmlconsole/data/assert.qml13
-rw-r--r--tests/auto/qml/qqmlconsole/data/categorized_logging.qml9
-rw-r--r--tests/auto/qml/qqmlconsole/data/exception.qml4
-rw-r--r--tests/auto/qml/qqmlconsole/data/logging.qml21
-rw-r--r--tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp27
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml21
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml31
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml32
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml45
-rw-r--r--tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro2
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp52
-rw-r--r--tests/auto/qml/qqmlecmascript/data/functionAsDefaultArgument.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js29
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp61
-rw-r--r--tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp33
-rw-r--r--tests/auto/qml/qqmlinstantiator/data/activeModelChangeInteraction.qml39
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp21
-rw-r--r--tests/auto/qml/qqmllanguage/data/Broken.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/ComponentType.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/Tab1.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.15a.qml33
-rw-r--r--tests/auto/qml/qqmllanguage/data/ambiguousBinding/TestCase.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/ambiguousBinding/ambiguousContainingType.qml3
-rw-r--r--tests/auto/qml/qqmllanguage/data/asBroken.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/bareInline.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt3
-rw-r--r--tests/auto/qml/qqmllanguage/data/hangOnWarning.qml3
-rw-r--r--tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/qtbug_89822.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp158
-rw-r--r--tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp8
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.mjs117
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.qml166
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSortWorker.mjs38
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/main.qml23
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp17
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp18
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp25
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp14
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml54
-rw-r--r--tests/auto/qml/qv4assembler/tst_qv4assembler.cpp3
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp95
-rw-r--r--tests/auto/qmltest/events/tst_wheel.qml2
-rw-r--r--tests/auto/qmltest/qmltest.pro1
-rw-r--r--tests/auto/qmltest/selftests/tst_grabImage.qml5
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml34
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml3
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp66
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml13
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp23
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml39
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp55
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp1
-rw-r--r--tests/auto/quick/qquickanimations/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickanimations/data/cleanupWhenRenderThreadStops.qml32
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp14
-rw-r--r--tests/auto/quick/qquickbehaviors/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_line.qml31
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml8
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml13
-rw-r--r--tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp45
-rw-r--r--tests/auto/quick/qquickdroparea/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml22
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp122
-rw-r--r--tests/auto/quick/qquickframebufferobject/BLACKLIST1
-rw-r--r--tests/auto/quick/qquickgridview/data/qtbug92998.qml52
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp19
-rw-r--r--tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp22
-rw-r--r--tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml51
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp31
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml37
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml463
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST2
-rw-r--r--tests/auto/quick/qquicklistview/data/AnimatedButton.qml10
-rw-r--r--tests/auto/quick/qquicklistview/data/animatedDelegate.qml35
-rw-r--r--tests/auto/quick/qquicklistview/data/delegateWithMouseArea2.qml120
-rw-r--r--tests/auto/quick/qquicklistview/data/qtbug_92809.qml119
-rw-r--r--tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml32
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp156
-rw-r--r--tests/auto/quick/qquickloader/BLACKLIST1
-rw-r--r--tests/auto/quick/qquickloader/data/initialPropertyValues.12.qml20
-rw-r--r--tests/auto/quick/qquickloader/data/loader-async-race-rect.qml10
-rw-r--r--tests/auto/quick/qquickloader/data/loader-async-race.qml14
-rw-r--r--tests/auto/quick/qquickloader/data/noEngine.qml32
-rw-r--r--tests/auto/quick/qquickloader/data/overflow.qml5
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp52
-rw-r--r--tests/auto/quick/qquickmousearea/data/containsMouse.qml14
-rw-r--r--tests/auto/quick/qquickmousearea/data/doubleClickToHide.qml19
-rw-r--r--tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml31
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp112
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml26
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/nestedPinchArea.qml44
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp66
-rw-r--r--tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml70
-rw-r--r--tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp66
-rw-r--r--tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp137
-rw-r--r--tests/auto/quick/qquickstates/data/noStateOsciallation.qml22
-rw-r--r--tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml48
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp83
-rw-r--r--tests/auto/quick/qquicktableview/data/sizefromdelegate.qml74
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp136
-rw-r--r--tests/auto/quick/qquicktext/data/elideZeroWidth.qml23
-rw-r--r--tests/auto/quick/qquicktext/data/fontSizeMode.qml2
-rw-r--r--tests/auto/quick/qquicktext/data/padding.qml26
-rw-r--r--tests/auto/quick/qquicktext/data/paddingInLoader.qml14
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp95
-rw-r--r--tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml16
-rw-r--r--tests/auto/quick/qquicktextinput/data/focusReason.qml39
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp112
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml43
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp15
-rw-r--r--tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml115
-rw-r--r--tests/auto/quick/scenegraph/data/widebtn1.pngbin0 -> 1969 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/widebtn2.pngbin0 -> 1965 bytes
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp4
-rw-r--r--tests/auto/quicktest/quicktest.pro3
-rw-r--r--tests/auto/quicktest/testwithcomponents/data/Sample.qml8
-rw-r--r--tests/auto/quicktest/testwithcomponents/data/tst_setup.qml (renamed from tests/auto/qmltest/pixel/tst_pixel.qml)50
-rw-r--r--tests/auto/quicktest/testwithcomponents/testwithcomponents.pro10
-rw-r--r--tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp32
-rw-r--r--tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml25
-rw-r--r--tests/manual/pointer/pinchHandler.qml8
-rw-r--r--tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_multi.qml38
-rw-r--r--tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_scissor.qml30
-rw-r--r--tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_stencil.qml31
-rw-r--r--tests/manual/scenegraph_lancelot/data/text/text_nativerendering_subpixelpositions.qml91
-rw-r--r--tests/manual/tableview/storagemodel/storagemodel.cpp25
-rw-r--r--tests/manual/touch/flicktext.qml30
-rw-r--r--tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in11
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp4
-rw-r--r--tools/qmlimportscanner/main.cpp6
-rw-r--r--tools/qmlplugindump/main.cpp26
490 files changed, 9865 insertions, 2185 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 4e82106546..65ef03633e 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -4,4 +4,4 @@ CONFIG += warning_clean
DEFINES += QT_NO_LINKED_LIST
DEFINES += QT_NO_JAVA_STYLE_ITERATORS
-MODULE_VERSION = 5.15.3
+MODULE_VERSION = 5.15.13
diff --git a/.tag b/.tag
index 6828f88dcb..8b42f85cf5 100644
--- a/.tag
+++ b/.tag
@@ -1 +1 @@
-$Format:%H$
+$Format:%T$
diff --git a/examples/quick/positioners/positioners-attachedproperties.qml b/examples/quick/positioners/positioners-attachedproperties.qml
index af89543289..38c71068f2 100644
--- a/examples/quick/positioners/positioners-attachedproperties.qml
+++ b/examples/quick/positioners/positioners-attachedproperties.qml
@@ -90,14 +90,14 @@ Rectangle {
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ + (parent.Positioner.isFirstItem ? " (First)" : "")
+ + (parent.Positioner.isLastItem ? " (Last)" : "")
}
// When mouse is clicked, display the values of the positioner
MouseArea {
- anchors.fill: parent
- onClicked: column.showInfo(green.Positioner)
+ anchors.fill: parent
+ onClicked: column.showInfo(green.Positioner)
}
}
//! [0]
@@ -113,14 +113,14 @@ Rectangle {
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ + (parent.Positioner.isFirstItem ? " (First)" : "")
+ + (parent.Positioner.isLastItem ? " (Last)" : "")
}
// When mouse is clicked, display the values of the positioner
MouseArea {
- anchors.fill: parent
- onClicked: column.showInfo(blue.Positioner)
+ anchors.fill: parent
+ onClicked: column.showInfo(blue.Positioner)
}
}
@@ -135,14 +135,14 @@ Rectangle {
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ + (parent.Positioner.isFirstItem ? " (First)" : "")
+ + (parent.Positioner.isLastItem ? " (Last)" : "")
}
// When mouse is clicked, display the values of the positioner
MouseArea {
- anchors.fill: parent
- onClicked: column.showInfo(purple.Positioner)
+ anchors.fill: parent
+ onClicked: column.showInfo(purple.Positioner)
}
}
@@ -159,8 +159,8 @@ Rectangle {
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ + (parent.Positioner.isFirstItem ? " (First)" : "")
+ + (parent.Positioner.isLastItem ? " (Last)" : "")
}
}
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
index a4e619bea9..80806d51d1 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -158,6 +158,10 @@ void OpenGLRenderNode::render(const RenderState *state)
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // Regardless of flags() returning DepthAwareRendering or not,
+ // we have to test against what's in the depth buffer already.
+ f->glEnable(GL_DEPTH_TEST);
+
// Clip support.
if (state->scissorEnabled()) {
f->glEnable(GL_SCISSOR_TEST);
@@ -177,7 +181,7 @@ void OpenGLRenderNode::render(const RenderState *state)
//! [4]
QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const
{
- return BlendState | ScissorState | StencilState;
+ return BlendState | ScissorState | StencilState | DepthState;
}
QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
diff --git a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
index 60167aa813..3db55a51a3 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
@@ -63,7 +63,11 @@ Rectangle {
Rectangle {
id: content
- anchors { left: parent.left; right: parent.right }
+ anchors {
+ left: parent ? parent.left : undefined
+ right: parent ? parent.right : undefined
+ }
+
height: column.implicitHeight + 4
border.width: 1
diff --git a/src/3rdparty/masm/assembler/ARM64Assembler.h b/src/3rdparty/masm/assembler/ARM64Assembler.h
index ca6b33d39a..3e988a277c 100644
--- a/src/3rdparty/masm/assembler/ARM64Assembler.h
+++ b/src/3rdparty/masm/assembler/ARM64Assembler.h
@@ -676,11 +676,11 @@ public:
struct RealTypes {
int64_t m_from : 48;
int64_t m_to : 48;
+ RegisterID m_compareRegister;
JumpType m_type : 8;
JumpLinkType m_linkType : 8;
Condition m_condition : 4;
unsigned m_bitNumber : 6;
- RegisterID m_compareRegister : 6;
bool m_is64Bit : 1;
} realTypes;
} data;
diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
index 14644a4193..617eef351b 100644
--- a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
+++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
@@ -51,7 +51,6 @@ template <typename, template <typename> class>
class LinkBufferBase;
template <typename>
class BranchCompactingLinkBuffer;
-class RepatchBuffer;
class Watchpoint;
namespace DFG {
struct OSRExit;
@@ -831,7 +830,6 @@ protected:
template <typename, template <typename> class> friend class LinkBufferBase;
template <typename> friend class BranchCompactingLinkBuffer;
- friend class RepatchBuffer;
static void linkJump(void* code, Jump jump, CodeLocationLabel target)
{
diff --git a/src/3rdparty/masm/assembler/AssemblerBuffer.h b/src/3rdparty/masm/assembler/AssemblerBuffer.h
index 2292a4c244..58ecac2a4a 100644
--- a/src/3rdparty/masm/assembler/AssemblerBuffer.h
+++ b/src/3rdparty/masm/assembler/AssemblerBuffer.h
@@ -140,7 +140,8 @@ namespace JSC {
if (!result)
return 0;
- ExecutableAllocator::makeWritable(result->memoryStart(), result->memorySize());
+ if (Q_UNLIKELY(!ExecutableAllocator::makeWritable(result->memoryStart(), result->memorySize())))
+ return 0;
memcpy(result->codeStart(), m_buffer, m_index);
diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h
index fa669deaf9..f1a6639b73 100644
--- a/src/3rdparty/masm/assembler/LinkBuffer.h
+++ b/src/3rdparty/masm/assembler/LinkBuffer.h
@@ -228,7 +228,7 @@ public:
return m_size;
}
- inline void makeExecutable();
+ inline bool makeExecutable();
private:
template <typename T> T applyOffset(T src)
@@ -353,10 +353,10 @@ void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::performFinaliza
}
template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator>
-inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::makeExecutable()
+inline bool LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::makeExecutable()
{
- ExecutableAllocator::makeExecutable(m_executableMemory->memoryStart(),
- m_executableMemory->memorySize());
+ return ExecutableAllocator::makeExecutable(m_executableMemory->memoryStart(),
+ m_executableMemory->memorySize());
}
template <typename MacroAssembler>
@@ -392,7 +392,7 @@ public:
}
virtual void performFinalization() override final;
- inline void makeExecutable();
+ inline bool makeExecutable();
inline void linkCode(void* ownerUID, JITCompilationEffort);
@@ -428,9 +428,9 @@ void BranchCompactingLinkBuffer<MacroAssembler>::performFinalization()
}
template <typename MacroAssembler>
-inline void BranchCompactingLinkBuffer<MacroAssembler>::makeExecutable()
+inline bool BranchCompactingLinkBuffer<MacroAssembler>::makeExecutable()
{
- ExecutableAllocator::makeExecutable(code(), m_initialSize);
+ return ExecutableAllocator::makeExecutable(code(), m_initialSize);
}
template <typename MacroAssembler>
@@ -443,9 +443,12 @@ inline void BranchCompactingLinkBuffer<MacroAssembler>::linkCode(void* ownerUID,
m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort);
if (!m_executableMemory)
return;
+ if (Q_UNLIKELY(!ExecutableAllocator::makeWritable(m_executableMemory->memoryStart(), m_executableMemory->memorySize()))) {
+ m_executableMemory = {};
+ return;
+ }
m_code = (uint8_t*)m_executableMemory->codeStart();
ASSERT(m_code);
- ExecutableAllocator::makeWritable(m_executableMemory->memoryStart(), m_executableMemory->memorySize());
uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode();
uint8_t* outData = reinterpret_cast<uint8_t*>(m_code);
int readPtr = 0;
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
index 6232834fde..1ccea63d23 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
@@ -1955,7 +1955,6 @@ protected:
private:
template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
static void linkCall(void* code, Call call, FunctionPtr function)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
index 07f0ec623f..6dfc104823 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
@@ -2818,7 +2818,6 @@ private:
bool m_fixedWidth;
template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
static void linkCall(void* code, Call call, FunctionPtr function)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
index 5cffa787ec..390a054468 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
@@ -313,7 +313,6 @@ public:
private:
template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
static void linkCall(void* code, Call call, FunctionPtr function)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
index 0a6db0805b..3be7cc2669 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
@@ -733,7 +733,6 @@ public:
private:
template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
static void linkCall(void* code, Call call, FunctionPtr function)
{
diff --git a/src/3rdparty/masm/assembler/RepatchBuffer.h b/src/3rdparty/masm/assembler/RepatchBuffer.h
deleted file mode 100644
index dbb56f9ad5..0000000000
--- a/src/3rdparty/masm/assembler/RepatchBuffer.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef RepatchBuffer_h
-#define RepatchBuffer_h
-
-#if ENABLE(JIT)
-
-#include "CodeBlock.h"
-#include <MacroAssembler.h>
-#include <wtf/Noncopyable.h>
-
-namespace JSC {
-
-// RepatchBuffer:
-//
-// This class is used to modify code after code generation has been completed,
-// and after the code has potentially already been executed. This mechanism is
-// used to apply optimizations to the code.
-//
-class RepatchBuffer {
- typedef MacroAssemblerCodePtr CodePtr;
-
-public:
- RepatchBuffer(CodeBlock* codeBlock)
- {
- JITCode& code = codeBlock->getJITCode();
- m_start = code.start();
- m_size = code.size();
-
- ExecutableAllocator::makeWritable(m_start, m_size);
- }
-
- ~RepatchBuffer()
- {
- ExecutableAllocator::makeExecutable(m_start, m_size);
- }
-
- void relink(CodeLocationJump jump, CodeLocationLabel destination)
- {
- MacroAssembler::repatchJump(jump, destination);
- }
-
- void relink(CodeLocationCall call, CodeLocationLabel destination)
- {
- MacroAssembler::repatchCall(call, destination);
- }
-
- void relink(CodeLocationCall call, FunctionPtr destination)
- {
- MacroAssembler::repatchCall(call, destination);
- }
-
- void relink(CodeLocationNearCall nearCall, CodePtr destination)
- {
- MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination));
- }
-
- void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination)
- {
- MacroAssembler::repatchNearCall(nearCall, destination);
- }
-
- void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value)
- {
- MacroAssembler::repatchInt32(dataLabel32, value);
- }
-
- void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value)
- {
- MacroAssembler::repatchCompact(dataLabelCompact, value);
- }
-
- void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value)
- {
- MacroAssembler::repatchPointer(dataLabelPtr, value);
- }
-
- void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label)
- {
- relink(CodeLocationCall(CodePtr(returnAddress)), label);
- }
-
- void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction)
- {
- relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction));
- }
-
- void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function)
- {
- relink(CodeLocationCall(CodePtr(returnAddress)), function);
- }
-
- void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label)
- {
- relink(CodeLocationNearCall(CodePtr(returnAddress)), label);
- }
-
- void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction)
- {
- relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction));
- }
-
- void replaceWithLoad(CodeLocationConvertibleLoad label)
- {
- MacroAssembler::replaceWithLoad(label);
- }
-
- void replaceWithAddressComputation(CodeLocationConvertibleLoad label)
- {
- MacroAssembler::replaceWithAddressComputation(label);
- }
-
- void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive)
- {
- if (isActive)
- replaceWithLoad(label);
- else
- replaceWithAddressComputation(label);
- }
-
- static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label)
- {
- return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label);
- }
-
- static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label)
- {
- return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label);
- }
-
- void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
- {
- MacroAssembler::replaceWithJump(instructionStart, destination);
- }
-
- // This is a *bit* of a silly API, since we currently always also repatch the
- // immediate after calling this. But I'm fine with that, since this just feels
- // less yucky.
- void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value)
- {
- MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value);
- }
-
- void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value)
- {
- MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value);
- }
-
-private:
- void* m_start;
- size_t m_size;
-};
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER)
-
-#endif // RepatchBuffer_h
diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h
index e8ae687036..8f9ee29a4d 100644
--- a/src/3rdparty/masm/assembler/X86Assembler.h
+++ b/src/3rdparty/masm/assembler/X86Assembler.h
@@ -201,12 +201,12 @@ private:
TwoByteOpcodeID jccRel32(Condition cond)
{
- return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond);
+ return (TwoByteOpcodeID)(int(OP2_JCC_rel32) + cond);
}
TwoByteOpcodeID setccOpcode(Condition cond)
{
- return (TwoByteOpcodeID)(OP_SETCC + cond);
+ return (TwoByteOpcodeID)(int(OP_SETCC) + cond);
}
typedef enum {
diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h
index f984704023..515285a7dc 100644
--- a/src/3rdparty/masm/stubs/ExecutableAllocator.h
+++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h
@@ -109,7 +109,7 @@ struct ExecutableAllocator {
return adoptRef(new ExecutableMemoryHandle(realAllocator, size));
}
- static void makeWritable(void* addr, size_t size)
+ static bool makeWritable(void* addr, size_t size)
{
quintptr pageSize = WTF::pageSize();
quintptr iaddr = reinterpret_cast<quintptr>(addr);
@@ -125,7 +125,7 @@ struct ExecutableAllocator {
# else
bool hr = VirtualProtectFromApp(addr, size, PAGE_READWRITE, &oldProtect);
if (!hr) {
- Q_UNREACHABLE();
+ return false;
}
# endif
# elif OS(INTEGRITY)
@@ -134,7 +134,7 @@ struct ExecutableAllocator {
int mode = PROT_READ | PROT_WRITE;
if (mprotect(addr, size, mode) != 0) {
perror("mprotect failed in ExecutableAllocator::makeWritable");
- Q_UNREACHABLE();
+ return false;
}
# endif
#else
@@ -142,9 +142,10 @@ struct ExecutableAllocator {
(void)addr; // suppress unused parameter warning
(void)size; // suppress unused parameter warning
#endif
+ return true;
}
- static void makeExecutable(void* addr, size_t size)
+ static bool makeExecutable(void* addr, size_t size)
{
quintptr pageSize = WTF::pageSize();
quintptr iaddr = reinterpret_cast<quintptr>(addr);
@@ -161,7 +162,7 @@ struct ExecutableAllocator {
# else
bool hr = VirtualProtectFromApp(addr, size, PAGE_EXECUTE_READ, &oldProtect);
if (!hr) {
- Q_UNREACHABLE();
+ return false;
}
# endif
# elif OS(INTEGRITY)
@@ -170,7 +171,7 @@ struct ExecutableAllocator {
int mode = PROT_READ | PROT_EXEC;
if (mprotect(addr, size, mode) != 0) {
perror("mprotect failed in ExecutableAllocator::makeExecutable");
- Q_UNREACHABLE();
+ return false;
}
# endif
#else
@@ -180,6 +181,7 @@ struct ExecutableAllocator {
(void)addr; // suppress unused parameter warning
(void)size; // suppress unused parameter warning
#endif
+ return true;
}
QV4::ExecutableAllocator *realAllocator;
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index d59fdcd675..b5c5f6a2b0 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -111,7 +111,11 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable,
(fd == -1 ? MAP_ANON : 0), fd, 0);
if (result == MAP_FAILED)
CRASH();
- madvise(result, bytes, MADV_DONTNEED);
+
+ while (madvise(result, bytes, MADV_DONTNEED)) {
+ if (errno != EAGAIN)
+ CRASH();
+ }
if (fd != -1)
close(fd);
@@ -218,7 +222,12 @@ void OSAllocator::commit(void* address, size_t bytes, bool writable, bool execut
protection |= PROT_EXEC;
if (mprotect(address, bytes, protection))
CRASH();
- madvise(address, bytes, MADV_WILLNEED);
+
+ while (madvise(address, bytes, MADV_WILLNEED)) {
+ if (errno != EAGAIN)
+ break; // We don't have to crash here. MADV_WILLNEED is only advisory
+ }
+
#elif HAVE(MADV_FREE_REUSE)
UNUSED_PARAM(writable);
UNUSED_PARAM(executable);
@@ -238,7 +247,10 @@ void OSAllocator::decommit(void* address, size_t bytes)
// Use PROT_NONE and MAP_LAZY to decommit the pages.
mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
#elif OS(LINUX)
- madvise(address, bytes, MADV_DONTNEED);
+ while (madvise(address, bytes, MADV_DONTNEED)) {
+ if (errno != EAGAIN)
+ CRASH();
+ }
if (mprotect(address, bytes, PROT_NONE))
CRASH();
#elif HAVE(MADV_FREE_REUSE)
diff --git a/src/3rdparty/masm/wtf/PageBlock.cpp b/src/3rdparty/masm/wtf/PageBlock.cpp
index e715ed262a..bc0e8d6f2d 100644
--- a/src/3rdparty/masm/wtf/PageBlock.cpp
+++ b/src/3rdparty/masm/wtf/PageBlock.cpp
@@ -64,6 +64,7 @@ inline size_t systemPageSize()
#endif
+inline namespace hidden {
size_t pageSize()
{
if (!s_pageSize)
@@ -78,5 +79,6 @@ size_t pageMask()
s_pageMask = ~(pageSize() - 1);
return s_pageMask;
}
+}
} // namespace WTF
diff --git a/src/3rdparty/masm/wtf/PageBlock.h b/src/3rdparty/masm/wtf/PageBlock.h
index 09e4048239..d85c39cb33 100644
--- a/src/3rdparty/masm/wtf/PageBlock.h
+++ b/src/3rdparty/masm/wtf/PageBlock.h
@@ -28,8 +28,13 @@
namespace WTF {
+// avoid false positive detection by apple
+// by putting the function inside an inline namespace
+// to obtain different name mangling
+inline namespace hidden {
WTF_EXPORT_PRIVATE size_t pageSize();
WTF_EXPORT_PRIVATE size_t pageMask();
+}
inline bool isPageAligned(void* address) { return !(reinterpret_cast<intptr_t>(address) & (pageSize() - 1)); }
inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); }
inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); }
diff --git a/src/3rdparty/masm/yarr/Yarr.h b/src/3rdparty/masm/yarr/Yarr.h
index ccf78f9880..2955ea7e72 100644
--- a/src/3rdparty/masm/yarr/Yarr.h
+++ b/src/3rdparty/masm/yarr/Yarr.h
@@ -28,6 +28,7 @@
#pragma once
#include <limits.h>
+#include <limits>
#include "YarrErrorCode.h"
namespace JSC { namespace Yarr {
diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp
index 1c8138c66e..28ca8e2629 100644
--- a/src/3rdparty/masm/yarr/YarrJIT.cpp
+++ b/src/3rdparty/masm/yarr/YarrJIT.cpp
@@ -3928,7 +3928,8 @@ public:
}
QV4::generateFunctionTable(nullptr, &codeRef);
- linkBuffer.makeExecutable();
+ if (Q_UNLIKELY(!linkBuffer.makeExecutable()))
+ m_failureReason = JITFailureReason::ExecutableMemoryAllocationFailure;
if (m_failureReason)
codeBlock.setFallBackWithFailureReason(*m_failureReason);
diff --git a/src/imports/labsmodels/qqmltablemodelcolumn_p.h b/src/imports/labsmodels/qqmltablemodelcolumn_p.h
index a18f21ab4f..5f6cf9a728 100644
--- a/src/imports/labsmodels/qqmltablemodelcolumn_p.h
+++ b/src/imports/labsmodels/qqmltablemodelcolumn_p.h
@@ -213,8 +213,6 @@ Q_SIGNALS:
void setSizeHintChanged();
private:
- int mIndex = -1;
-
// We store these in hashes because QQuickTableModel needs string-based lookup in certain situations.
QHash<QString, QJSValue> mGetters;
QHash<QString, QJSValue> mSetters;
diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp
index 4df264f264..dbd56b1996 100644
--- a/src/imports/layouts/qquicklinearlayout.cpp
+++ b/src/imports/layouts/qquicklinearlayout.cpp
@@ -470,8 +470,6 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
if (!isReady())
return;
- ensureLayoutItemsUpdated();
-
qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
const auto refCounter = qScopeGuard([&d] {
--(d->m_recurRearrangeCounter);
@@ -483,6 +481,8 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
return;
}
+ ensureLayoutItemsUpdated();
+
d->m_rearranging = true;
qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
Qt::LayoutDirection visualDir = effectiveLayoutDirection();
diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp
index d6ee4afe84..d127488b82 100644
--- a/src/imports/layouts/qquickstacklayout.cpp
+++ b/src/imports/layouts/qquickstacklayout.cpp
@@ -171,6 +171,7 @@ void QQuickStackLayout::itemChange(QQuickItem::ItemChange change, const QQuickIt
QQuickLayout::itemChange(change, value);
if (change == ItemChildRemovedChange) {
+ m_cachedItemSizeHints.remove(value.item);
invalidate();
} else if (change == ItemChildAddedChange) {
invalidate();
@@ -192,10 +193,8 @@ QSizeF QQuickStackLayout::sizeHint(Qt::SizeHint whichSizeHint) const
maxS = QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity());
const int count = itemCount();
- m_cachedItemSizeHints.resize(count);
for (int i = 0; i < count; ++i) {
- SizeHints &hints = m_cachedItemSizeHints[i];
- QQuickStackLayout::collectItemSizeHints(itemAt(i), hints.array);
+ SizeHints &hints = cachedItemSizeHints(i);
minS = minS.expandedTo(hints.min());
prefS = prefS.expandedTo(hints.pref());
//maxS = maxS.boundedTo(hints.max()); // Can be resized to be larger than any of its items.
@@ -256,11 +255,12 @@ void QQuickStackLayout::setAlignment(QQuickItem * /*item*/, Qt::Alignment /*alig
void QQuickStackLayout::invalidate(QQuickItem *childItem)
{
- const int indexOfChild = indexOf(childItem);
- if (indexOfChild >= 0 && indexOfChild < m_cachedItemSizeHints.count()) {
- m_cachedItemSizeHints[indexOfChild].min() = QSizeF();
- m_cachedItemSizeHints[indexOfChild].pref() = QSizeF();
- m_cachedItemSizeHints[indexOfChild].max() = QSizeF();
+ ensureLayoutItemsUpdated();
+ if (childItem) {
+ SizeHints &hints = m_cachedItemSizeHints[childItem];
+ hints.min() = QSizeF();
+ hints.pref() = QSizeF();
+ hints.max() = QSizeF();
}
for (int i = 0; i < Qt::NSizeHints; ++i)
@@ -294,6 +294,16 @@ void QQuickStackLayout::updateLayoutItems()
}
}
+QQuickStackLayout::SizeHints &QQuickStackLayout::cachedItemSizeHints(int index) const
+{
+ QQuickItem *item = itemAt(index);
+ Q_ASSERT(item);
+ SizeHints &hints = m_cachedItemSizeHints[item]; // will create an entry if it doesn't exist
+ if (!hints.min().isValid())
+ QQuickStackLayout::collectItemSizeHints(item, hints.array);
+ return hints;
+}
+
void QQuickStackLayout::rearrange(const QSizeF &newSize)
{
Q_D(QQuickStackLayout);
@@ -305,7 +315,7 @@ void QQuickStackLayout::rearrange(const QSizeF &newSize)
if (d->currentIndex == -1 || d->currentIndex >= m_cachedItemSizeHints.count())
return;
- QQuickStackLayout::SizeHints &hints = m_cachedItemSizeHints[d->currentIndex];
+ QQuickStackLayout::SizeHints &hints = cachedItemSizeHints(d->currentIndex);
QQuickItem *item = itemAt(d->currentIndex);
Q_ASSERT(item);
item->setPosition(QPointF(0,0)); // ### respect alignment?
diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h
index 0319e259d4..d8feecc11b 100644
--- a/src/imports/layouts/qquickstacklayout_p.h
+++ b/src/imports/layouts/qquickstacklayout_p.h
@@ -95,8 +95,9 @@ private:
QSizeF array[Qt::NSizeHints];
} SizeHints;
- mutable QVector<SizeHints> m_cachedItemSizeHints;
+ mutable QHash<QQuickItem*, SizeHints> m_cachedItemSizeHints;
mutable QSizeF m_cachedSizeHints[Qt::NSizeHints];
+ SizeHints &cachedItemSizeHints(int index) const;
};
class QQuickStackLayoutPrivate : public QQuickLayoutPrivate
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index 8c37f8368f..dcebb21b41 100644
--- a/src/imports/statemachine/signaltransition.cpp
+++ b/src/imports/statemachine/signaltransition.cpp
@@ -166,12 +166,16 @@ void SignalTransition::connectTriggered()
Q_ASSERT(m_bindings.count() == 1);
const QV4::CompiledData::Binding *binding = m_bindings.at(0);
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle();
QV4::Scope scope(jsEngine);
QV4::Scoped<QV4::QObjectMethod> qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal));
- Q_ASSERT(qobjectSignal);
+ if (!qobjectSignal) {
+ m_signalExpression.take(nullptr);
+ return;
+ }
+
QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex());
int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod);
@@ -198,7 +202,7 @@ void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::Executable
return;
}
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script) {
error(binding, SignalTransition::tr("SignalTransition: script expected"));
return;
}
diff --git a/src/imports/testlib/quicktestevent.cpp b/src/imports/testlib/quicktestevent.cpp
index f2d7f93d26..59b7f15946 100644
--- a/src/imports/testlib/quicktestevent.cpp
+++ b/src/imports/testlib/quicktestevent.cpp
@@ -231,8 +231,10 @@ namespace QtQuickTest
QTEST_ASSERT(item);
if (delay == -1 || delay < QTest::defaultMouseDelay())
delay = QTest::defaultMouseDelay();
- if (delay > 0)
+ if (delay > 0) {
QTest::qWait(delay);
+ lastMouseTimestamp += delay;
+ }
QPoint pos;
QQuickItem *sgitem = qobject_cast<QQuickItem *>(item);
@@ -245,6 +247,7 @@ namespace QtQuickTest
stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
QWheelEvent we(pos, window->mapToGlobal(pos), QPoint(0, 0), QPoint(xDelta, yDelta), buttons,
stateKey, Qt::NoScrollPhase, false);
+ we.setTimestamp(++lastMouseTimestamp);
QSpontaneKeyEvent::setSpontaneous(&we); // hmmmm
if (!qApp->notify(window, &we))
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 4d67691771..d2a0e5545b 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -1132,6 +1132,7 @@ QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
, m_startedImageLoading(0)
, m_rhi(nullptr)
, m_apiChecked(false)
+ , m_previousActive(false)
{
setFlag(ItemHasContents);
}
@@ -1960,11 +1961,13 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData
}
if (m_system && m_system->isRunning() && !m_system->isPaused()){
- prepareNextFrame(&node);
+ bool dirty = prepareNextFrame(&node);
if (node) {
update();
- foreach (QSGGeometryNode* n, m_nodes)
- n->markDirty(QSGNode::DirtyGeometry);
+ if (dirty) {
+ foreach (QSGGeometryNode* n, m_nodes)
+ n->markDirty(QSGNode::DirtyGeometry);
+ }
} else if (m_startedImageLoading < 2) {
update();//To call prepareNextFrame() again from the renderThread
}
@@ -1978,7 +1981,7 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData
return node;
}
-void QQuickImageParticle::prepareNextFrame(QSGNode **node)
+bool QQuickImageParticle::prepareNextFrame(QSGNode **node)
{
if (*node == nullptr){//TODO: Staggered loading (as emitted)
buildParticleNodes(node);
@@ -1994,7 +1997,7 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node)
qDebug() << "Total count: " << count;
}
if (*node == nullptr)
- return;
+ return false;
}
qint64 timeStamp = m_system->systemSync(this);
@@ -2015,8 +2018,23 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node)
getState(m_material)->timestamp = time;
break;
}
- foreach (QSGGeometryNode* node, m_nodes)
- node->markDirty(QSGNode::DirtyMaterial);
+
+ bool active = false;
+ for (auto groupId : groupIds()) {
+ if (m_system->groupData[groupId]->isActive()) {
+ active = true;
+ break;
+ }
+ }
+
+ const bool dirty = active || m_previousActive;
+ if (dirty) {
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ m_previousActive = active;
+ return dirty;
}
void QQuickImageParticle::spritesUpdate(qreal time)
diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h
index fdb404861c..b5fdfdadc9 100644
--- a/src/particles/qquickimageparticle_p.h
+++ b/src/particles/qquickimageparticle_p.h
@@ -363,7 +363,7 @@ protected:
void commit(int gIdx, int pIdx) override;
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
- void prepareNextFrame(QSGNode**);
+ bool prepareNextFrame(QSGNode**);
void buildParticleNodes(QSGNode**);
void sceneGraphInvalidated() override;
@@ -460,6 +460,7 @@ private:
int m_startedImageLoading;
QRhi *m_rhi;
bool m_apiChecked;
+ bool m_previousActive;
};
QT_END_NAMESPACE
diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp
index ce254c212d..60d368a2a3 100644
--- a/src/particles/qquickitemparticle.cpp
+++ b/src/particles/qquickitemparticle.cpp
@@ -165,6 +165,7 @@ void QQuickItemParticle::give(QQuickItem *item)
if (data->delegate == item){
m_deletables << item;
data->delegate = nullptr;
+ m_system->groupData[groupId]->kill(data);
return;
}
}
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index ea890d20f4..c74be45c7a 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -205,6 +205,8 @@ public:
QString name();
+ bool isActive() { return freeList.count() > 0; }
+
void setSize(int newSize);
const ID index;
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
index 3e75e39f86..c89fb86aec 100644
--- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
+++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
@@ -324,3 +324,5 @@ bool QPacketProtocolPrivate::readFromDevice(char *buffer, qint64 size)
*/
QT_END_NAMESPACE
+
+#include "moc_qpacketprotocol_p.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
index 3851cdc71f..c28bf0aaff 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
@@ -56,3 +56,5 @@ QQmlDebugService *QQmlDebuggerServiceFactory::create(const QString &key)
}
QT_END_NAMESPACE
+
+#include "moc_qqmldebuggerservicefactory.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
index d435e82390..ec7f91d045 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
@@ -812,9 +812,9 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth
int lineNumber = 0;
QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
- if (oldMethod && oldMethod->d()->function) {
- lineNumber = oldMethod->d()->function->compiledFunction->location.line;
- }
+ if (oldMethod && oldMethod->d()->function)
+ lineNumber = oldMethod->d()->function->compiledFunction->location.line();
+
QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
return true;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 506ecb64bb..b50490e831 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -216,7 +216,8 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
return false;
QV4::ScopedObject scopeObject(scope, engine()->newObject());
- if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) {
+ if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext ||
+ ctxt->d()->type == QV4::Heap::ExecutionContext::Type_BlockContext) {
QStringList names;
Refs collectedRefs;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index 71645579c5..499f060c9c 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -163,7 +163,7 @@ int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, boo
for (QV4Debugger *debugger : qAsConst(m_debuggers))
debugger->addBreakPoint(fileName, lineNumber, condition);
- int id = m_breakPoints.size();
+ const int id = ++m_lastBreakPointId;
m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition));
return id;
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
index 39ac4d4dcb..43baec32d7 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
@@ -93,6 +93,7 @@ private:
};
QHash<int, BreakPoint> m_breakPoints;
+ int m_lastBreakPointId = 0;
bool m_breakOnThrow;
QV4DebugServiceImpl *m_debugService;
};
diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
index 012730902b..1638f18d2b 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
+++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
@@ -407,4 +407,6 @@ GlobalInspector::~GlobalInspector()
QT_END_NAMESPACE
+#include "moc_globalinspector.cpp"
+
#include <globalinspector.moc>
diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
index 4f6cb9364d..2de805d40d 100644
--- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp
@@ -106,3 +106,5 @@ void QDebugMessageServiceImpl::synchronizeTime(const QElapsedTimer &otherTimer)
}
QT_END_NAMESPACE
+
+#include "moc_qdebugmessageservice.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp
index c0b74c74ff..f3990f7e57 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp
@@ -52,3 +52,5 @@ QQmlDebugService *QQmlNativeDebugServiceFactory::create(const QString &key)
}
QT_END_NAMESPACE
+
+#include "moc_qqmlnativedebugservicefactory.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
index 1561777202..af255781bc 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
@@ -66,3 +66,5 @@ void QQmlDebugTranslationServiceImpl::foundTranslationBinding(QQmlTranslationBin
}
QT_END_NAMESPACE
+
+#include "moc_qqmldebugtranslationservice.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
index 5e78539155..e11b8c9776 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
@@ -398,6 +398,14 @@ bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const
void QQmlPreviewFileEngine::load() const
{
+ // We can get here from different threads on different instances of QQmlPreviewFileEngine.
+ // However, there is only one loader per QQmlPreviewFileEngineHandler and it is not thread-safe.
+ // Its content mutex doesn't help us here because we explicitly wait on it in load(), which
+ // causes it to be released. Therefore, lock the load mutex first.
+ // This doesn't cause any deadlocks because the only thread that wakes the loader on the content
+ // mutex never calls load(). It's the QML debug server thread that handles the debug protocol.
+ QMutexLocker loadLocker(m_loader->loadMutex());
+
m_result = m_loader->load(m_absolute);
switch (m_result) {
case QQmlPreviewFileLoader::File:
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
index bb43f75c63..a84ef0dc7b 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
@@ -101,7 +101,7 @@ QQmlPreviewFileLoader::~QQmlPreviewFileLoader() {
QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_path = path;
auto fileIterator = m_fileCache.constFind(path);
@@ -124,19 +124,19 @@ QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
m_entries.clear();
m_contents.clear();
emit request(path);
- m_waitCondition.wait(&m_mutex);
+ m_waitCondition.wait(&m_contentMutex);
return m_result;
}
QByteArray QQmlPreviewFileLoader::contents()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_contents;
}
QStringList QQmlPreviewFileLoader::entries()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_entries;
}
@@ -144,20 +144,20 @@ void QQmlPreviewFileLoader::whitelist(const QUrl &url)
{
const QString path = QQmlFile::urlToLocalFileOrQrc(url);
if (!path.isEmpty()) {
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
}
}
bool QQmlPreviewFileLoader::isBlacklisted(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
return m_blacklist.isBlacklisted(path);
}
void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
m_fileCache[path] = contents;
if (path == m_path) {
@@ -169,7 +169,7 @@ void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents
void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.whitelist(path);
m_directoryCache[path] = entries;
if (path == m_path) {
@@ -181,7 +181,7 @@ void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &en
void QQmlPreviewFileLoader::error(const QString &path)
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_blacklist.blacklist(path);
if (path == m_path) {
m_result = Fallback;
@@ -191,9 +191,11 @@ void QQmlPreviewFileLoader::error(const QString &path)
void QQmlPreviewFileLoader::clearCache()
{
- QMutexLocker locker(&m_mutex);
+ QMutexLocker locker(&m_contentMutex);
m_fileCache.clear();
m_directoryCache.clear();
}
QT_END_NAMESPACE
+
+#include "moc_qqmlpreviewfileloader.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
index 0c55c48c4a..ffda9c0dbf 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
@@ -79,7 +79,9 @@ public:
QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service);
~QQmlPreviewFileLoader();
+ QMutex *loadMutex() { return &m_loadMutex; }
Result load(const QString &file);
+
QByteArray contents();
QStringList entries();
@@ -90,7 +92,8 @@ signals:
void request(const QString &file);
private:
- QMutex m_mutex;
+ QMutex m_loadMutex;
+ QMutex m_contentMutex;
QWaitCondition m_waitCondition;
QThread m_thread;
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
index 8bb3b95e48..0e2521055c 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -456,3 +456,5 @@ void QQmlPreviewHandler::tryCreateObject()
}
QT_END_NAMESPACE
+
+#include "moc_qqmlpreviewhandler.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
index 2e6aaa5858..8e23749a47 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -197,3 +197,5 @@ void QQmlPreviewServiceImpl::forwardFps(const QQmlPreviewHandler::FpsInfo &frame
}
QT_END_NAMESPACE
+
+#include "moc_qqmlpreviewservice.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
index 6ff9805bbe..44d2cab526 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
@@ -54,3 +54,5 @@ QQmlDebugService *QQmlPreviewServiceFactory::create(const QString &key)
}
QT_END_NAMESPACE
+
+#include "moc_qqmlpreviewservicefactory.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index 4702bc3c33..7a1b19ab8b 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -159,3 +159,5 @@ void QQmlProfilerAdapter::receiveData(const QVector<QQmlProfilerData> &new_data,
}
QT_END_NAMESPACE
+
+#include "moc_qqmlprofileradapter.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
index 79a1c82411..0abd54dfb1 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
@@ -175,3 +175,5 @@ void QQuickProfilerAdapter::receiveData(const QVector<QQuickProfilerData> &new_d
}
QT_END_NAMESPACE
+
+#include "moc_qquickprofileradapter.cpp"
diff --git a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro
index d7d24a4d39..4233388a11 100644
--- a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro
+++ b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro
@@ -2,7 +2,7 @@ TARGET = qmldbg_server
QT = qml-private packetprotocol-private
SOURCES += \
- $$PWD/qqmldebugserver.cpp
+ $$PWD/qqmldebugserverfactory.cpp
HEADERS += \
$$PWD/qqmldebugserverfactory.h
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
index 4d68a4508b..19393dbe05 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
@@ -764,4 +764,4 @@ QQmlDebugConnector *QQmlDebugServerFactory::create(const QString &key)
QT_END_NAMESPACE
-#include "qqmldebugserver.moc"
+#include "qqmldebugserverfactory.moc"
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index f3c12bce3f..e15771531a 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -65,6 +65,30 @@ QQmlAnimationTimer::QQmlAnimationTimer() :
{
}
+void QQmlAnimationTimer::unsetJobTimer(QAbstractAnimationJob *animation)
+{
+ if (!animation)
+ return;
+ if (animation->m_timer == this)
+ animation->m_timer = nullptr;
+
+ if (animation->isGroup()) {
+ QAnimationGroupJob *group = static_cast<QAnimationGroupJob *>(animation);
+ for (auto *child = group->firstChild(); child; child = child->nextSibling())
+ unsetJobTimer(child);
+ }
+}
+
+QQmlAnimationTimer::~QQmlAnimationTimer()
+{
+ for (const auto &animation : qAsConst(animations))
+ unsetJobTimer(animation);
+ for (const auto &animation : qAsConst(animationsToStart))
+ unsetJobTimer(animation);
+ for (const auto &animation : qAsConst(runningPauseAnimations))
+ unsetJobTimer(animation);
+}
+
QQmlAnimationTimer *QQmlAnimationTimer::instance(bool create)
{
QQmlAnimationTimer *inst;
@@ -216,6 +240,7 @@ void QQmlAnimationTimer::registerRunningAnimation(QAbstractAnimationJob *animati
void QQmlAnimationTimer::unregisterRunningAnimation(QAbstractAnimationJob *animation)
{
+ unsetJobTimer(animation);
if (animation->userControlDisabled())
return;
@@ -282,8 +307,10 @@ QAbstractAnimationJob::~QAbstractAnimationJob()
Q_ASSERT(m_state == Stopped);
if (oldState == Running) {
- Q_ASSERT(QQmlAnimationTimer::instance() == m_timer);
- m_timer->unregisterAnimation(this);
+ if (m_timer) {
+ Q_ASSERT(QQmlAnimationTimer::instance(false) == m_timer);
+ m_timer->unregisterAnimation(this);
+ }
}
Q_ASSERT(!m_hasRegisteredTimer);
}
@@ -308,8 +335,9 @@ void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
if (m_loopCount == 0)
return;
- if (!m_timer)
- m_timer = QQmlAnimationTimer::instance();
+ if (!m_timer) // don't create a timer just to stop the animation
+ m_timer = QQmlAnimationTimer::instance(newState != Stopped);
+ Q_ASSERT(m_timer || newState == Stopped);
State oldState = m_state;
int oldCurrentTime = m_currentTime;
@@ -337,8 +365,9 @@ void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
if (oldState == Running) {
if (newState == Paused && m_hasRegisteredTimer)
m_timer->ensureTimerUpdate();
- //the animation, is not running any more
- m_timer->unregisterAnimation(this);
+ // the animation is not running any more
+ if (m_timer)
+ m_timer->unregisterAnimation(this);
} else if (newState == Running) {
m_timer->registerAnimation(this, isTopLevel);
}
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 9490070246..1010f0f392 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -207,6 +207,8 @@ private:
QQmlAnimationTimer();
public:
+ ~QQmlAnimationTimer(); // must be destructible by QThreadStorage
+
static QQmlAnimationTimer *instance();
static QQmlAnimationTimer *instance(bool create);
@@ -252,6 +254,7 @@ private:
void registerRunningAnimation(QAbstractAnimationJob *animation);
void unregisterRunningAnimation(QAbstractAnimationJob *animation);
+ void unsetJobTimer(QAbstractAnimationJob *animation);
int closestPauseAnimationTimeToFinish();
};
diff --git a/src/qml/animations/qanimationgroupjob.cpp b/src/qml/animations/qanimationgroupjob.cpp
index 66599561fc..60b3003d0a 100644
--- a/src/qml/animations/qanimationgroupjob.cpp
+++ b/src/qml/animations/qanimationgroupjob.cpp
@@ -47,9 +47,45 @@ QAnimationGroupJob::QAnimationGroupJob()
m_isGroup = true;
}
+void QAnimationGroupJob::ungroupChild(QAbstractAnimationJob *animation)
+{
+ Q_ASSERT(animation);
+ Q_ASSERT(animation->m_group == this);
+ QAbstractAnimationJob *prev = animation->previousSibling();
+ QAbstractAnimationJob *next = animation->nextSibling();
+
+ if (prev)
+ prev->m_nextSibling = next;
+ else
+ m_firstChild = next;
+
+ if (next)
+ next->m_previousSibling = prev;
+ else
+ m_lastChild = prev;
+
+ animation->m_previousSibling = nullptr;
+ animation->m_nextSibling = nullptr;
+
+ animation->m_group = nullptr;
+}
+
+void QAnimationGroupJob::handleAnimationRemoved(QAbstractAnimationJob *animation)
+{
+ resetUncontrolledAnimationFinishTime(animation);
+ if (!firstChild()) {
+ m_currentTime = 0;
+ stop();
+ }
+}
+
QAnimationGroupJob::~QAnimationGroupJob()
{
- clear();
+ while (QAbstractAnimationJob *animation = firstChild()) {
+ ungroupChild(animation);
+ handleAnimationRemoved(animation);
+ delete animation;
+ }
}
void QAnimationGroupJob::topLevelAnimationLoopChanged()
@@ -96,25 +132,9 @@ void QAnimationGroupJob::prependAnimation(QAbstractAnimationJob *animation)
void QAnimationGroupJob::removeAnimation(QAbstractAnimationJob *animation)
{
- Q_ASSERT(animation);
- Q_ASSERT(animation->m_group == this);
QAbstractAnimationJob *prev = animation->previousSibling();
QAbstractAnimationJob *next = animation->nextSibling();
-
- if (prev)
- prev->m_nextSibling = next;
- else
- m_firstChild = next;
-
- if (next)
- next->m_previousSibling = prev;
- else
- m_lastChild = prev;
-
- animation->m_previousSibling = nullptr;
- animation->m_nextSibling = nullptr;
-
- animation->m_group = nullptr;
+ ungroupChild(animation);
animationRemoved(animation, prev, next);
}
@@ -154,11 +174,7 @@ void QAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *an
void QAnimationGroupJob::animationRemoved(QAbstractAnimationJob* anim, QAbstractAnimationJob* , QAbstractAnimationJob* )
{
- resetUncontrolledAnimationFinishTime(anim);
- if (!firstChild()) {
- m_currentTime = 0;
- stop();
- }
+ handleAnimationRemoved(anim);
}
void QAnimationGroupJob::debugChildren(QDebug d) const
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h
index a27c9195dd..6a0941db65 100644
--- a/src/qml/animations/qanimationgroupjob_p.h
+++ b/src/qml/animations/qanimationgroupjob_p.h
@@ -91,6 +91,9 @@ protected:
void debugChildren(QDebug d) const;
private:
+ void ungroupChild(QAbstractAnimationJob *animation);
+ void handleAnimationRemoved(QAbstractAnimationJob *animation);
+
//definition
QAbstractAnimationJob *m_firstChild = nullptr;
QAbstractAnimationJob *m_lastChild = nullptr;
diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h
index 83cf3b246f..2b7bda3123 100644
--- a/src/qml/animations/qanimationjobutil_p.h
+++ b/src/qml/animations/qanimationjobutil_p.h
@@ -70,7 +70,7 @@ struct SelfDeletable {
// \param func statements or functions that to be executed under test.
// \param action post process if p was deleted under test.
#define ACTION_IF_DELETED(p, func, action) \
-{ \
+do { \
static_assert(std::is_same<decltype((p)->m_selfDeletable), SelfDeletable>::value, "m_selfDeletable must be SelfDeletable");\
bool *prevWasDeleted = (p)->m_selfDeletable.m_wasDeleted; \
bool wasDeleted = false; \
@@ -82,7 +82,7 @@ struct SelfDeletable {
{action;} \
} \
(p)->m_selfDeletable.m_wasDeleted = prevWasDeleted; \
-}
+} while (false)
#define RETURN_IF_DELETED(func) \
ACTION_IF_DELETED(this, func, return)
diff --git a/src/qml/animations/qsequentialanimationgroupjob.cpp b/src/qml/animations/qsequentialanimationgroupjob.cpp
index dc57444b32..1d19bbf79d 100644
--- a/src/qml/animations/qsequentialanimationgroupjob.cpp
+++ b/src/qml/animations/qsequentialanimationgroupjob.cpp
@@ -338,7 +338,7 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat
if (m_direction == Forward) {
// set the current animation to be the next one
if (m_currentAnimation->nextSibling())
- setCurrentAnimation(m_currentAnimation->nextSibling());
+ RETURN_IF_DELETED(setCurrentAnimation(m_currentAnimation->nextSibling()));
for (QAbstractAnimationJob *a = animation->nextSibling(); a; a = a->nextSibling()) {
int dur = a->duration();
@@ -353,7 +353,7 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat
} else {
// set the current animation to be the previous one
if (m_currentAnimation->previousSibling())
- setCurrentAnimation(m_currentAnimation->previousSibling());
+ RETURN_IF_DELETED(setCurrentAnimation(m_currentAnimation->previousSibling()));
for (QAbstractAnimationJob *a = animation->previousSibling(); a; a = a->previousSibling()) {
int dur = a->duration();
@@ -374,12 +374,12 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat
void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim)
{
if (m_currentAnimation == nullptr)
- setCurrentAnimation(firstChild()); // initialize the current animation
+ RETURN_IF_DELETED(setCurrentAnimation(firstChild())); // initialize the current animation
if (m_currentAnimation == anim->nextSibling()
&& m_currentAnimation->currentTime() == 0 && m_currentAnimation->currentLoop() == 0) {
//in this case we simply insert the animation before the current one has actually started
- setCurrentAnimation(anim);
+ RETURN_IF_DELETED(setCurrentAnimation(anim));
}
// TODO
@@ -398,11 +398,11 @@ void QSequentialAnimationGroupJob::animationRemoved(QAbstractAnimationJob *anim,
bool removingCurrent = anim == m_currentAnimation;
if (removingCurrent) {
if (next)
- setCurrentAnimation(next); //let's try to take the next one
+ RETURN_IF_DELETED(setCurrentAnimation(next)); //let's try to take the next one
else if (prev)
- setCurrentAnimation(prev);
+ RETURN_IF_DELETED(setCurrentAnimation(prev));
else// case all animations were removed
- setCurrentAnimation(nullptr);
+ RETURN_IF_DELETED(setCurrentAnimation(nullptr));
}
// duration of the previous animations up to the current animation
diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri
index b333c0f6d9..b824812d27 100644
--- a/src/qml/common/common.pri
+++ b/src/qml/common/common.pri
@@ -1,8 +1,8 @@
!build_pass {
# Create a header containing a hash that describes this library. For a
# released version of Qt, we'll use the .tag file that is updated by git
- # archive with the commit hash. For unreleased versions, we'll ask git
- # describe. Note that it won't update unless qmake is run again, even if
+ # archive with the tree hash. For unreleased versions, we'll ask git
+ # rev-parse. Note that it won't update unless qmake is run again, even if
# the commit change also changed something in this library.
tagFile = $$PWD/../../.tag
tag =
@@ -10,7 +10,7 @@
tag = $$cat($$tagFile, singleline)
QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile
}
- !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
+ !equals(tag, "$${LITERAL_DOLLAR}Format:%T$${LITERAL_DOLLAR}") {
QML_COMPILE_HASH = $$tag
} else:exists($$PWD/../../.git) {
commit = $$system(git rev-parse HEAD)
diff --git a/src/qml/common/qqmljsfixedpoolarray_p.h b/src/qml/common/qqmljsfixedpoolarray_p.h
index b65b994d6c..15a8cd6878 100644
--- a/src/qml/common/qqmljsfixedpoolarray_p.h
+++ b/src/qml/common/qqmljsfixedpoolarray_p.h
@@ -86,7 +86,7 @@ public:
if (QTypeInfo<T>::isComplex) {
for (int i = 0; i < count; ++i)
new (data + i) T(vector.at(i));
- } else {
+ } else if (count) {
memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
}
}
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index a5a1cf8969..413edead2a 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -120,18 +120,35 @@ struct TableIterator
struct Location
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 20> line;
- quint32_le_bitfield<20, 12> column;
- };
-
- Location() : _dummy(0) { }
+ Location() : m_data(QSpecialIntegerBitfieldZero) {}
+ Location(quint32 l, quint32 c) : Location()
+ {
+ m_data.set<LineField>(l);
+ m_data.set<ColumnField>(c);
+ Q_ASSERT(m_data.get<LineField>() == l);
+ Q_ASSERT(m_data.get<ColumnField>() == c);
+ }
inline bool operator<(const Location &other) const {
- return line < other.line ||
- (line == other.line && column < other.column);
+ return m_data.get<LineField>() < other.m_data.get<LineField>()
+ || (m_data.get<LineField>() == other.m_data.get<LineField>()
+ && m_data.get<ColumnField>() < other.m_data.get<ColumnField>());
}
+
+ void set(quint32 line, quint32 column)
+ {
+ m_data.set<LineField>(line);
+ m_data.set<ColumnField>(column);
+ }
+
+ quint32 line() const { return m_data.get<LineField>(); }
+ quint32 column() const { return m_data.get<ColumnField>(); }
+
+private:
+ using LineField = quint32_le_bitfield_member<0, 20>;
+ using ColumnField = quint32_le_bitfield_member<20, 12>;
+
+ quint32_le_bitfield_union<LineField, ColumnField> m_data;
};
static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -145,13 +162,21 @@ struct RegExp
RegExp_Unicode = 0x08,
RegExp_Sticky = 0x10
};
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 5> flags;
- quint32_le_bitfield<5, 27> stringIndex;
- };
- RegExp() : _dummy(0) { }
+ RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
+ RegExp(quint32 flags, quint32 stringIndex) : RegExp()
+ {
+ m_data.set<FlagsField>(flags);
+ m_data.set<StringIndexField>(stringIndex);
+ }
+
+ quint32 flags() const { return m_data.get<FlagsField>(); }
+ quint32 stringIndex() const { return m_data.get<StringIndexField>(); }
+
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 5>;
+ using StringIndexField = quint32_le_bitfield_member<5, 27>;
+ quint32_le_bitfield_union<FlagsField, StringIndexField> m_data;
};
static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -164,25 +189,40 @@ struct Lookup
Type_QmlContextPropertyGetter = 3
};
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 4> type_and_flags;
- quint32_le_bitfield<4, 28> nameIndex;
- };
+ quint32 typeAndFlags() const { return m_data.get<TypeAndFlagsField>(); }
+ quint32 nameIndex() const { return m_data.get<NameIndexField>(); }
- Lookup() : _dummy(0) { }
+ Lookup() : m_data(QSpecialIntegerBitfieldZero) {}
+ Lookup(Type type, quint32 nameIndex) : Lookup()
+ {
+ m_data.set<TypeAndFlagsField>(type);
+ m_data.set<NameIndexField>(nameIndex);
+ }
+
+private:
+ using TypeAndFlagsField = quint32_le_bitfield_member<0, 4>;
+ using NameIndexField = quint32_le_bitfield_member<4, 28>;
+ quint32_le_bitfield_union<TypeAndFlagsField, NameIndexField> m_data;
};
static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 31> nameOffset;
- quint32_le_bitfield<31, 1> isAccessor;
- };
+ JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {}
- JSClassMember() : _dummy(0) { }
+ void set(quint32 nameOffset, bool isAccessor)
+ {
+ m_data.set<NameOffsetField>(nameOffset);
+ m_data.set<IsAccessorField>(isAccessor ? 1 : 0);
+ }
+
+ quint32 nameOffset() const { return m_data.get<NameOffsetField>(); }
+ bool isAccessor() const { return m_data.get<IsAccessorField>() != 0; }
+
+private:
+ using NameOffsetField = quint32_le_bitfield_member<0, 31>;
+ using IsAccessorField = quint32_le_bitfield_member<31, 1>;
+ quint32_le_bitfield_union<NameOffsetField, IsAccessorField> m_data;
};
static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -265,11 +305,26 @@ enum class BuiltinType : unsigned int {
struct ParameterType
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 1> indexIsBuiltinType;
- quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType;
- };
+ void set(bool indexIsBuiltinType, quint32 typeNameIndexOrBuiltinType)
+ {
+ m_data.set<IndexIsBuiltinTypeField>(indexIsBuiltinType ? 1 : 0);
+ m_data.set<TypeNameIndexOrBuiltinTypeField>(typeNameIndexOrBuiltinType);
+ }
+
+ bool indexIsBuiltinType() const
+ {
+ return m_data.get<IndexIsBuiltinTypeField>() != 0;
+ }
+
+ quint32 typeNameIndexOrBuiltinType() const
+ {
+ return m_data.get<TypeNameIndexOrBuiltinTypeField>();
+ }
+
+private:
+ using IndexIsBuiltinTypeField = quint32_le_bitfield_member<0, 1>;
+ using TypeNameIndexOrBuiltinTypeField = quint32_le_bitfield_member<1, 31>;
+ quint32_le_bitfield_union<IndexIsBuiltinTypeField, TypeNameIndexOrBuiltinTypeField> m_data;
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -448,7 +503,7 @@ struct Binding
{
quint32_le propertyNameIndex;
- enum ValueType : unsigned int {
+ enum Type : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
@@ -462,7 +517,7 @@ struct Binding
Type_GroupProperty
};
- enum Flags : unsigned int {
+ enum Flag : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
@@ -474,11 +529,20 @@ struct Binding
IsCustomParserBinding = 0x100,
IsFunctionExpression = 0x200
};
+ Q_DECLARE_FLAGS(Flags, Flag);
+
+ using FlagsField = quint32_le_bitfield_member<0, 16>;
+ using TypeField = quint32_le_bitfield_member<16, 16>;
+ quint32_le_bitfield_union<FlagsField, TypeField> flagsAndType;
+
+ void clearFlags() { flagsAndType.set<FlagsField>(0); }
+ void setFlag(Flag flag) { flagsAndType.set<FlagsField>(flagsAndType.get<FlagsField>() | flag); }
+ bool hasFlag(Flag flag) const { return Flags(flagsAndType.get<FlagsField>()) & flag; }
+ Flags flags() const { return Flags(flagsAndType.get<FlagsField>()); }
+
+ void setType(Type type) { flagsAndType.set<TypeField>(type); }
+ Type type() const { return Type(flagsAndType.get<TypeField>()); }
- union {
- quint32_le_bitfield<0, 16> flags;
- quint32_le_bitfield<16, 16> type;
- };
union {
bool b;
quint32_le constantValueIndex;
@@ -492,23 +556,29 @@ struct Binding
Location location;
Location valueLocation;
+ bool hasSignalHandlerBindingFlag() const
+ {
+ const Flags bindingFlags = flags();
+ return (bindingFlags & IsSignalHandlerExpression || bindingFlags & IsSignalHandlerObject);
+ }
+
bool isValueBinding() const
{
- if (type == Type_AttachedProperty
- || type == Type_GroupProperty)
+ switch (type()) {
+ case Type_AttachedProperty:
+ case Type_GroupProperty:
return false;
- if (flags & IsSignalHandlerExpression
- || flags & IsSignalHandlerObject)
- return false;
- return true;
+ default:
+ return !hasSignalHandlerBindingFlag();
+ }
}
- bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); }
- bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); }
+ bool isValueBindingNoAlias() const { return isValueBinding() && !hasFlag(IsBindingToAlias); }
+ bool isValueBindingToAlias() const { return isValueBinding() && hasFlag(IsBindingToAlias); }
bool isSignalHandler() const
{
- if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) {
+ if (hasSignalHandlerBindingFlag()) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isAttachedProperty());
Q_ASSERT(!isGroupProperty());
@@ -519,7 +589,7 @@ struct Binding
bool isAttachedProperty() const
{
- if (type == Type_AttachedProperty) {
+ if (type() == Type_AttachedProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isGroupProperty());
@@ -530,7 +600,7 @@ struct Binding
bool isGroupProperty() const
{
- if (type == Type_GroupProperty) {
+ if (type() == Type_GroupProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isAttachedProperty());
@@ -539,7 +609,7 @@ struct Binding
return false;
}
- bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
+ bool isFunctionExpression() const { return hasFlag(IsFunctionExpression); }
//reverse of Lexer::singleEscape()
static QString escapedString(const QString &string)
@@ -584,16 +654,19 @@ struct Binding
return tmp;
}
- bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
+ bool isTranslationBinding() const
+ {
+ const Binding::Type bindingType = type();
+ return bindingType == Type_Translation || bindingType == Type_TranslationById;
+ }
+ bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); }
bool valueAsBoolean() const
{
- if (type == Type_Boolean)
+ if (type() == Type_Boolean)
return value.b;
return false;
}
-
};
static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -666,32 +739,57 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected
struct Property
{
- quint32_le nameIndex;
- union {
- quint32_le_bitfield<0, 28> builtinTypeOrTypeNameIndex;
- quint32_le_bitfield<28, 1> isRequired;
- quint32_le_bitfield<29, 1> isBuiltinType;
- quint32_le_bitfield<30, 1> isList;
- quint32_le_bitfield<31, 1> isReadOnly;
- };
+private:
+ using BuiltinTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>;
+ using IsRequiredField = quint32_le_bitfield_member<28, 1>;
+ using IsBuiltinTypeField = quint32_le_bitfield_member<29, 1>;
+ using IsListField = quint32_le_bitfield_member<30, 1>;
+ using IsReadOnlyField = quint32_le_bitfield_member<31, 1>;
+public:
+ quint32_le nameIndex;
+ quint32_le_bitfield_union<
+ BuiltinTypeOrTypeNameIndexField,
+ IsRequiredField,
+ IsBuiltinTypeField,
+ IsListField,
+ IsReadOnlyField> data;
Location location;
void setBuiltinType(BuiltinType t)
{
- builtinTypeOrTypeNameIndex = static_cast<quint32>(t);
- isBuiltinType = true;
+ data.set<BuiltinTypeOrTypeNameIndexField>(static_cast<quint32>(t));
+ data.set<IsBuiltinTypeField>(true);
}
+
BuiltinType builtinType() const {
- if (isBuiltinType)
- return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex));
+ if (data.get<IsBuiltinTypeField>() != 0)
+ return BuiltinType(data.get<BuiltinTypeOrTypeNameIndexField>());
return BuiltinType::InvalidBuiltin;
}
+
void setCustomType(int nameIndex)
{
- builtinTypeOrTypeNameIndex = nameIndex;
- isBuiltinType = false;
+ data.set<BuiltinTypeOrTypeNameIndexField>(nameIndex);
+ data.set<IsBuiltinTypeField>(false);
}
+
+ int customType() const
+ {
+ return data.get<IsBuiltinTypeField>() ? -1 : data.get<BuiltinTypeOrTypeNameIndexField>();
+ }
+
+ bool isBuiltinType() const { return data.get<IsBuiltinTypeField>(); }
+ uint builtinTypeOrTypeNameIndex() const { return data.get<BuiltinTypeOrTypeNameIndexField>(); }
+
+ bool isList() const { return data.get<IsListField>(); }
+ void setIsList(bool isList) { data.set<IsListField>(isList); }
+
+ bool isRequired() const { return data.get<IsRequiredField>(); }
+ void setIsRequired(bool isRequired) { data.set<IsRequiredField>(isRequired); }
+
+ bool isReadOnly() const { return data.get<IsReadOnlyField>(); }
+ void setIsReadOnly(bool isReadOnly) { data.set<IsReadOnlyField>(isReadOnly); }
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -702,20 +800,31 @@ struct RequiredPropertyExtraData {
static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias {
- enum Flags : unsigned int {
+private:
+ using NameIndexField = quint32_le_bitfield_member<0, 29>;
+ using FlagsField = quint32_le_bitfield_member<29, 3>;
+
+ // object id index (in QQmlContextData::idValues)
+ using TargetObjectIdField = quint32_le_bitfield_member<0, 31>;
+ using AliasToLocalAliasField = quint32_le_bitfield_member<31, 1>;
+
+public:
+
+ enum Flag : unsigned int {
IsReadOnly = 0x1,
Resolved = 0x2,
AliasPointsToPointerObject = 0x4
};
- union {
- quint32_le_bitfield<0, 29> nameIndex;
- quint32_le_bitfield<29, 3> flags;
- };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ quint32_le_bitfield_union<NameIndexField, FlagsField> nameIndexAndFlags;
+
union {
quint32_le idIndex; // string index
- quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
- quint32_le_bitfield<31, 1> aliasToLocalAlias;
+ quint32_le_bitfield_union<TargetObjectIdField, AliasToLocalAliasField>
+ targetObjectIdAndAliasToLocalAlias;
};
+
union {
quint32_le propertyNameIndex; // string index
qint32_le encodedMetaPropertyIndex;
@@ -724,16 +833,67 @@ struct Alias {
Location location;
Location referenceLocation;
- bool isObjectAlias() const {
- Q_ASSERT(flags & Resolved);
+ bool hasFlag(Flag flag) const
+ {
+ return nameIndexAndFlags.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ nameIndexAndFlags.set<FlagsField>(nameIndexAndFlags.get<FlagsField>() | flag);
+ }
+
+ void clearFlags()
+ {
+ nameIndexAndFlags.set<FlagsField>(0);
+ }
+
+ quint32 nameIndex() const
+ {
+ return nameIndexAndFlags.get<NameIndexField>();
+ }
+
+ void setNameIndex(quint32 nameIndex)
+ {
+ nameIndexAndFlags.set<NameIndexField>(nameIndex);
+ }
+
+ bool isObjectAlias() const
+ {
+ Q_ASSERT(hasFlag(Resolved));
return encodedMetaPropertyIndex == -1;
}
+
+ bool isAliasToLocalAlias() const
+ {
+ return targetObjectIdAndAliasToLocalAlias.get<AliasToLocalAliasField>();
+ }
+
+ void setIsAliasToLocalAlias(bool isAliasToLocalAlias)
+ {
+ targetObjectIdAndAliasToLocalAlias.set<AliasToLocalAliasField>(isAliasToLocalAlias);
+ }
+
+ quint32 targetObjectId() const
+ {
+ return targetObjectIdAndAliasToLocalAlias.get<TargetObjectIdField>();
+ }
+
+ void setTargetObjectId(quint32 targetObjectId)
+ {
+ targetObjectIdAndAliasToLocalAlias.set<TargetObjectIdField>(targetObjectId);
+ }
};
static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Object
{
- enum Flags : unsigned int {
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 15>;
+ using DefaultPropertyIsAliasField = quint32_le_bitfield_member<15, 1>;
+ using IdField = quint32_le_bitfield_member<16, 16, qint32>;
+public:
+ enum Flag : unsigned int {
NoFlag = 0x0,
IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
HasDeferredBindings = 0x2, // any of the bindings are deferred
@@ -741,17 +901,15 @@ struct Object
IsInlineComponentRoot = 0x8,
InPartOfInlineComponent = 0x10
};
+ Q_DECLARE_FLAGS(Flags, Flag);
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
quint32_le inheritedTypeNameIndex;
quint32_le idNameIndex;
- union {
- quint32_le_bitfield<0, 15> flags;
- quint32_le_bitfield<15, 1> defaultPropertyIsAlias;
- qint32_le_bitfield<16, 16> id;
- };
+ quint32_le_bitfield_union<FlagsField, DefaultPropertyIsAliasField, IdField>
+ flagsAndDefaultPropertyIsAliasAndId;
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
quint16_le nFunctions;
quint16_le nProperties;
@@ -780,6 +938,48 @@ struct Object
// InlineComponent[]
// RequiredPropertyExtraData[]
+ Flags flags() const
+ {
+ return Flags(flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>());
+ }
+
+ bool hasFlag(Flag flag) const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(
+ flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() | flag);
+ }
+
+ void setFlags(Flags flags)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(flags);
+ }
+
+ bool hasAliasAsDefaultProperty() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<DefaultPropertyIsAliasField>();
+ }
+
+ void setHasAliasAsDefaultProperty(bool defaultAlias)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<DefaultPropertyIsAliasField>(defaultAlias);
+ }
+
+ qint32 objectId() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<IdField>();
+ }
+
+ void setObjectId(qint32 id)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<IdField>(id);
+ }
+
+
static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
{
return ( sizeof(Object)
@@ -1129,8 +1329,8 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto prop = obj->propertiesBegin();
auto const propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
- if (!prop->isBuiltinType) {
- TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location);
+ if (!prop->isBuiltinType()) {
+ TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex(), prop->location);
r.errorWhenNotFound = true;
}
}
@@ -1138,7 +1338,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto binding = obj->bindingsBegin();
auto const bindingEnd = obj->bindingsEnd();
for ( ; binding != bindingEnd; ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty)
this->add(binding->propertyNameIndex, binding->location);
}
@@ -1330,7 +1530,8 @@ public:
template<typename Char>
bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
{
- auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; });
+ const quint32_le oldFlags = mutableFlags();
+ auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; });
mutableFlags() |= temporaryFlags;
return writer(data<Char>(), size());
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 89f99e21cd..36269691fc 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -76,7 +76,7 @@ void Object::simplifyRequiredProperties() {
for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) {
auto requiredIt = required.find(it->nameIndex);
if (requiredIt != required.end()) {
- it->isRequired = true;
+ it->setIsRequired(true);
required.erase(requiredIt);
}
}
@@ -106,20 +106,18 @@ bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::J
bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex)
{
- paramType->indexIsBuiltinType = false;
- paramType->typeNameIndexOrBuiltinType = 0;
const QString typeName = stringGenerator->stringForIndex(typeNameIndex);
auto builtinType = stringToBuiltinType(typeName);
if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) {
- if (typeName.isEmpty() || !typeName.at(0).isUpper())
+ if (typeName.isEmpty() || !typeName.at(0).isUpper()) {
+ paramType->set(false, 0);
return false;
- paramType->indexIsBuiltinType = false;
- paramType->typeNameIndexOrBuiltinType = typeNameIndex;
+ }
Q_ASSERT(quint32(typeNameIndex) < (1u << 31));
+ paramType->set(false, typeNameIndex);
} else {
- paramType->indexIsBuiltinType = true;
- paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType);
Q_ASSERT(quint32(builtinType) < (1u << 31));
+ paramType->set(true, static_cast<quint32>(builtinType));
}
return true;
}
@@ -173,9 +171,7 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
{
inheritedTypeNameIndex = typeNameIndex;
- location.line = loc.startLine;
- location.column = loc.startColumn;
-
+ location.set(loc.startLine, loc.startColumn);
idNameIndex = idIndex;
id = -1;
indexOfDefaultPropertyOrAlias = -1;
@@ -198,8 +194,8 @@ QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &il
QSet<int> functionNames;
for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
Function *f = functionit.ptr;
- errorLocation->startLine = f->location.line;
- errorLocation->startColumn = f->location.column;
+ errorLocation->startLine = f->location.line();
+ errorLocation->startColumn = f->location.column();
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
@@ -280,7 +276,7 @@ QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefau
target = this;
auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){
- return targetAlias.nameIndex == alias->nameIndex;
+ return targetAlias.nameIndex() == alias->nameIndex();
});
if (aliasWithSameName != target->aliases->end())
return tr("Duplicate alias name");
@@ -323,13 +319,17 @@ void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraDat
QString Object::appendBinding(Binding *b, bool isListBinding)
{
const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
- if (!isListBinding && !bindingToDefaultProperty
- && b->type != QV4::CompiledData::Binding::Type_GroupProperty
- && b->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ if (!isListBinding
+ && !bindingToDefaultProperty
+ && b->type() != QV4::CompiledData::Binding::Type_GroupProperty
+ && b->type() != QV4::CompiledData::Binding::Type_AttachedProperty
+ && !b->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
Binding *existing = findBinding(b->propertyNameIndex);
- if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment))
+ if (existing
+ && existing->isValueBinding() == b->isValueBinding()
+ && !existing->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
return tr("Property value set multiple times");
+ }
}
if (bindingToDefaultProperty)
insertSorted(b);
@@ -397,8 +397,7 @@ void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString
import->type = QV4::CompiledData::Import::ImportScript;
import->uriIndex = jsGenerator->registerString(jsfile);
import->qualifierIndex = jsGenerator->registerString(module);
- import->location.line = lineNumber;
- import->location.column = column;
+ import->location.set(lineNumber, column);
document->imports << import;
}
@@ -413,8 +412,7 @@ void ScriptDirectivesCollector::importModule(const QString &uri, const QString &
import->majorVersion = vmaj;
import->minorVersion = vmin;
import->qualifierIndex = jsGenerator->registerString(module);
- import->location.line = lineNumber;
- import->location.column = column;
+ import->location.set(lineNumber, column);
document->imports << import;
}
@@ -448,7 +446,9 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen
errors << m;
}
- return false;
+
+ if (!errors.isEmpty() || !parseResult)
+ return false;
}
program = parser.ast();
Q_ASSERT(program);
@@ -573,8 +573,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast)
inlineComponent->nameIndex = registerString(ast->name.toString());
inlineComponent->objectIndex = idx;
auto location = ast->firstSourceLocation();
- inlineComponent->location.line = location.startLine;
- inlineComponent->location.column = location.startColumn;
+ inlineComponent->location.set(location.startLine, location.startColumn);
_object->appendInlineComponent(inlineComponent);
return false;
}
@@ -770,8 +769,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
import->minorVersion = -1;
}
- import->location.line = node->importToken.startLine;
- import->location.column = node->importToken.startColumn;
+ import->location.set(node->importToken.startLine, node->importToken.startColumn);
import->uriIndex = registerString(uri);
@@ -799,8 +797,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node)
return false;
}
- pragma->location.line = node->pragmaToken.startLine;
- pragma->location.column = node->pragmaToken.startColumn;
+ pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn);
_pragmas.append(pragma);
return false;
@@ -833,8 +830,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
if (enumName.at(0).isLower())
COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter"));
- enumeration->location.line = node->enumToken.startLine;
- enumeration->location.column = node->enumToken.startColumn;
+ enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn);
enumeration->enumValues = New<PoolList<EnumValue>>();
@@ -853,8 +849,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range"));
enumValue->value = e->value;
- enumValue->location.line = e->memberToken.startLine;
- enumValue->location.column = e->memberToken.startColumn;
+ enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn);
enumeration->enumValues->append(enumValue);
e = e->next;
@@ -878,8 +873,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
signal->nameIndex = registerString(signalName);
QQmlJS::SourceLocation loc = node->typeToken;
- signal->location.line = loc.startLine;
- signal->location.column = loc.startColumn;
+ signal->location.set(loc.startLine, loc.startColumn);
signal->parameters = New<PoolList<Parameter> >();
@@ -928,8 +922,8 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
const QStringRef &name = node->name;
Property *property = New<Property>();
- property->isReadOnly = node->isReadonlyMember;
- property->isRequired = node->isRequired;
+ property->setIsReadOnly(node->isReadonlyMember);
+ property->setIsRequired(node->isRequired);
QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType);
bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin;
@@ -941,7 +935,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
property->setCustomType(registerString(memberType));
if (typeModifier == QLatin1String("list")) {
- property->isList = true;
+ property->setIsList(true);
} else if (!typeModifier.isEmpty()) {
recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
return false;
@@ -961,8 +955,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
property->nameIndex = registerString(propName);
QQmlJS::SourceLocation loc = node->firstSourceLocation();
- property->location.line = loc.startLine;
- property->location.column = loc.startColumn;
+ property->location.set(loc.startLine, loc.startColumn);
QQmlJS::SourceLocation errorLocation;
QString error;
@@ -1006,8 +999,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
Function *f = New<Function>();
QQmlJS::SourceLocation loc = funDecl->identifierToken;
- f->location.line = loc.startLine;
- f->location.column = loc.startColumn;
+ f->location.set(loc.startLine, loc.startColumn);
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
@@ -1087,26 +1079,25 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::SourceLocation &first, const QQmlJ
void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
{
QQmlJS::SourceLocation loc = statement->firstSourceLocation();
- binding->valueLocation.line = loc.startLine;
- binding->valueLocation.column = loc.startColumn;
- binding->type = QV4::CompiledData::Binding::Type_Invalid;
- if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
- binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
+ binding->valueLocation.set(loc.startLine, loc.startColumn);
+ binding->setType(QV4::CompiledData::Binding::Type_Invalid);
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly())
+ binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration);
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (exprStmt) {
QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = registerString(lit->value.toString());
} else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) {
- binding->type = QV4::CompiledData::Binding::Type_Boolean;
+ binding->setType(QV4::CompiledData::Binding::Type_Boolean);
binding->value.b = true;
} else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) {
- binding->type = QV4::CompiledData::Binding::Type_Boolean;
+ binding->setType(QV4::CompiledData::Binding::Type_Boolean);
binding->value.b = false;
} else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value));
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
@@ -1115,21 +1106,21 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
// below.
}
} else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
- binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression;
+ binding->setFlag(QV4::CompiledData::Binding::IsFunctionExpression);
} else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value));
}
} else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_Null;
+ binding->setType(QV4::CompiledData::Binding::Type_Null);
binding->value.nullMarker = 0;
}
}
// Do binding instead
- if (binding->type == QV4::CompiledData::Binding::Type_Invalid) {
- binding->type = QV4::CompiledData::Binding::Type_Script;
+ if (binding->type() == QV4::CompiledData::Binding::Type_Invalid) {
+ binding->setType(QV4::CompiledData::Binding::Type_Script);
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
@@ -1185,7 +1176,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_Translation;
+ binding->setType(QV4::CompiledData::Binding::Type_Translation);
binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("qsTrId")) {
QV4::CompiledData::TranslationData translationData;
@@ -1218,7 +1209,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_TranslationById;
+ binding->setType(QV4::CompiledData::Binding::Type_TranslationById);
binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) {
if (!args || !args->expression)
@@ -1235,7 +1226,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = jsGenerator->registerString(str.toString());
} else if (base == QLatin1String("QT_TRANSLATE_NOOP")) {
if (!args || !args->expression)
@@ -1256,7 +1247,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = jsGenerator->registerString(str.toString());
}
}
@@ -1293,9 +1284,8 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = nameLocation.offset;
- binding->location.line = nameLocation.startLine;
- binding->location.column = nameLocation.startColumn;
- binding->flags = 0;
+ binding->location.set(nameLocation.startLine, nameLocation.startColumn);
+ binding->clearFlags();
setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
@@ -1313,27 +1303,26 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = nameLocation.offset;
- binding->location.line = nameLocation.startLine;
- binding->location.column = nameLocation.startColumn;
+ binding->location.set(nameLocation.startLine, nameLocation.startColumn);
const Object *obj = _objects.at(objectIndex);
binding->valueLocation = obj->location;
- binding->flags = 0;
+ binding->clearFlags();
- if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
- binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly())
+ binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration);
// No type name on the initializer means it must be a group property
if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex)
- binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
+ binding->setType(Binding::Type_GroupProperty);
else
- binding->type = QV4::CompiledData::Binding::Type_Object;
+ binding->setType(Binding::Type_Object);
if (isOnAssignment)
- binding->flags |= QV4::CompiledData::Binding::IsOnAssignment;
+ binding->setFlag(Binding::IsOnAssignment);
if (isListItem)
- binding->flags |= QV4::CompiledData::Binding::IsListItem;
+ binding->setFlag(Binding::IsListItem);
binding->value.objectIndex = objectIndex;
QString error = bindingsTarget()->appendBinding(binding, isListItem);
@@ -1345,16 +1334,15 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
{
Alias *alias = New<Alias>();
- alias->flags = 0;
+ alias->clearFlags();
if (node->isReadonlyMember)
- alias->flags |= QV4::CompiledData::Alias::IsReadOnly;
+ alias->setFlag(QV4::CompiledData::Alias::IsReadOnly);
const QString propName = node->name.toString();
- alias->nameIndex = registerString(propName);
+ alias->setNameIndex(registerString(propName));
QQmlJS::SourceLocation loc = node->firstSourceLocation();
- alias->location.line = loc.startLine;
- alias->location.column = loc.startColumn;
+ alias->location.set(loc.startLine, loc.startColumn);
alias->propertyNameIndex = emptyStringIndex;
@@ -1368,8 +1356,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
rhsLoc = node->statement->firstSourceLocation();
else
rhsLoc = node->semicolonToken;
- alias->referenceLocation.line = rhsLoc.startLine;
- alias->referenceLocation.column = rhsLoc.startColumn;
+ alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn);
QStringList aliasReference;
@@ -1464,8 +1451,7 @@ bool IRBuilder::setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Sta
COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
_object->idNameIndex = registerString(idQString);
- _object->locationOfIdProperty.line = idLocation.startLine;
- _object->locationOfIdProperty.column = idLocation.startColumn;
+ _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn);
return true;
}
@@ -1511,19 +1497,23 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = qualifiedIdElement->identifierToken.offset;
- binding->location.line = qualifiedIdElement->identifierToken.startLine;
- binding->location.column = qualifiedIdElement->identifierToken.startColumn;
- binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine;
- binding->valueLocation.column = qualifiedIdElement->next->identifierToken.startColumn;
- binding->flags = 0;
+ binding->location.set(qualifiedIdElement->identifierToken.startLine,
+ qualifiedIdElement->identifierToken.startColumn);
+ binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine,
+ qualifiedIdElement->next->identifierToken.startColumn);
+ binding->location.set(qualifiedIdElement->identifierToken.startLine,
+ qualifiedIdElement->identifierToken.startColumn);
+ binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine,
+ qualifiedIdElement->next->identifierToken.startColumn);
+ binding->clearFlags();
if (onAssignment)
- binding->flags |= QV4::CompiledData::Binding::IsOnAssignment;
+ binding->setFlag(QV4::CompiledData::Binding::IsOnAssignment);
if (isAttachedProperty)
- binding->type = QV4::CompiledData::Binding::Type_AttachedProperty;
+ binding->setType(QV4::CompiledData::Binding::Type_AttachedProperty);
else
- binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
+ binding->setType(QV4::CompiledData::Binding::Type_GroupProperty);
int objIndex = 0;
if (!defineQMLObject(&objIndex, nullptr, QQmlJS::SourceLocation(), nullptr, nullptr))
@@ -1584,7 +1574,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
{
- if (property->isBuiltinType || property->isList)
+ if (property->isBuiltinType() || property->isList())
return false;
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (!exprStmt)
@@ -1682,10 +1672,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
- objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias;
- objectToWrite->flags = o->flags;
+ objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias);
+ objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags));
objectToWrite->idNameIndex = o->idNameIndex;
- objectToWrite->id = o->id;
+ objectToWrite->setObjectId(o->id);
objectToWrite->location = o->location;
objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
@@ -1860,7 +1850,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
continue;
QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
*bindingToWrite = *b;
- if (b->type == QV4::CompiledData::Binding::Type_Script)
+ if (b->type() == QV4::CompiledData::Binding::Type_Script)
bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
bindingPtr += sizeof(QV4::CompiledData::Binding);
}
@@ -1899,6 +1889,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
}
+ /* We do not want to visit the whole function, as we already called enterQmlFunction
+ However, there might be a function defined as a default argument of the function.
+ That needs to be considered, too, so we call handleTopLevelFunctionFormals to
+ deal with them.
+ */
+ scan.handleTopLevelFunctionFormals(function);
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
@@ -1964,7 +1960,7 @@ bool JSCodeGen::compileComponent(int contextObject)
if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
contextObject = componentBinding->value.objectIndex;
}
for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it)
@@ -1992,11 +1988,12 @@ bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int s
}
for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
- if (binding->type < QV4::CompiledData::Binding::Type_Object)
+ const Binding::Type bindingType = binding->type();
+ if (bindingType < QV4::CompiledData::Binding::Type_Object)
continue;
int target = binding->value.objectIndex;
- int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
+ int scope = bindingType == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope))
return false;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 9629a73199..8996777289 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -394,6 +394,10 @@ public:
int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); }
const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
+ bool hasFlag(QV4::CompiledData::Object::Flag flag) const { return flags & flag; }
+ qint32 objectId() const { return id; }
+ bool hasAliasAsDefaultProperty() const { return defaultPropertyIsAlias; }
+
private:
friend struct ::QQmlIRLoader;
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index 1895a34a68..5244c443c4 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -186,13 +186,13 @@ QT_WARNING_POP
Q_REQUIRED_RESULT Jump jumpNotUndefined()
{
- Instruction::JumpNotUndefined data;
+ Instruction::JumpNotUndefined data{};
return addJumpInstruction(data);
}
Q_REQUIRED_RESULT Jump jumpNoException()
{
- Instruction::JumpNoException data;
+ Instruction::JumpNoException data{};
return addJumpInstruction(data);
}
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 4588690307..5a0dc11fbf 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2927,6 +2927,17 @@ bool Codegen::visit(YieldExpression *ast)
return false;
}
+ auto innerMostCurentFunctionContext = _context;
+ while (innerMostCurentFunctionContext && innerMostCurentFunctionContext->contextType != ContextType::Function)
+ innerMostCurentFunctionContext = innerMostCurentFunctionContext->parent;
+
+ Q_ASSERT(innerMostCurentFunctionContext); // yield outside function would have been rejected by parser
+
+ if (!innerMostCurentFunctionContext->isGenerator) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Yield is only valid in generator functions"));
+ return false;
+ }
+
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 14cf0a0c8f..18e19cf01c 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -160,10 +160,7 @@ int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Getter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Getter, nameIndex);
return lookups.size() - 1;
}
@@ -174,49 +171,37 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Setter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Setter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_GlobalGetter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_QmlContextPropertyGetter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
- CompiledData::RegExp re;
- re.stringIndex = registerString(regexp->pattern.toString());
-
- re.flags = 0;
+ quint32 flags = 0;
if (regexp->flags & QQmlJS::Lexer::RegExp_Global)
- re.flags |= CompiledData::RegExp::RegExp_Global;
+ flags |= CompiledData::RegExp::RegExp_Global;
if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
- re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
+ flags |= CompiledData::RegExp::RegExp_IgnoreCase;
if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
- re.flags |= CompiledData::RegExp::RegExp_Multiline;
+ flags |= CompiledData::RegExp::RegExp_Multiline;
if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode)
- re.flags |= CompiledData::RegExp::RegExp_Unicode;
+ flags |= CompiledData::RegExp::RegExp_Unicode;
if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky)
- re.flags |= CompiledData::RegExp::RegExp_Sticky;
+ flags |= CompiledData::RegExp::RegExp_Sticky;
- regexps.append(re);
+ regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString())));
return regexps.size() - 1;
}
@@ -249,8 +234,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
for (const auto &name : members) {
- member->nameOffset = registerString(name);
- member->isAccessor = false;
+ member->set(registerString(name), false);
++member;
}
@@ -468,8 +452,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
currentOffset += function->nLabelInfos * sizeof(quint32);
}
- function->location.line = irFunction->line;
- function->location.column = irFunction->column;
+ function->location.set(irFunction->line, irFunction->column);
function->codeOffset = currentOffset;
function->codeSize = irFunction->code.size();
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index a1ddee8234..bd3a456c36 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -57,10 +57,7 @@ using namespace QQmlJS::AST;
static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation)
{
- CompiledData::Location target;
- target.line = astLocation.startLine;
- target.column = astLocation.startColumn;
- return target;
+ return CompiledData::Location(astLocation.startLine, astLocation.startColumn);
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index e39aa2454e..56172e2ea5 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -83,6 +83,13 @@ public:
ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
void operator()(QQmlJS::AST::Node *node);
+ // see comment at its call site in generateJSCodeForFunctionsAndBindings
+ // for why this function is necessary
+ void handleTopLevelFunctionFormals(QQmlJS::AST::FunctionExpression *node) {
+ if (node && node->formals)
+ node->formals->accept(this);
+ }
+
void enterGlobalEnvironment(ContextType compilationMode);
void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode,
const QString &name);
diff --git a/src/qml/configure.pri b/src/qml/configure.pri
index fcd2eacace..4506a5790d 100644
--- a/src/qml/configure.pri
+++ b/src/qml/configure.pri
@@ -13,7 +13,7 @@ defineTest(qtConfTest_detectPython) {
}
# Make tests.python.location available in configure.json.
- $${1}.location = $$shell_path($$python_path)
+ $${1}.location = $$shell_quote($$shell_path($$python_path))
export($${1}.location)
$${1}.cache += location
export($${1}.cache)
diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri
index 1281886816..6f976e149e 100644
--- a/src/qml/debugger/debugger.pri
+++ b/src/qml/debugger/debugger.pri
@@ -15,6 +15,7 @@ qtConfig(qml-debug) {
$$PWD/qqmldebugservice.cpp \
$$PWD/qqmlabstractprofileradapter.cpp \
$$PWD/qqmlprofiler.cpp \
+ $$PWD/qqmldebugserver.cpp \
$$PWD/qqmldebugserviceinterfaces.cpp
}
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 58b8ea2c4f..0ca56f3a70 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -44,17 +44,26 @@
#include <private/qqmlengine_p.h>
#include <private/qv4compileddata_p.h>
+#include <atomic>
#include <cstdio>
QT_REQUIRE_CONFIG(qml_debug);
QT_BEGIN_NAMESPACE
+#if __cplusplus >= 202002L
+# define Q_ATOMIC_FLAG_INIT {}
+#else
+# define Q_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT // deprecated in C++20
+#endif
+
+static std::atomic_flag s_printedWarning = Q_ATOMIC_FLAG_INIT;
+
QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning)
{
- if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning)
+ if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed))
fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n");
- QQmlEnginePrivate::qml_debugging_enabled = true;
+ QQmlEnginePrivate::qml_debugging_enabled.store(true, std::memory_order_relaxed);
}
/*!
diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp
index 0ef40d6911..01a0723b76 100644
--- a/src/qml/debugger/qqmldebugconnector.cpp
+++ b/src/qml/debugger/qqmldebugconnector.cpp
@@ -111,7 +111,7 @@ QQmlDebugConnector *QQmlDebugConnector::instance()
if (!params)
return nullptr;
- if (!QQmlEnginePrivate::qml_debugging_enabled) {
+ if (!QQmlEnginePrivate::qml_debugging_enabled.load(std::memory_order_relaxed)) {
if (!params->arguments.isEmpty()) {
qWarning().noquote() << QString::fromLatin1(
"QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging "
diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h
index d1ad90adfd..4f7b013160 100644
--- a/src/qml/debugger/qqmldebugconnector_p.h
+++ b/src/qml/debugger/qqmldebugconnector_p.h
@@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlDebugConnector
{
+ virtual ~QQmlDebugConnector() = default; // don't break 'override' on ~QQmlDebugServer
public:
static QQmlDebugConnector *instance() { return nullptr; }
diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp
new file mode 100644
index 0000000000..5119fc4209
--- /dev/null
+++ b/src/qml/debugger/qqmldebugserver.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldebugserver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlDebugServer::~QQmlDebugServer()
+ = default;
+
+QT_END_NAMESPACE
+
+#include "moc_qqmldebugserver_p.cpp"
diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h
index e848b00bda..c99155051c 100644
--- a/src/qml/debugger/qqmldebugserver_p.h
+++ b/src/qml/debugger/qqmldebugserver_p.h
@@ -62,6 +62,7 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugServer : public QQmlDebugConnector
{
Q_OBJECT
public:
+ ~QQmlDebugServer() override;
virtual void setDevice(QIODevice *socket) = 0;
};
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index d3eedab1c6..ed1e011830 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -176,7 +176,7 @@ public:
RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url,
const QV4::CompiledData::Object *obj, const QString &type)
- : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
+ : Location(QQmlSourceLocation(type, obj->location.line(), obj->location.column()), url),
locationType(Creating), sent(false)
{
unit = ref;
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
index 86c3e078c8..1326b8c87a 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
+++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
@@ -50,8 +50,9 @@
//![0]
// script.mjs
import { factorial } from "factorial.mjs"
+export { factorial }
-function showCalculations(value) {
+export function showCalculations(value) {
console.log(
"Call factorial() from script.js:",
factorial(value));
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index ece2fd5fd7..ffe0ec9737 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -69,6 +69,21 @@ exposed to QML but the type itself should not be instantiable.
For a quick guide to choosing the correct approach to expose C++ types to QML,
see \l {Choosing the Correct Integration Method Between C++ and QML}.
+\section2 Preconditions
+
+All the macros mentioned below are available from the \c qqml.h
+header. You need to add the following code to the files using them in order to
+make the macros available:
+
+\code
+#include <QtQml/qqml.h>
+\endcode
+
+Furthermore, your class declarations have to live in headers reachable via your
+project's include path. The declarations are used to generate registration code
+at compile time, and the registration code needs to include the headers that
+contain the declarations.
+
\section2 Registering an Instantiable Object Type
\b{Any QObject-derived C++ class can be registered as the definition of a
diff --git a/src/qml/doc/src/javascript/finetuning.qdoc b/src/qml/doc/src/javascript/finetuning.qdoc
index fcd710db8b..35310ee051 100644
--- a/src/qml/doc/src/javascript/finetuning.qdoc
+++ b/src/qml/doc/src/javascript/finetuning.qdoc
@@ -82,9 +82,12 @@ Running JavaScript code can be influenced by a few environment variables, partic
\li \c{QV4_MAX_CALL_DEPTH}
\li Stack overflows when running (as opposed to compiling) JavaScript are prevented by
controlling the call depth: the number of nested function invocations. By
- default, an exception is generated if the call depth exceeds 1234. If it contains a
- number, this environment variable overrides the maximum call depth. Beware that the
- recursion limit when compiling JavaScript is not affected.
+ default, an exception is generated if the call depth exceeds a maximum number tuned
+ to the platform's default stack size. If the \c{QV4_MAX_CALL_DEPTH} environment
+ variable contains a number, this number is used as maximum call depth. Beware that
+ the recursion limit when compiling JavaScript is not affected. The default maximum
+ call depth is 1234 on most platforms. On QNX it is 640 because on QNX the default
+ stack size is smaller than on most platforms.
\row
\li \c{QV4_MM_AGGRESSIVE_GC}
\li Setting this environment variable runs the garbage collector before each memory
diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc
index 9227f0e604..8d49c02f62 100644
--- a/src/qml/doc/src/javascript/imports.qdoc
+++ b/src/qml/doc/src/javascript/imports.qdoc
@@ -95,17 +95,18 @@ or modules).
A JavaScript resource may import another in the following fashion:
\code
-.import "filename.js" as Qualifier
+import * as MathFunctions from "factorial.mjs";
\endcode
-For example:
+Or:
\code
-import * as MathFunctions from "factorial.mjs";
+.import "filename.js" as Qualifier
\endcode
-The latter is standard ECMAScript syntax for importing ECMAScript modules, and
+The former is standard ECMAScript syntax for importing ECMAScript modules, and
only works from within ECMAScript modules as denoted by the \c mjs file
-extension. The former is an extension to JavaScript provided by the \c QML
-engine and will work also with non-modules.
+extension. The latter is an extension to JavaScript provided by the \c QML
+engine and will work also with non-modules. As an extension superseded by the
+ECMAScript standard, its usage is discouraged.
When a JavaScript file is imported this way, it is imported with a qualifier.
The functions in that file are then accessible from the importing script via the
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 4e531ceb61..6f5a0c4196 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -159,11 +159,11 @@
\relates QQmlEngine
Declares the enclosing type to be a singleton in QML. This only takes effect
- if the type is available in QML, by having a \l QML_ELEMENT or
- \l QML_NAMED_ELEMENT() macro. By default, each QQmlEngine will try to create a
- singleton instance using the type's default constructor when the type is first
- accessed. If there is no default constructor the singleton is initially
- inaccessible. This behavior can be overridden by calling
+ if the type is a \l Q_OBJECT and is available in QML (by having a
+ \l QML_ELEMENT or \l QML_NAMED_ELEMENT() macro). By default, each QQmlEngine
+ will try to create a singleton instance using the type's default constructor
+ when the type is first accessed. If there is no default constructor the
+ singleton is initially inaccessible. This behavior can be overridden by calling
\l qmlRegisterSingletonType() with a specific factory function or
\l qmlRegisterSingletonInstance() with a specific instance for the same class
and the same type namespace and version.
diff --git a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
index ec888ae937..b1fce12dc3 100644
--- a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
+++ b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
@@ -33,7 +33,7 @@ called "my.module", you would add the forward declaration in global scope:
\code
void qml_register_types_my_module();
-\code
+\endcode
Then add the following snippet of code in the implementation of any function
that's part of the same binary as the registration:
@@ -41,7 +41,7 @@ that's part of the same binary as the registration:
\code
volatile auto registration = &qml_register_types_my_module;
Q_UNUSED(registration);
-\code
+\endcode
\section1 TimeExample QML extension plugin
diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
index 075b3a7646..0e06cbec8a 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
@@ -133,7 +133,7 @@ Rectangle {
Connections {
target: button
- function onClicked(): {
+ function onClicked() {
rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
}
}
diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h
index 99b28349cd..c3ab0e1fe5 100644
--- a/src/qml/inlinecomponentutils_p.h
+++ b/src/qml/inlinecomponentutils_p.h
@@ -55,24 +55,38 @@
namespace icutils {
struct Node {
+private:
+ using IndexType = std::vector<QV4::CompiledData::InlineComponent>::size_type;
+ using IndexField = quint32_le_bitfield_member<0, 30, IndexType>;
+ using TemporaryMarkField = quint32_le_bitfield_member<30, 1>;
+ using PermanentMarkField = quint32_le_bitfield_member<31, 1>;
+ quint32_le_bitfield_union<IndexField, TemporaryMarkField, PermanentMarkField> m_data;
+
+public:
Node() = default;
Node(const Node &) = default;
Node(Node &&) = default;
Node& operator=(Node const &) = default;
Node& operator=(Node &&) = default;
- bool operator==(Node const &other) const {return index == other.index;}
+ bool operator==(Node const &other) const {return m_data.data() == other.m_data.data(); }
+
+ Node(IndexType s) : m_data(QSpecialIntegerBitfieldZero) { m_data.set<IndexField>(s); }
+
+ bool hasPermanentMark() const { return m_data.get<PermanentMarkField>(); }
+ bool hasTemporaryMark() const { return m_data.get<TemporaryMarkField>(); }
+
+ void setPermanentMark()
+ {
+ m_data.set<TemporaryMarkField>(0);
+ m_data.set<PermanentMarkField>(1);
+ }
- Node(std::vector<QV4::CompiledData::InlineComponent>::size_type s) {
- index = quint32(s);
- temporaryMark = 0;
- permanentMark = 0;
+ void setTemporaryMark()
+ {
+ m_data.set<TemporaryMarkField>(1);
}
- union {
- quint32_le_bitfield<0, 30> index;
- quint32_le_bitfield<30, 1> temporaryMark;
- quint32_le_bitfield<31, 1> permanentMark;
- };
+ IndexType index() const { return m_data.get<IndexField>(); }
};
using AdjacencyList = std::vector<std::vector<Node*>>;
@@ -107,8 +121,11 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
auto referencedInICObjectIndex = ic.objectIndex + 1;
while (int(referencedInICObjectIndex) < objectContainer->objectCount()) {
auto potentiallyReferencedInICObject = objectContainer->objectAt(referencedInICObjectIndex);
- bool stillInIC = !(potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- && (potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::InPartOfInlineComponent);
+ bool stillInIC
+ = !potentiallyReferencedInICObject->hasFlag(
+ QV4::CompiledData::Object::IsInlineComponentRoot)
+ && potentiallyReferencedInICObject->hasFlag(
+ QV4::CompiledData::Object::InPartOfInlineComponent);
if (!stillInIC)
break;
createEdgeFromTypeRef(objectContainer->resolvedType(potentiallyReferencedInICObject->inheritedTypeNameIndex));
@@ -118,21 +135,20 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
};
inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle, std::vector<Node> &nodesSorted) {
- if (node->permanentMark)
+ if (node->hasPermanentMark())
return;
- if (node->temporaryMark) {
+ if (node->hasTemporaryMark()) {
hasCycle = true;
return;
}
- node->temporaryMark = 1;
+ node->setTemporaryMark();
- auto const &edges = adjacencyList[node->index];
+ auto const &edges = adjacencyList[node->index()];
for (auto edgeTarget =edges.begin(); edgeTarget != edges.end(); ++edgeTarget) {
topoVisit(*edgeTarget, adjacencyList, hasCycle, nodesSorted);
}
- node->temporaryMark = 0;
- node->permanentMark = 1;
+ node->setPermanentMark();
nodesSorted.push_back(*node);
};
@@ -143,7 +159,7 @@ inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjac
hasCycle = false;
auto currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) {
- return node.permanentMark == 0;
+ return !node.hasPermanentMark();
});
// Do a topological sort of all inline components
// afterwards, nodesSorted contains the nodes for the inline components in reverse topological order
@@ -151,7 +167,7 @@ inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjac
Node& currentNode = *currentNodeIt;
topoVisit(&currentNode, adjacencyList, hasCycle, nodesSorted);
currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) {
- return node.permanentMark == 0;
+ return !node.hasPermanentMark();
});
}
return nodesSorted;
diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp
index cc8af723b2..b090fc7597 100644
--- a/src/qml/jit/qv4assemblercommon.cpp
+++ b/src/qml/jit/qv4assemblercommon.cpp
@@ -51,6 +51,8 @@
#include <assembler/LinkBuffer.h>
#include <WTFStubs.h>
+#if QT_CONFIG(qml_jit)
+
#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
QT_BEGIN_NAMESPACE
@@ -159,7 +161,8 @@ void PlatformAssemblerCommon::link(Function *function, const char *jitKind)
generateFunctionTable(function, &codeRef);
- linkBuffer.makeExecutable();
+ if (Q_UNLIKELY(!linkBuffer.makeExecutable()))
+ function->jittedCode = nullptr; // The function is not executable, but the coderef exists.
}
void PlatformAssemblerCommon::prepareCallWithArgCount(int argc)
@@ -369,3 +372,5 @@ void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr)
} // QV4 namepsace
QT_END_NAMESPACE
+
+#endif // QT_CONFIG(qml_jit)
diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h
index ead1e757de..c3b1eb34eb 100644
--- a/src/qml/jit/qv4assemblercommon_p.h
+++ b/src/qml/jit/qv4assemblercommon_p.h
@@ -58,7 +58,7 @@
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
-QT_REQUIRE_CONFIG(qml_jit);
+#if QT_CONFIG(qml_jit)
QT_BEGIN_NAMESPACE
@@ -740,4 +740,6 @@ private:
QT_END_NAMESPACE
+#endif // QT_CONFIG(qml_jit)
+
#endif // QV4PLATFORMASSEMBLER_P_H
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index dd6a40afe0..7ad131335e 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -55,6 +55,8 @@
#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
+#if QT_CONFIG(qml_jit)
+
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
@@ -1619,3 +1621,5 @@ void BaselineAssembler::ret()
} // QV4 namepsace
QT_END_NAMESPACE
+
+#endif // QT_CONFIG(qml_jit)
diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index c2c735282b..c6fdab51c5 100644
--- a/src/qml/jit/qv4baselineassembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -55,7 +55,7 @@
#include <private/qv4function_p.h>
#include <QHash>
-QT_REQUIRE_CONFIG(qml_jit);
+#if QT_CONFIG(qml_jit)
QT_BEGIN_NAMESPACE
@@ -182,4 +182,6 @@ private:
QT_END_NAMESPACE
+#endif // QT_CONFIG(qml_jit)
+
#endif // QV4BASELINEASSEMBLER_P_H
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index d329a5afec..45150cfffd 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -42,6 +42,8 @@
#include <private/qv4lookup_p.h>
#include <private/qv4generatorobject_p.h>
+#if QT_CONFIG(qml_jit)
+
QT_USE_NAMESPACE
using namespace QV4;
using namespace QV4::JIT;
@@ -930,3 +932,6 @@ void BaselineJIT::endInstruction(Instr::Type instr)
{
Q_UNUSED(instr);
}
+
+#endif // QT_CONFIG(qml_jit)
+
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 284faf0ff0..2b0913169e 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -56,7 +56,7 @@
#include <private/qv4instr_moth_p.h>
#include <private/qv4bytecodehandler_p.h>
-QT_REQUIRE_CONFIG(qml_jit);
+#if QT_CONFIG(qml_jit)
QT_BEGIN_NAMESPACE
@@ -220,4 +220,6 @@ private:
QT_END_NAMESPACE
+#endif // QT_CONFIG(qml_jit)
+
#endif // QV4JIT_P_H
diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h
index 2f95e0ff31..80641ff235 100644
--- a/src/qml/jsapi/qjsvalue.h
+++ b/src/qml/jsapi/qjsvalue.h
@@ -57,7 +57,6 @@ class QDateTime;
typedef QList<QJSValue> QJSValueList;
namespace QV4 {
struct ExecutionEngine;
- struct Value;
}
class Q_QML_EXPORT QJSValue
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
index 199b1a728a..51387edf6e 100644
--- a/src/qml/jsruntime/qv4arrayiterator.cpp
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -86,18 +86,18 @@ ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const
return IteratorPrototype::createIterResultObject(scope.engine, Value::fromInt32(index), false);
}
- ReturnedValue elementValue = a->get(index);
+ QV4::ScopedValue elementValue(scope, a->get(index));
CHECK_EXCEPTION();
if (itemKind == ValueIteratorKind) {
- return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false);
+ return IteratorPrototype::createIterResultObject(scope.engine, elementValue, false);
} else {
Q_ASSERT(itemKind == KeyValueIteratorKind);
ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
resultArray->arrayReserve(2);
resultArray->arrayPut(0, Value::fromInt32(index));
- resultArray->arrayPut(1, Value::fromReturnedValue(elementValue));
+ resultArray->arrayPut(1, elementValue);
resultArray->setArrayLengthUnchecked(2);
return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index df9f117d04..03e5c29973 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -254,6 +254,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
}
if (mapfn) {
+ Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc
mapArguments[0] = *nextValue;
mapArguments[1] = Value::fromDouble(k);
mappedValue = mapfn->call(thisArg, mapArguments, 2);
@@ -297,6 +298,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
CHECK_EXCEPTION();
if (mapfn) {
+ Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc
mapArguments[0] = kValue;
mapArguments[1] = Value::fromDouble(k);
mappedValue = mapfn->call(thisArg, mapArguments, 2);
diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index 74f34a284d..86bcf229d1 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -40,23 +40,69 @@
#include "qv4compilationunitmapper_p.h"
#include <private/qv4compileddata_p.h>
-#include <QFileInfo>
-#include <QDateTime>
-#include <QCoreApplication>
+#include <private/qv4executablecompilationunit_p.h>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qhash.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-CompilationUnitMapper::CompilationUnitMapper()
- : dataPtr(nullptr)
+class StaticUnitCache
{
+public:
+ StaticUnitCache() : m_lock(&s_mutex) {}
-}
+ CompilationUnitMapper get(const QString &file)
+ {
+ const auto it = s_staticUnits.constFind(file);
+ return it == s_staticUnits.constEnd() ? CompilationUnitMapper() : *it;
+ }
+
+ void set(const QString &file, const CompilationUnitMapper &staticUnit) {
+ s_staticUnits.insert(file, staticUnit);
+ }
+
+private:
+ QMutexLocker m_lock;
+
+ static QMutex s_mutex;
+
+ // We can copy the mappers around because they're all static, that is the dtors are noops.
+ static QHash<QString, CompilationUnitMapper> s_staticUnits;
+};
+
+QHash<QString, CompilationUnitMapper> StaticUnitCache::s_staticUnits;
+QMutex StaticUnitCache::s_mutex;
CompilationUnitMapper::~CompilationUnitMapper()
{
close();
}
+CompiledData::Unit *CompilationUnitMapper::get(
+ const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString)
+{
+ StaticUnitCache cache;
+
+ CompilationUnitMapper mapper = cache.get(cacheFilePath);
+ if (mapper.dataPtr) {
+ auto *unit = reinterpret_cast<CompiledData::Unit *>(mapper.dataPtr);
+ if (ExecutableCompilationUnit::verifyHeader(unit, sourceTimeStamp, errorString)) {
+ *this = mapper;
+ return unit;
+ }
+
+ return nullptr;
+ }
+
+ CompiledData::Unit *data = open(cacheFilePath, sourceTimeStamp, errorString);
+ if (data && (data->flags & CompiledData::Unit::StaticData))
+ cache.set(cacheFilePath, *this);
+
+ return data;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 80f914c141..10ad0a6ede 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -65,17 +65,20 @@ struct Unit;
class CompilationUnitMapper
{
public:
- CompilationUnitMapper();
~CompilationUnitMapper();
- CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
- void close();
+ CompiledData::Unit *get(
+ const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
private:
+ CompiledData::Unit *open(
+ const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ void close();
+
#if defined(Q_OS_UNIX)
- size_t length;
+ size_t length = 0;
#endif
- void *dataPtr;
+ void *dataPtr = nullptr;
};
}
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 4e445a73fa..a14c7d9257 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -78,10 +78,21 @@
QTBUG-75585 for an explanation and possible workarounds.
*/
#define USE_QTZ_SYSTEM_ZONE
+#elif defined(Q_OS_WASM)
+/*
+ TODO: evaluate using this version of the code more generally, rather than
+ the #else branches of the various USE_QTZ_SYSTEM_ZONE choices. It might even
+ work better than the timezone variant; experiments needed.
+*/
+// Kludge around the lack of time-zone info using QDateTime.
+// It uses localtime() and friends to determine offsets from UTC.
+#define USE_QDT_LOCAL_TIME
#endif
#ifdef USE_QTZ_SYSTEM_ZONE
#include <QtCore/QTimeZone>
+#elif defined(USE_QDT_LOCAL_TIME)
+// QDateTime already included above
#else
# ifdef Q_OS_WIN
# include <windows.h>
@@ -356,6 +367,7 @@ static inline double MakeDate(double day, double time)
mean a whole day of DST offset for some zones, that have crossed the
international date line. This shall confuse client code.) The bug report
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
+ and they've now changed the spec so that the following conforms to it ;^>
*/
static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
@@ -363,6 +375,12 @@ static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC t
return QTimeZone::systemTimeZone().offsetFromUtc(
QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
+#elif defined(USE_QDT_LOCAL_TIME)
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
+{
+ return QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC
+ ).toLocalTime().offsetFromUtc() * 1e3 - localTZA;
+}
#else
// This implementation fails to take account of past changes in standard offset.
static inline double DaylightSavingTA(double t, double /*localTZA*/)
@@ -721,6 +739,26 @@ static double getLocalTZA()
// TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above.
// Standard offset, with no daylight-savings adjustment, in ms:
return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3;
+#elif defined(USE_QDT_LOCAL_TIME)
+ QDate today = QDate::currentDate();
+ QDateTime near = today.startOfDay(Qt::LocalTime);
+ // Early out if we're in standard time anyway:
+ if (!near.isDaylightTime())
+ return near.offsetFromUtc() * 1000;
+ int year, month;
+ today.getDate(&year, &month, nullptr);
+ // One of the solstices is probably in standard time:
+ QDate summer(year, 6, 21), winter(year - (month < 7 ? 1 : 0), 12, 21);
+ // But check the one closest to the present by preference, in case there's a
+ // standard time offset change between them:
+ QDateTime far = summer.startOfDay(Qt::LocalTime);
+ near = winter.startOfDay(Qt::LocalTime);
+ if (month > 3 && month < 10)
+ near.swap(far);
+ bool isDst = near.isDaylightTime();
+ if (isDst && far.isDaylightTime()) // Permanent DST, probably an hour west:
+ return (qMin(near.offsetFromUtc(), far.offsetFromUtc()) - 3600) * 1000;
+ return (isDst ? far : near).offsetFromUtc() * 1000;
#else
# ifdef Q_OS_WIN
TIME_ZONE_INFORMATION tzInfo;
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 833df534d6..0a564fc6d5 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -300,6 +300,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
if (ok && envMaxGCStackSize > 0)
m_maxGCStackSize = envMaxGCStackSize;
+ // We allocate guard pages around our stacks.
+ const size_t guardPages = 2 * WTF::pageSize();
+
memoryManager = new QV4::MemoryManager(this);
if (maxCallDepth == -1) {
@@ -310,7 +313,11 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
if (!ok || maxCallDepth <= 0) {
#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
+#ifdef Q_OS_QNX
+ maxCallDepth = 640; // QNX's stack is only 512k by default
+#else
maxCallDepth = 1234;
+#endif
#else
// no (tail call) optimization is done, so there'll be a lot mare stack frames active
maxCallDepth = 200;
@@ -323,9 +330,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
- *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ m_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
jsStackBase = (Value *)jsStack->base();
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
@@ -333,9 +340,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *gcStack = WTF::PageAllocation::allocate(
+ m_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
{
ok = false;
@@ -1885,6 +1892,25 @@ int ExecutionEngine::maxGCStackSize() const
return m_maxGCStackSize;
}
+/*!
+ \internal
+ Returns \a length converted to int if its safe to
+ pass to \c Scope::alloc.
+ Otherwise it throws a RangeError, and returns 0.
+ */
+int ExecutionEngine::safeForAllocLength(qint64 len64)
+{
+ if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) {
+ this->throwRangeError(QStringLiteral("Invalid array length."));
+ return 0;
+ }
+ if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) {
+ this->throwRangeError(QStringLiteral("Array too large for apply()."));
+ return 0;
+ }
+ return len64;
+}
+
ReturnedValue ExecutionEngine::global()
{
return globalObject->asReturnedValue();
@@ -2026,7 +2052,7 @@ void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
{
- if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen)
+ if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen())
return;
QV4::Scope scope(v4);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index efe44a324c..dd6e6ed279 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -655,6 +655,7 @@ public:
int maxGCStackSize() const;
bool checkStackLimits();
+ int safeForAllocLength(qint64 len64);
bool canJIT(Function *f = nullptr)
{
@@ -723,6 +724,9 @@ public:
QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr);
private:
+ template<int Frames>
+ friend struct ExecutionEngineCallDepthRecorder;
+
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
@@ -753,14 +757,17 @@ private:
};
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
- ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
+ ExecutionEngineCallDepthRecorder<1> _executionEngineCallDepthRecorder(v4);
+template<int Frames = 1>
struct ExecutionEngineCallDepthRecorder
{
ExecutionEngine *ee;
- ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; }
- ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; }
+ ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ee->callDepth += Frames; }
+ ~ExecutionEngineCallDepthRecorder() { ee->callDepth -= Frames; }
+
+ bool hasOverflow() const { return ee->callDepth >= ExecutionEngine::maxCallDepth; }
};
inline bool ExecutionEngine::checkStackLimits()
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 29e1c6dbf7..09a1e936ad 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -155,10 +155,10 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- uint f = re->flags;
+ uint f = re->flags();
const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
runtimeRegularExpressions[i] = QV4::RegExp::create(
- engine, stringAt(re->stringIndex), flags);
+ engine, stringAt(re->stringIndex()), flags);
}
if (data->lookupTableSize) {
@@ -169,7 +169,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
QV4::Lookup *l = runtimeLookups + i;
CompiledData::Lookup::Type type
- = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags));
+ = CompiledData::Lookup::Type(uint(compiledLookups[i].typeAndFlags()));
if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
else if (type == CompiledData::Lookup::Type_Setter)
@@ -178,7 +178,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
- l->nameIndex = compiledLookups[i].nameIndex;
+ l->nameIndex = compiledLookups[i].nameIndex();
}
}
@@ -199,8 +199,8 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
runtimeClasses[i]
= runtimeClasses[i]->addMember(
engine->identifierTable->asPropertyKey(
- runtimeStrings[member->nameOffset]),
- member->isAccessor
+ runtimeStrings[member->nameOffset()]),
+ member->isAccessor()
? QV4::Attr_Accessor
: QV4::Attr_Data);
}
@@ -296,24 +296,8 @@ void ExecutableCompilationUnit::unlink()
propertyCaches.clear();
if (runtimeLookups) {
- for (uint i = 0; i < data->lookupTableSize; ++i) {
- QV4::Lookup &l = runtimeLookups[i];
- if (l.getter == QV4::QObjectWrapper::lookupGetter
- || l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
- if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
- pc->release();
- } else if (l.getter == QQmlValueTypeWrapper::lookupGetter
- || l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
- if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
- pc->release();
- }
-
- if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
- || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
- if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
- pc->release();
- }
- }
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].releasePropertyCache();
}
dependentScripts.clear();
@@ -384,7 +368,7 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com
const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId());
}
return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
@@ -437,13 +421,14 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi
// We need to first iterate over all inline components, as the containing component might create instances of them
// and in that case we need to add its object count
for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
- const auto &ic = allICs.at(nodeIt->index);
+ const auto &ic = allICs.at(nodeIt->index());
int lastICRoot = ic.objectIndex;
for (int i = ic.objectIndex; i<objectCount(); ++i) {
const QV4::CompiledData::Object *obj = objectAt(i);
- bool leftCurrentInlineComponent =
- (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent);
+ bool leftCurrentInlineComponent
+ = (i != lastICRoot
+ && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
+ || !obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent);
if (leftCurrentInlineComponent)
break;
inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings;
@@ -473,9 +458,9 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi
int objectCount = 0;
for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
const QV4::CompiledData::Object *obj = objectAt(i);
- if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) {
+ if (obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent))
continue;
- }
+
bindingCount += obj->nBindings;
if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1)
@@ -587,7 +572,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
if (!valuePtr) {
QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
referenceErrorMessage += importName->toQString();
- engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ engine->throwReferenceError(
+ referenceErrorMessage, fileName(),
+ entry.location.line(), entry.location.column());
return nullptr;
}
imports[i] = valuePtr;
@@ -603,7 +590,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
if (!dependentModuleUnit->resolveExport(importName)) {
QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
referenceErrorMessage += importName->toQString();
- engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ engine->throwReferenceError(
+ referenceErrorMessage, fileName(),
+ entry.location.line(), entry.location.column());
return nullptr;
}
}
@@ -751,7 +740,7 @@ bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &s
const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
for (const QString &cachePath : cachePaths) {
- CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
+ CompiledData::Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
if (!mappedUnit)
continue;
@@ -874,7 +863,7 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e
QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
{
using namespace CompiledData;
- switch (binding->type) {
+ switch (binding->type()) {
case Binding::Type_Script:
case Binding::Type_String:
return stringAt(binding->stringIndex);
@@ -922,7 +911,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind
QString ExecutableCompilationUnit::bindingValueAsScriptString(
const CompiledData::Binding *binding) const
{
- return (binding->type == CompiledData::Binding::Type_String)
+ return (binding->type() == CompiledData::Binding::Type_String)
? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
: bindingValueAsString(binding);
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index 7bfb68da9d..29e82bf2d5 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -282,7 +282,7 @@ public:
QString bindingValueAsScriptString(const CompiledData::Binding *binding) const;
double bindingValueAsNumber(const CompiledData::Binding *binding) const
{
- if (binding->type != CompiledData::Binding::Type_Number)
+ if (binding->type() != CompiledData::Binding::Type_Number)
return 0.0;
return constants[binding->value.constantValueIndex].doubleValue();
}
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index aeb4835c40..cf8a53cf9f 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -176,7 +176,8 @@ QString Function::prettyName(const Function *function, const void *code)
QQmlSourceLocation Function::sourceLocation() const
{
- return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
+ return QQmlSourceLocation(
+ sourceFile(), compiledFunction->location.line(), compiledFunction->location.column());
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index cdb3b8942b..8dafe251e0 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -364,15 +364,10 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
if (!arr)
return v4->throwTypeError();
- const qint64 len64 = arr->getLength();
- if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max()))
- return v4->throwRangeError(QStringLiteral("Invalid array length."));
- if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop))
- return v4->throwRangeError(QStringLiteral("Array too large for apply()."));
-
- const uint len = uint(len64);
-
Scope scope(v4);
+ const uint len = v4->safeForAllocLength(arr->getLength());
+ CHECK_EXCEPTION();
+
Value *arguments = scope.alloc<Scope::Uninitialized>(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 4eee6f4338..5077bf1d2b 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -97,24 +97,21 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu
Function *function = gf->function();
ExecutionEngine *engine = gf->engine();
- // We need to set up a separate stack for the generator, as it's being re-entered
- uint stackSize = argc // space for the original arguments
- + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame
-
- size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize;
-
Scope scope(gf);
- Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
+ Scoped<GeneratorObject> g(scope, engine->memoryManager->allocManaged<GeneratorObject>(sizeof(GeneratorObject::Data), engine->classes[EngineBase::Class_GeneratorObject]));
g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
+ // We need to set up a separate JSFrame for the generator, as it's being re-entered
Heap::GeneratorObject *gp = g->d();
- gp->stack.size = stackSize;
- gp->stack.alloc = stackSize;
+ gp->values.set(engine, engine->newArrayObject(argc));
+ gp->jsFrame.set(engine, engine->newArrayObject(CppStackFrame::requiredJSStackFrameSize(function)));
// copy original arguments
- memcpy(gp->stack.values, argv, argc*sizeof(Value));
- gp->cppFrame.init(engine, function, gp->stack.values, argc);
- gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(),
+ for (int i = 0; i < argc; i++)
+ gp->values->arrayData->setArrayData(engine, i, argv[i]);
+
+ gp->cppFrame.init(engine, function, gp->values->arrayData->values.values, argc);
+ gp->cppFrame.setupJSFrame(gp->jsFrame->arrayData->values.values, *gf, gf->scope(),
thisObject ? *thisObject : Value::undefinedValue(),
Value::undefinedValue());
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
index 366319723d..14368f5416 100644
--- a/src/qml/jsruntime/qv4generatorobject_p.h
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -87,10 +87,10 @@ struct GeneratorPrototype : FunctionObject {
#define GeneratorObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, context) \
- Member(class, Pointer, GeneratorFunction *, function) \
Member(class, NoMark, GeneratorState, state) \
Member(class, NoMark, CppStackFrame, cppFrame) \
- Member(class, ValueArray, ValueArray, stack)
+ Member(class, Pointer, ArrayObject *, values) \
+ Member(class, Pointer, ArrayObject *, jsFrame)
DECLARE_HEAP_OBJECT(GeneratorObject, Object) {
DECLARE_MARKOBJECTS(GeneratorObject);
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index c6a737b467..ccc55d869f 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -64,21 +64,6 @@
#include <qtqmlglobal.h>
#include <private/qtqmlglobal_p.h>
-#if defined(Q_CC_MSVC)
-#include <float.h>
-#include <math.h>
-
-namespace std {
-
-inline bool isinf(double d) { return !_finite(d) && !_isnan(d); }
-inline bool isnan(double d) { return !!_isnan(d); }
-inline bool isfinite(double d) { return _finite(d); }
-
-} // namespace std
-
-inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
-#endif
-
// Do certain things depending on whether the JIT is enabled or disabled
#if QT_CONFIG(qml_jit)
@@ -318,7 +303,6 @@ struct PropertyAttributes
void clear() { m_all = 0; }
bool isEmpty() const { return !m_all; }
- uint flags() const { return m_flags; }
uint all() const { return m_all; }
bool operator==(PropertyAttributes other) {
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 70849775cb..904c6a5eaf 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -178,7 +178,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s)
data->values.size = s;
}
-PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i)
+PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) const
{
Q_ASSERT(data && i < size());
return PropertyKey::fromId(data->values.values[i].rawValue());
@@ -265,6 +265,13 @@ namespace Heap {
void InternalClass::init(ExecutionEngine *engine)
{
+// InternalClass is automatically zeroed during allocation:
+// prototype = nullptr;
+// parent = nullptr;
+// size = 0;
+// numRedundantTransitions = 0;
+// flags = 0;
+
Base::init();
new (&propertyTable) PropertyHash();
new (&nameMap) SharedInternalClassData<PropertyKey>(engine);
@@ -273,13 +280,6 @@ void InternalClass::init(ExecutionEngine *engine)
this->engine = engine;
vtable = QV4::InternalClass::staticVTable();
-// prototype = nullptr;
-// parent = nullptr;
-// size = 0;
- extensible = true;
- isFrozen = false;
- isSealed = false;
- isUsedAsProto = false;
protoId = engine->newProtoId();
// Also internal classes need an internal class pointer. Simply make it point to itself
@@ -300,10 +300,8 @@ void InternalClass::init(Heap::InternalClass *other)
prototype = other->prototype;
parent = other;
size = other->size;
- extensible = other->extensible;
- isSealed = other->isSealed;
- isFrozen = other->isFrozen;
- isUsedAsProto = other->isUsedAsProto;
+ numRedundantTransitions = other->numRedundantTransitions;
+ flags = other->flags;
protoId = engine->newProtoId();
internalClass.set(engine, other->internalClass);
@@ -365,7 +363,99 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
++newClass->size;
}
-Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
+static PropertyAttributes attributesFromFlags(int flags)
+{
+ PropertyAttributes attributes;
+ attributes.m_all = uchar(flags);
+ return attributes;
+}
+
+static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig)
+{
+ if (++orig->numRedundantTransitions < Heap::InternalClass::MaxRedundantTransitions)
+ return orig;
+
+ // We will generally add quite a few transitions here. We have 255 redundant ones.
+ // We can expect at least as many significant ones in addition.
+ std::vector<InternalClassTransition> transitions;
+
+ Scope scope(orig->engine);
+ Scoped<QV4::InternalClass> child(scope, orig);
+
+ {
+ quint8 remainingRedundantTransitions = orig->numRedundantTransitions;
+ QSet<PropertyKey> properties;
+ int structureChanges = 0;
+
+ Scoped<QV4::InternalClass> parent(scope, orig->parent);
+ while (parent && remainingRedundantTransitions > 0) {
+ Q_ASSERT(child->d() != scope.engine->classes[ExecutionEngine::Class_Empty]);
+ const auto it = std::find_if(
+ parent->d()->transitions.begin(), parent->d()->transitions.end(),
+ [&child](const InternalClassTransition &t) {
+ return child->d() == t.lookup;
+ });
+ Q_ASSERT(it != parent->d()->transitions.end());
+
+ if (it->flags & InternalClassTransition::StructureChange) {
+ // A structural change. Each kind of structural change has to be recorded only once.
+ if ((structureChanges & it->flags) != it->flags) {
+ transitions.push_back(*it);
+ structureChanges |= it->flags;
+ } else {
+ --remainingRedundantTransitions;
+ }
+ } else if (!properties.contains(it->id)) {
+ // We only need the final state of the property.
+ properties.insert(it->id);
+
+ // Property removal creates _two_ redundant transitions.
+ // We don't have to replay either, but numRedundantTransitions only records one.
+ if (it->flags != 0)
+ transitions.push_back(*it);
+ } else {
+ --remainingRedundantTransitions;
+ }
+
+ child = parent->d();
+ parent = child->d()->parent;
+ Q_ASSERT(child->d() != parent->d());
+ }
+ }
+
+ for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) {
+ switch (it->flags) {
+ case InternalClassTransition::NotExtensible:
+ child = child->d()->nonExtensible();
+ continue;
+ case InternalClassTransition::VTableChange:
+ child = child->d()->changeVTable(it->vtable);
+ continue;
+ case InternalClassTransition::PrototypeChange:
+ child = child->d()->changePrototype(it->prototype);
+ continue;
+ case InternalClassTransition::ProtoClass:
+ child = child->d()->asProtoClass();
+ continue;
+ case InternalClassTransition::Sealed:
+ child = child->d()->sealed();
+ continue;
+ case InternalClassTransition::Frozen:
+ child = child->d()->frozen();
+ continue;
+ default:
+ Q_ASSERT(it->flags != 0);
+ Q_ASSERT(it->flags < InternalClassTransition::StructureChange);
+ child = child->addMember(it->id, attributesFromFlags(it->flags));
+ continue;
+ }
+ }
+
+ return child->d();
+}
+
+Heap::InternalClass *InternalClass::changeMember(
+ PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
if (!data.isEmpty())
data.resolve();
@@ -381,7 +471,7 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
}
if (data == propertyData.at(idx))
- return static_cast<Heap::InternalClass *>(this);
+ return this;
Transition temp = { { identifier }, nullptr, int(data.all()) };
Transition &t = lookupOrInsertTransition(temp);
@@ -394,7 +484,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
Q_ASSERT(!propertyData.at(idx).isAccessor());
// add a dummy entry for the accessor
- entry->setterIndex = newClass->size;
+ if (entry)
+ entry->setterIndex = newClass->size;
e->setterIndex = newClass->size;
addDummyEntry(newClass, *e);
}
@@ -403,7 +494,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
t.lookup = newClass;
Q_ASSERT(t.lookup);
- return newClass;
+
+ return cleanInternalClass(newClass);
}
Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
@@ -413,7 +505,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
- Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
+ Q_ASSERT(!proto || proto->internalClass->isUsedAsProto());
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange };
temp.prototype = proto;
@@ -427,8 +519,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
newClass->prototype = proto;
t.lookup = newClass;
-
- return newClass;
+ return prototype ? cleanInternalClass(newClass) : newClass;
}
Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
@@ -449,12 +540,14 @@ Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
t.lookup = newClass;
Q_ASSERT(t.lookup);
Q_ASSERT(newClass->vtable);
- return newClass;
+ return vtable == QV4::InternalClass::staticVTable()
+ ? newClass
+ : cleanInternalClass(newClass);
}
Heap::InternalClass *InternalClass::nonExtensible()
{
- if (!extensible)
+ if (!isExtensible())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible};
@@ -463,7 +556,7 @@ Heap::InternalClass *InternalClass::nonExtensible()
return t.lookup;
Heap::InternalClass *newClass = engine->newClass(this);
- newClass->extensible = false;
+ newClass->flags |= NotExtensible;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -500,7 +593,7 @@ Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAt
Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
- Transition temp = { { identifier }, nullptr, (int)data.flags() };
+ Transition temp = { { identifier }, nullptr, int(data.all()) };
Transition &t = lookupOrInsertTransition(temp);
if (entry) {
@@ -553,21 +646,23 @@ void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier)
changeMember(object, identifier, Attr_Invalid);
#ifndef QT_NO_DEBUG
- // we didn't remove the data slot, just made it inaccessible
- Q_ASSERT(object->internalClass()->size == oldClass->size);
+ // We didn't remove the data slot, just made it inaccessible.
+ // ... unless we've rebuilt the whole class. Then all the deleted properties are gone.
+ Q_ASSERT(object->internalClass()->numRedundantTransitions == 0
+ || object->internalClass()->size == oldClass->size);
#endif
}
Heap::InternalClass *InternalClass::sealed()
{
- if (isSealed)
+ if (isSealed())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isSealed);
+ Q_ASSERT(t.lookup && t.lookup->isSealed());
return t.lookup;
}
@@ -575,7 +670,7 @@ Heap::InternalClass *InternalClass::sealed()
Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
Heap::InternalClass *s = ic->d();
- if (!isFrozen) { // freezing also makes all properties non-configurable
+ if (!isFrozen()) { // freezing also makes all properties non-configurable
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
@@ -584,7 +679,7 @@ Heap::InternalClass *InternalClass::sealed()
s->propertyData.set(i, attrs);
}
}
- s->isSealed = true;
+ s->flags |= Sealed;
t.lookup = s;
return s;
@@ -592,14 +687,14 @@ Heap::InternalClass *InternalClass::sealed()
Heap::InternalClass *InternalClass::frozen()
{
- if (isFrozen)
+ if (isFrozen())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isFrozen);
+ Q_ASSERT(t.lookup && t.lookup->isFrozen());
return t.lookup;
}
@@ -616,7 +711,7 @@ Heap::InternalClass *InternalClass::frozen()
attrs.setConfigurable(false);
f->propertyData.set(i, attrs);
}
- f->isFrozen = true;
+ f->flags |= Frozen;
t.lookup = f;
return f;
@@ -640,7 +735,7 @@ InternalClass *InternalClass::cryopreserved()
bool InternalClass::isImplicitlyFrozen() const
{
- if (isFrozen)
+ if (isFrozen())
return true;
for (uint i = 0; i < size; ++i) {
@@ -656,7 +751,7 @@ bool InternalClass::isImplicitlyFrozen() const
Heap::InternalClass *InternalClass::asProtoClass()
{
- if (isUsedAsProto)
+ if (isUsedAsProto())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass };
@@ -665,7 +760,7 @@ Heap::InternalClass *InternalClass::asProtoClass()
return t.lookup;
Heap::InternalClass *newClass = engine->newClass(this);
- newClass->isUsedAsProto = true;
+ newClass->flags |= UsedAsProto;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -685,7 +780,7 @@ static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
void InternalClass::updateProtoUsage(Heap::Object *o)
{
- Q_ASSERT(isUsedAsProto);
+ Q_ASSERT(isUsedAsProto());
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
@@ -698,6 +793,9 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
if (ic->prototype)
ic->prototype->mark(stack);
+ if (ic->parent)
+ ic->parent->mark(stack);
+
ic->nameMap.mark(stack);
}
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index 403702ae55..a5a1471cf1 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -172,7 +172,7 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> {
uint size() const { return m_size; }
void setSize(uint s) { m_size = s; }
- PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
+ PropertyAttributes at(uint i) const { Q_ASSERT(data && i < m_alloc); return data[i]; }
void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
void mark(MarkStack *) {}
@@ -197,7 +197,7 @@ struct SharedInternalClassDataPrivate<PropertyKey> {
uint size() const;
void setSize(uint s);
- PropertyKey at(uint i);
+ PropertyKey at(uint i) const;
void set(uint i, PropertyKey t);
void mark(MarkStack *s);
@@ -290,24 +290,33 @@ struct InternalClassTransition
int flags;
enum {
// range 0-0xff is reserved for attribute changes
- NotExtensible = 0x100,
- VTableChange = 0x200,
- PrototypeChange = 0x201,
- ProtoClass = 0x202,
- Sealed = 0x203,
- Frozen = 0x204
+ StructureChange = 0x100,
+ NotExtensible = StructureChange | (1 << 0),
+ VTableChange = StructureChange | (1 << 1),
+ PrototypeChange = StructureChange | (1 << 2),
+ ProtoClass = StructureChange | (1 << 3),
+ Sealed = StructureChange | (1 << 4),
+ Frozen = StructureChange | (1 << 5),
};
bool operator==(const InternalClassTransition &other) const
{ return id == other.id && flags == other.flags; }
bool operator<(const InternalClassTransition &other) const
- { return id < other.id || (id == other.id && flags < other.flags); }
+ { return flags < other.flags || (flags == other.flags && id < other.id); }
};
namespace Heap {
struct InternalClass : Base {
+ enum Flag {
+ NotExtensible = 1 << 0,
+ Sealed = 1 << 1,
+ Frozen = 1 << 2,
+ UsedAsProto = 1 << 3,
+ };
+ enum { MaxRedundantTransitions = 255 };
+
ExecutionEngine *engine;
const VTable *vtable;
quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
@@ -323,10 +332,13 @@ struct InternalClass : Base {
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
uint size;
- bool extensible;
- bool isSealed;
- bool isFrozen;
- bool isUsedAsProto;
+ quint8 numRedundantTransitions;
+ quint8 flags;
+
+ bool isExtensible() const { return !(flags & NotExtensible); }
+ bool isSealed() const { return flags & Sealed; }
+ bool isFrozen() const { return flags & Frozen; }
+ bool isUsedAsProto() const { return flags & UsedAsProto; }
void init(ExecutionEngine *engine);
void init(InternalClass *other);
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index d3ea50867a..db6fb39695 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -41,6 +41,7 @@
#include "qv4jscall_p.h"
#include "qv4string_p.h"
#include <private/qv4identifiertable_p.h>
+#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
@@ -144,47 +145,76 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va
return l->resolvePrimitiveGetter(engine, object);
}
+static inline void setupObjectLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
+{
+ Heap::InternalClass *ic1 = first.objectLookup.ic;
+ const uint offset1 = first.objectLookup.offset;
+ Heap::InternalClass *ic2 = second.objectLookup.ic;
+ const uint offset2 = second.objectLookup.offset;
+
+ l->objectLookupTwoClasses.ic = ic1;
+ l->objectLookupTwoClasses.ic2 = ic2;
+ l->objectLookupTwoClasses.offset = offset1;
+ l->objectLookupTwoClasses.offset2 = offset2;
+}
+
+static inline void setupProtoLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
+{
+ const quintptr protoId1 = first.protoLookup.protoId;
+ const Value *data1 = first.protoLookup.data;
+ const quintptr protoId2 = second.protoLookup.protoId;
+ const Value *data2 = second.protoLookup.data;
+
+ l->protoLookupTwoClasses.protoId = protoId1;
+ l->protoLookupTwoClasses.protoId2 = protoId2;
+ l->protoLookupTwoClasses.data = data1;
+ l->protoLookupTwoClasses.data2 = data2;
+}
+
ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (const Object *o = object.as<Object>()) {
- Lookup first = *l;
- Lookup second = *l;
- ReturnedValue result = second.resolveGetter(engine, o);
-
- if (first.getter == getter0Inline && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
- l->objectLookupTwoClasses.ic = first.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = second.objectLookup.ic;
- l->objectLookupTwoClasses.offset = first.objectLookup.offset;
- l->objectLookupTwoClasses.offset2 = second.objectLookup.offset;
- l->getter = second.getter == getter0Inline ? getter0Inlinegetter0Inline : getter0Inlinegetter0MemberData;
+ // Do the resolution on a second lookup, then merge.
+ Lookup second;
+ memset(&second, 0, sizeof(Lookup));
+ second.nameIndex = l->nameIndex;
+ second.getter = getterGeneric;
+ const ReturnedValue result = second.resolveGetter(engine, o);
+
+ if (l->getter == getter0Inline
+ && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
+ setupObjectLookupTwoClasses(l, *l, second);
+ l->getter = (second.getter == getter0Inline)
+ ? getter0Inlinegetter0Inline
+ : getter0Inlinegetter0MemberData;
return result;
}
- if (first.getter == getter0MemberData && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
- l->objectLookupTwoClasses.ic = second.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = first.objectLookup.ic;
- l->objectLookupTwoClasses.offset = second.objectLookup.offset;
- l->objectLookupTwoClasses.offset2 = first.objectLookup.offset;
- l->getter = second.getter == getter0Inline ? getter0Inlinegetter0MemberData : getter0MemberDatagetter0MemberData;
+
+ if (l->getter == getter0MemberData
+ && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
+ setupObjectLookupTwoClasses(l, second, *l);
+ l->getter = (second.getter == getter0Inline)
+ ? getter0Inlinegetter0MemberData
+ : getter0MemberDatagetter0MemberData;
return result;
}
- if (first.getter == getterProto && second.getter == getterProto) {
- l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
- l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
- l->protoLookupTwoClasses.data = first.protoLookup.data;
- l->protoLookupTwoClasses.data2 = second.protoLookup.data;
+
+
+ if (l->getter == getterProto && second.getter == getterProto) {
+ setupProtoLookupTwoClasses(l, *l, second);
l->getter = getterProtoTwoClasses;
return result;
}
- if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
- l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
- l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
- l->protoLookupTwoClasses.data = first.protoLookup.data;
- l->protoLookupTwoClasses.data2 = second.protoLookup.data;
+
+ if (l->getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
+ setupProtoLookupTwoClasses(l, *l, second);
l->getter = getterProtoAccessorTwoClasses;
return result;
}
+ // If any of the above options were true, the propertyCache was inactive.
+ second.releasePropertyCache();
}
l->getter = getterFallback;
@@ -371,7 +401,19 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
+}
+ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qobjectLookup.propertyCache->release();
+ lookup->qobjectLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ return QObjectWrapper::lookupGetterImpl(
+ lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
}
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
@@ -463,23 +505,30 @@ bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, co
bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Lookup first = *l;
- Lookup second = *l;
+ // A precondition of this method is that l->objectLookup is the active variant of the union.
+ Q_ASSERT(l->setter == setter0MemberData || l->setter == setter0Inline);
if (object.isObject()) {
+
+ // As l->objectLookup is active, we can stash some members here, before resolving.
+ Heap::InternalClass *ic = l->objectLookup.ic;
+ const uint index = l->objectLookup.index;
+
if (!l->resolveSetter(engine, static_cast<Object *>(&object), value)) {
l->setter = setterFallback;
return false;
}
if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) {
- l->objectLookupTwoClasses.ic = first.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = second.objectLookup.ic;
- l->objectLookupTwoClasses.offset = first.objectLookup.index;
- l->objectLookupTwoClasses.offset2 = second.objectLookup.index;
+ l->objectLookupTwoClasses.ic = ic;
+ l->objectLookupTwoClasses.ic2 = ic;
+ l->objectLookupTwoClasses.offset = index;
+ l->objectLookupTwoClasses.offset2 = index;
l->setter = setter0setter0;
return true;
}
+
+ l->releasePropertyCache();
}
l->setter = setterFallback;
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 31c90b31f6..6241a81850 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -56,11 +56,17 @@
#include "qv4context_p.h"
#include "qv4object_p.h"
#include "qv4internalclass_p.h"
+#include "qv4qmlcontext_p.h"
+#include <private/qqmltypewrapper_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
+// Note: We cannot hide the copy ctor and assignment operator of this class because it needs to
+// be trivially copyable. But you should never ever copy it. There are refcounted members
+// in there.
struct Q_QML_PRIVATE_EXPORT Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -187,6 +193,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -216,6 +223,20 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
void clear() {
memset(&markDef, 0, sizeof(markDef));
}
+
+ void releasePropertyCache()
+ {
+ if (getter == getterQObject
+ || getter == QQmlTypeWrapper::lookupSingletonProperty
+ || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
+ || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
+ if (QQmlPropertyCache *pc = qobjectLookup.propertyCache)
+ pc->release();
+ } else if (getter == QQmlValueTypeWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = qgadgetLookup.propertyCache)
+ pc->release();
+ }
+ }
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
@@ -223,6 +244,33 @@ Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
// across 32-bit and 64-bit (matters when cross-compiling).
Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0);
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData)
+{
+ lookup->releasePropertyCache();
+ Q_ASSERT(ddata->propertyCache != nullptr);
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+}
+
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData,
+ const Object *self)
+{
+ lookup->qobjectLookup.ic = self->internalClass();
+ setupQObjectLookup(lookup, ddata, propertyData);
+}
+
+
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData,
+ const Object *self, const Object *qmlType)
+{
+ lookup->qobjectLookup.qmlTypeIc = qmlType->internalClass();
+ setupQObjectLookup(lookup, ddata, propertyData, self);
+}
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index 07440047d4..c6b74e7aba 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -47,7 +47,6 @@
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qthreadstorage.h>
-#include <math.h>
#include <cmath>
using namespace QV4;
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index b723141caa..ac90a7fdb9 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -61,17 +61,52 @@ DEFINE_OBJECT_VTABLE(Object);
void Object::setInternalClass(Heap::InternalClass *ic)
{
- d()->internalClass.set(engine(), ic);
- if (ic->isUsedAsProto)
- ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
- uint nInline = d()->vtable()->nInlineProperties;
- if (ic->size <= nInline)
- return;
- bool hasMD = d()->memberData != nullptr;
- uint requiredSize = ic->size - nInline;
- if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize))
- d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData));
+ Heap::Object *p = d();
+
+ if (ic->numRedundantTransitions < p->internalClass.get()->numRedundantTransitions) {
+ // IC was rebuilt. The indices are different now. We need to move everything.
+
+ Scope scope(engine());
+
+ // We allocate before setting the new IC. Protect it from GC.
+ Scoped<InternalClass> newIC(scope, ic);
+
+ // Pick the members of the old IC that are still valid in the new IC.
+ // Order them by index in memberData (or inline data).
+ Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size));
+ for (uint i = 0; i < ic->size; ++i)
+ newMembers->set(scope.engine, i, get(ic->nameMap.at(i)));
+
+ p->internalClass.set(scope.engine, ic);
+ const uint nInline = p->vtable()->nInlineProperties;
+
+ if (ic->size > nInline)
+ p->memberData.set(scope.engine, MemberData::allocate(ic->engine, ic->size - nInline));
+ else
+ p->memberData.set(scope.engine, nullptr);
+
+ const auto &memberValues = newMembers->d()->values;
+ for (uint i = 0; i < ic->size; ++i)
+ setProperty(i, memberValues[i]);
+ } else {
+ // The old indices are still the same. No need to move any values.
+ // We may need to re-allocate, though.
+
+ p->internalClass.set(ic->engine, ic);
+ const uint nInline = p->vtable()->nInlineProperties;
+ if (ic->size > nInline) {
+ const uint requiredSize = ic->size - nInline;
+ if ((p->memberData ? p->memberData->values.size : 0) < requiredSize) {
+ p->memberData.set(ic->engine, MemberData::allocate(
+ ic->engine, requiredSize, p->memberData));
+ }
+ }
+ }
+
+ if (ic->isUsedAsProto())
+ ic->updateProtoUsage(p);
+
}
void Object::getProperty(const InternalClassEntry &entry, Property *p) const
@@ -958,7 +993,7 @@ bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property
bool Object::virtualIsExtensible(const Managed *m)
{
- return m->d()->internalClass->extensible;
+ return m->d()->internalClass->isExtensible();
}
bool Object::virtualPreventExtensions(Managed *m)
@@ -982,7 +1017,7 @@ bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
Heap::Object *protod = proto ? proto->d() : nullptr;
if (current == protod)
return true;
- if (!o->internalClass()->extensible)
+ if (!o->internalClass()->isExtensible())
return false;
Heap::Object *p = protod;
while (p) {
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index 26e1074fe3..1f518966d6 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -50,8 +50,8 @@ FunctionLocation FunctionCall::resolveLocation() const
{
return FunctionLocation(m_function->name()->toQString(),
m_function->executableCompilationUnit()->fileName(),
- m_function->compiledFunction->location.line,
- m_function->compiledFunction->location.column);
+ m_function->compiledFunction->location.line(),
+ m_function->compiledFunction->location.column());
}
FunctionCallProperties FunctionCall::properties() const
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index ecaff72b22..ac1cc81771 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -116,6 +116,8 @@ struct ResolveThenableEvent : public QEvent
} // namespace QV4
QT_END_NAMESPACE
+#include "moc_qv4promiseobject_p.cpp"
+
ReactionHandler::ReactionHandler(QObject *parent)
: QObject(parent)
{}
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index b2a2ec3dea..81f2331666 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -51,6 +51,7 @@
//
#include <private/qv4global_p.h>
+#include <QtCore/qhashfunctions.h>
QT_BEGIN_NAMESPACE
@@ -145,6 +146,10 @@ public:
bool operator ==(const PropertyKey &other) const { return val == other.val; }
bool operator !=(const PropertyKey &other) const { return val != other.val; }
bool operator <(const PropertyKey &other) const { return val < other.val; }
+ inline friend uint qHash(const PropertyKey &key, uint seed) noexcept
+ {
+ return QT_PREPEND_NAMESPACE(qHash)(key.val, seed);
+ }
};
}
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 6eece147a6..be6c614eda 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -294,11 +294,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
QQmlData *ddata = QQmlData::get(scopeObject, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
- const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
- lookup->qobjectLookup.ic = That->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = propertyData;
+ QV4::setupQObjectLookup(lookup, ddata, propertyData, val->objectValue());
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
}
}
@@ -326,11 +322,8 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
QQmlData *ddata = QQmlData::get(context->contextObject, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject)));
- const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
- lookup->qobjectLookup.ic = That->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = propertyData;
+ QV4::setupQObjectLookup(lookup, ddata, propertyData,
+ val->objectValue());
lookup->qmlContextPropertyGetter = contextGetterFunction;
}
} else if (originalLookup) {
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 9899c9274e..94613598af 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -875,26 +875,11 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
}
- lookup->qobjectLookup.ic = This->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
- lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ QV4::setupQObjectLookup(lookup, ddata, property, This);
+ lookup->getter = QV4::Lookup::getterQObject;
return lookup->getter(lookup, engine, *object);
}
-ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
-{
- const auto revertLookup = [lookup, engine, &object]() {
- lookup->qobjectLookup.propertyCache->release();
- lookup->qobjectLookup.propertyCache = nullptr;
- lookup->getter = Lookup::getterGeneric;
- return Lookup::getterGeneric(lookup, engine, object);
- };
-
- return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
-}
-
bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
@@ -1160,8 +1145,7 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markSta
void QObjectWrapper::destroyObject(bool lastCall)
{
Heap::QObjectWrapper *h = d();
- if (!h->internalClass)
- return; // destroyObject already got called
+ Q_ASSERT(h->internalClass);
if (h->object()) {
QQmlData *ddata = QQmlData::get(h->object(), false);
@@ -1191,7 +1175,7 @@ void QObjectWrapper::destroyObject(bool lastCall)
}
}
- h->~Data();
+ h->destroy();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index ac9cad2bdb..10ba79a5e9 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -183,7 +183,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
- static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
+
template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 2a6c61f044..39a0b03dec 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -76,7 +76,10 @@ struct CallArgs {
static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
{
- int len = o->getLength();
+ int len = scope.engine->safeForAllocLength(o->getLength());
+ if (scope.engine->hasException)
+ return {nullptr, 0};
+
Value *arguments = scope.alloc(len);
for (int i = 0; i < len; ++i) {
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 5fc94b9ddd..cf2e4ec1dd 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1530,6 +1530,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
if (done->booleanValue())
break;
++argCount;
+ constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
+ if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
+ scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
+ return { nullptr, 0 };
+ }
v = scope.alloc<Scope::Uninitialized>();
}
}
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index aff8844bb0..f84718b48f 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -720,13 +720,20 @@ DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealList);
}
#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType);
-void SequencePrototype::init()
+static bool registerAllSequenceTypes()
{
FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE)
+ return true;
+}
+#undef REGISTER_QML_SEQUENCE_METATYPE
+
+void SequencePrototype::init()
+{
+ static const bool registered = registerAllSequenceTypes();
+ Q_UNUSED(registered);
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0);
}
-#undef REGISTER_QML_SEQUENCE_METATYPE
ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
{
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index bf098fef26..b5b406b317 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -533,9 +533,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const
if (std::isnan(position))
position = +qInf();
else
- position = trunc(position);
+ position = std::trunc(position);
- int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
+ int pos = std::trunc(qMin(qMax(position, 0.0), double(value.length())));
if (!searchString.isEmpty() && pos == value.length())
--pos;
if (searchString.isNull() && pos == 0)
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index fb103d492d..d05f48d340 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -442,7 +442,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
#if QT_CONFIG(qml_jit)
if (debugger == nullptr) {
- if (function->jittedCode == nullptr) {
+ // Check for codeRef here. In rare cases the JIT compilation may fail, which leaves us
+ // with a (useless) codeRef, but no jittedCode. In that case, don't try to JIT again every
+ // time we execute the function, but just interpret instead.
+ if (function->codeRef == nullptr) {
if (engine->canJIT(function))
QV4::JIT::BaselineJIT(function).generate();
else
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 06caf04e5a..da149a67c4 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -981,7 +981,7 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) {
for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
- if (!it.value().isNullOrUndefined())
+ if (it.value().isNullOrUndefined())
it = multiplyWrappedQObjects->erase(it);
else
++it;
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 907825d28c..2b294ffff6 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -146,6 +146,14 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
break;
case QQmlComponent::Ready: {
auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties);
+
+ if (c->isError()) {
+ qWarning() << "QQmlApplicationEngine failed to create component";
+ warning(c->errors());
+ q->objectCreated(nullptr, c->url());
+ break;
+ }
+
objects << newObj;
QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); });
q->objectCreated(objects.constLast(), c->url());
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index b9566d5862..d7bd93aa0d 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -353,7 +353,9 @@ public:
QQmlSourceLocation sourceLocation() const override final
{
- return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
+ return QQmlSourceLocation(
+ m_compilationUnit->fileName(), m_binding->valueLocation.line(),
+ m_binding->valueLocation.column());
}
@@ -532,8 +534,8 @@ QString QQmlBinding::expressionIdentifier() const
{
if (auto f = function()) {
QString url = f->sourceFile();
- uint lineNumber = f->compiledFunction->location.line;
- uint columnNumber = f->compiledFunction->location.column;
+ uint lineNumber = f->compiledFunction->location.line();
+ uint columnNumber = f->compiledFunction->location.column();
return url + QString::asprintf(":%u:%u", lineNumber, columnNumber);
}
@@ -645,7 +647,10 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD
Q_ASSERT(valueTypeMetaObject);
QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
+
+ // valueTypeData is expected to be local here. It must not be shared with other threads.
valueTypeData->setPropType(vtProp.userType());
+
valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
}
}
@@ -748,7 +753,11 @@ QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlProper
if (property && property->isQObject())
return new QObjectPointerBinding(engine, property->propType());
- const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType;
+ // If the property is not resolved at this point, you get a binding of unknown type.
+ // This has been the case for a long time and we keep it like this in Qt5 to be bug-compatible.
+ const int type = (property && property->isResolved())
+ ? property->propType()
+ : QMetaType::UnknownType;
if (type == qMetaTypeId<QQmlBinding *>()) {
return new QQmlBindingBinding;
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 9ee4fdbe41..6e0956472f 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -375,10 +375,15 @@ bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString&
{
QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(component, name, requiredProperties());
QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
- if (!prop.isValid() || !privProp->writeValueProperty(value, {})) {
+ const bool isValid = prop.isValid();
+ if (!isValid || !privProp->writeValueProperty(value, {})) {
QQmlError error{};
error.setUrl(url);
- error.setDescription(QLatin1String("Could not set property %1").arg(name));
+ if (isValid)
+ error.setDescription(QLatin1String("Could not set initial property %1").arg(name));
+ else
+ error.setDescription(QLatin1String("Setting initial properties failed: %2 does not have a property called %1").arg(name,
+ QQmlMetaType::prettyTypeName(component)));
state.errors.push_back(error);
return false;
} else
@@ -830,6 +835,14 @@ QObject *QQmlComponent::create(QQmlContext *context)
properties with \a initialProperties. \a context specifies the context
where the object instance is to be created.
+ \omit
+ TODO: also mention errorString() when QTBUG-93239 is fixed
+ \endomit
+
+ If any of the \c initialProperties cannot be set, \l isError() will return
+ \c true, and the \l errors() function can be used to
+ get detailed information about the error(s).
+
\sa QQmlComponent::create
\since 5.14
*/
@@ -1423,8 +1436,10 @@ QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredP
}
error.setDescription(description);
error.setUrl(unsetRequiredProperty.fileUrl);
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ unsetRequiredProperty.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ unsetRequiredProperty.location.column()));
return error;
}
@@ -1762,3 +1777,4 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
QT_END_NAMESPACE
#include "moc_qqmlcomponent.cpp"
+#include "moc_qqmlcomponentattached_p.cpp"
diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h
index 8ecd9da17d..d7c04a82f1 100644
--- a/src/qml/qml/qqmlcomponentattached_p.h
+++ b/src/qml/qml/qqmlcomponentattached_p.h
@@ -58,6 +58,7 @@
QT_BEGIN_NAMESPACE
+// implemented in qqmlcomponent.cpp
class Q_QML_PRIVATE_EXPORT QQmlComponentAttached : public QObject
{
Q_OBJECT
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index 305c610a8f..f293ff8e9c 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -82,11 +82,11 @@ QQmlContextPrivate::QQmlContextPrivate()
context->setContextProperty("myModel", &modelData);
QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
+ component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
QObject *window = component.create(context);
\endcode
- Note it is the responsibility of the creator to delete any QQmlContext it
+ \note It is the responsibility of the creator to delete any QQmlContext it
constructs. If the \c context object in the example is no longer needed when the
\c window component instance is destroyed, the \c context must be destroyed explicitly.
The simplest way to ensure this is to set \c window as the parent of \c context.
@@ -102,10 +102,10 @@ QQmlContextPrivate::QQmlContextPrivate()
object.
\code
- class MyDataSet : ... {
- ...
+ class MyDataSet : public QObject {
+ // ...
Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
- ...
+ // ...
};
MyDataSet myDataSet;
@@ -114,7 +114,7 @@ QQmlContextPrivate::QQmlContextPrivate()
context->setContextObject(&myDataSet);
QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
+ component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
component.create(context);
\endcode
@@ -138,10 +138,10 @@ QQmlContextPrivate::QQmlContextPrivate()
QQmlContext *context1 = new QQmlContext(engine.rootContext());
QQmlContext *context2 = new QQmlContext(context1);
- context1->setContextProperty("a", 12);
- context1->setContextProperty("b", 12);
+ context1->setContextProperty("a", 9001);
+ context1->setContextProperty("b", 9001);
- context2->setContextProperty("b", 15);
+ context2->setContextProperty("b", 42);
\endcode
While QML objects instantiated in a context are not strictly owned by that
@@ -235,7 +235,7 @@ bool QQmlContext::isValid() const
}
/*!
- Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the
+ Return the context's QQmlEngine, or \nullptr if the context has no QQmlEngine or the
QQmlEngine was destroyed.
*/
QQmlEngine *QQmlContext::engine() const
@@ -245,7 +245,7 @@ QQmlEngine *QQmlContext::engine() const
}
/*!
- Return the context's parent QQmlContext, or 0 if this context has no
+ Return the context's parent QQmlContext, or \nullptr if this context has no
parent or if the parent has been destroyed.
*/
QQmlContext *QQmlContext::parentContext() const
@@ -255,7 +255,7 @@ QQmlContext *QQmlContext::parentContext() const
}
/*!
- Return the context object, or 0 if there is no context object.
+ Return the context object, or \nullptr if there is no context object.
*/
QObject *QQmlContext::contextObject() const
{
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index 5e6e1187e2..172557b7c2 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -102,8 +102,8 @@ void QQmlCustomParser::clearErrors()
void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
exceptions << error;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 54cc07af2e..852a673ebd 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -193,7 +193,7 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
\endcode
*/
-bool QQmlEnginePrivate::qml_debugging_enabled = false;
+std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
bool QQmlEnginePrivate::s_designerMode = false;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
@@ -1063,6 +1063,16 @@ QQmlEngine::~QQmlEngine()
Once the component cache has been cleared, components must be loaded before
any new objects can be created.
+ \note Any existing objects created from QML components retain their types,
+ even if you clear the component cache. This includes singleton objects. If you
+ create more objects from the same QML code after clearing the cache, the new
+ objects will be of different types than the old ones. Assigning such a new
+ object to a property of its declared type belonging to an object created
+ before clearing the cache won't work.
+
+ As a general rule of thumb, make sure that no objects created from QML
+ components are alive when you clear the component cache.
+
\sa trimComponentCache()
*/
void QQmlEngine::clearComponentCache()
@@ -1802,7 +1812,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCo
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
const QQmlPropertyData *property = propertyData.at(i);
- if (property && binding->flags & QV4::CompiledData::Binding::IsDeferredBinding)
+ if (property && binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
deferData->bindings.insert(property->coreIndex(), binding);
}
@@ -2661,4 +2671,6 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
QT_END_NAMESPACE
+#include "moc_qqmlengine_p.cpp"
+
#include "moc_qqmlengine.cpp"
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 263c69e2d8..9160fd8aa2 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -80,6 +80,8 @@
#include <private/qjsengine_p.h>
#include <private/qqmldirparser_p.h>
+#include <atomic>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -267,7 +269,7 @@ public:
static bool designerMode();
static void activateDesignerMode();
- static bool qml_debugging_enabled;
+ static std::atomic<bool> qml_debugging_enabled;
mutable QMutex networkAccessManagerMutex;
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index f6a5afb891..e9f8bce942 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -204,7 +204,7 @@ QQmlExpression::~QQmlExpression()
}
/*!
- Returns the QQmlEngine this expression is associated with, or 0 if there
+ Returns the QQmlEngine this expression is associated with, or \nullptr if there
is no association or the QQmlEngine has been destroyed.
*/
QQmlEngine *QQmlExpression::engine() const
@@ -214,7 +214,7 @@ QQmlEngine *QQmlExpression::engine() const
}
/*!
- Returns the QQmlContext this expression is associated with, or 0 if there
+ Returns the QQmlContext this expression is associated with, or \nullptr if there
is no association or the QQmlContext has been destroyed.
*/
QQmlContext *QQmlExpression::context() const
diff --git a/src/qml/qml/qqmlfileselector.cpp b/src/qml/qml/qqmlfileselector.cpp
index 32dce8b4bc..c386f2727b 100644
--- a/src/qml/qml/qqmlfileselector.cpp
+++ b/src/qml/qml/qqmlfileselector.cpp
@@ -148,7 +148,7 @@ QQmlFileSelectorPrivate::~QQmlFileSelectorPrivate()
/*!
Sets the QFileSelector instance for use by the QQmlFileSelector to \a selector.
QQmlFileSelector does not take ownership of the new QFileSelector. To reset QQmlFileSelector
- to use its internal QFileSelector instance, call setSelector(0).
+ to use its internal QFileSelector instance, call setSelector(\nullptr).
*/
void QQmlFileSelector::setSelector(QFileSelector *selector)
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 6d4fba0aa4..e7263d1850 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -46,7 +46,6 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qpluginloader.h>
#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qreadwritelock.h>
#include <QtQml/qqmlextensioninterface.h>
#include <QtQml/qqmlextensionplugin.h>
#include <private/qqmlextensionplugin_p.h>
@@ -730,7 +729,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (containingType.isValid()) {
// we currently cannot reference a Singleton inside itself
// in that case, containingType is still invalid
- if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1) {
+ int icID = containingType.lookupInlineComponentIdByName(typeStr);
+ if (icID != -1) {
*type_return = containingType.lookupInlineComponentById(icID);
} else {
auto icType = createICType();
@@ -1408,11 +1408,16 @@ QQmlImports::LocalQmldirResult QQmlImportsPrivate::locateLocalQmldir(
if (!absoluteFilePath.isEmpty()) {
QString url;
const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1);
- if (absolutePath.at(0) == Colon)
+ if (absolutePath.at(0) == Colon) {
url = QLatin1String("qrc") + absolutePath;
- else
+ } else {
url = QUrl::fromLocalFile(absolutePath.toString()).toString();
-
+ // This handles the UNC path case as when the path is retrieved from the QUrl it
+ // will convert the host name from upper case to lower case. So the absoluteFilePath
+ // is changed at this point to make sure it will match later on in that case.
+ if (absoluteFilePath.startsWith(QLatin1String("//")))
+ absoluteFilePath = QUrl::fromLocalFile(absoluteFilePath).toString(QUrl::RemoveScheme);
+ }
QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
cache->versionMajor = vmaj;
cache->versionMinor = vmin;
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 0ad013e90b..3cbd5e17d2 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -279,6 +279,20 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
// get a copy of the engine pointer as it might get reset;
QQmlEnginePrivate *enginePriv = this->enginePriv;
+ // Incubating objects takes quite a bit more stack space than our usual V4 function
+ enum { EstimatedSizeInV4Frames = 2 };
+ QV4::ExecutionEngineCallDepthRecorder<EstimatedSizeInV4Frames> callDepthRecorder(
+ compilationUnit->engine);
+ if (callDepthRecorder.hasOverflow()) {
+ QQmlError error;
+ error.setMessageType(QtCriticalMsg);
+ error.setUrl(compilationUnit->url());
+ error.setDescription(QQmlComponent::tr("Maximum call stack size exceeded."));
+ errors << error;
+ progress = QQmlIncubatorPrivate::Completed;
+ goto finishIncubate;
+ }
+
if (!vmeGuard.isOK()) {
QQmlError error;
error.setMessageType(QtInfoMsg);
@@ -330,7 +344,7 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
ddata->rootObjectInCreation = false;
if (q) {
q->setInitialState(result);
- if (!creator->requiredProperties().empty()) {
+ if (creator && !creator->requiredProperties().empty()) {
const auto& unsetRequiredProperties = creator->requiredProperties();
for (const auto& unsetRequiredProperty: unsetRequiredProperties)
errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index 5609ba8aa8..b6b4372faf 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -94,10 +94,11 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex);
object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
- object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
- object->isInlineComponent = serializedObject->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
- object->flags = serializedObject->flags;
- object->id = serializedObject->id;
+ object->defaultPropertyIsAlias = serializedObject->hasAliasAsDefaultProperty();
+ object->isInlineComponent = serializedObject->hasFlag(
+ QV4::CompiledData::Object::IsInlineComponentRoot);
+ object->flags = serializedObject->flags();
+ object->id = serializedObject->objectId();
object->location = serializedObject->location;
object->locationOfIdProperty = serializedObject->locationOfIdProperty;
@@ -108,7 +109,7 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
QmlIR::Binding *b = pool->New<QmlIR::Binding>();
*static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i];
object->bindings->append(b);
- if (b->type == QV4::CompiledData::Binding::Type_Script) {
+ if (b->type() == QV4::CompiledData::Binding::Type_Script) {
functionIndices.append(b->value.compiledScriptIndex);
b->value.compiledScriptIndex = functionIndices.count() - 1;
@@ -208,6 +209,13 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
object->inlineComponents->append(ic);
}
+ const QV4::CompiledData::RequiredPropertyExtraData *serializedRequiredPropertyExtraData = serializedObject->requiredPropertyExtraDataTable();
+ for (uint i = 0u; i < serializedObject->nRequiredPropertyExtraData; ++i, ++serializedRequiredPropertyExtraData) {
+ QmlIR::RequiredPropertyExtraData *extra = pool->New<QmlIR::RequiredPropertyExtraData>();
+ *static_cast<QV4::CompiledData::RequiredPropertyExtraData *>(extra) = *serializedRequiredPropertyExtraData;
+ object->requiredPropertyExtraDatas->append(extra);
+ }
+
return object;
}
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 7af6af276a..f55bbe7ffc 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -891,7 +891,7 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
*/
/*!
- \qmlproperty enumeration QtQml::Locale::NumberOption
+ \qmlproperty enumeration QtQml::Locale::numberOptions
Holds a set of options for number-to-string and
string-to-number conversions.
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
index b59a26e17e..98dfe62f0c 100644
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -129,7 +129,7 @@ void QQmlLoggingCategory::componentComplete()
{
m_initialized = true;
if (m_name.isNull()) {
- qmlWarning(this) << QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !");
+ qmlWarning(this) << QLatin1String("Declaring the name of a LoggingCategory is mandatory and cannot be changed later");
} else {
QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData(), QtMsgType(m_defaultLogLevel)));
m_category.swap(category);
@@ -138,23 +138,30 @@ void QQmlLoggingCategory::componentComplete()
void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel)
{
+ if (m_defaultLogLevel == defaultLogLevel)
+ return;
+
if (m_initialized) {
- qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created");
+ qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed");
return;
}
m_defaultLogLevel = defaultLogLevel;
}
-
void QQmlLoggingCategory::setName(const QString &name)
{
+ const QByteArray newName = name.toUtf8();
+
+ if (m_name == newName)
+ return;
+
if (m_initialized) {
- qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the Item is created");
+ qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the component is completed");
return;
}
- m_name = name.toUtf8();
+ m_name = newName;
}
#include "moc_qqmlloggingcategory_p.cpp"
diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp
index a967f46b12..d273849ccb 100644
--- a/src/qml/qml/qqmlmetaobject.cpp
+++ b/src/qml/qml/qqmlmetaobject.cpp
@@ -232,8 +232,6 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
Q_ASSERT(!_m.isNull() && index >= 0);
if (_m.isT1()) {
- typedef QQmlPropertyCacheMethodArguments A;
-
QQmlPropertyCache *c = _m.asT1();
Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
@@ -242,19 +240,16 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
- if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
- return static_cast<A *>(rv->arguments())->arguments;
+ if (QQmlPropertyCacheMethodArguments *args = rv->arguments())
+ return args->arguments;
const QMetaObject *metaObject = c->createMetaObject();
Q_ASSERT(metaObject);
QMetaMethod m = metaObject->method(index);
int argc = m.parameterCount();
- if (!rv->arguments()) {
- A *args = c->createArgumentsObject(argc, m.parameterNames());
- rv->setArguments(args);
- }
- A *args = static_cast<A *>(rv->arguments());
+
+ QQmlPropertyCacheMethodArguments *args = c->createArgumentsObject(argc, m.parameterNames());
QList<QByteArray> argTypeNames; // Only loaded if needed
@@ -280,8 +275,14 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
}
args->arguments[ii + 1] = type;
}
- args->argumentsValid = true;
- return static_cast<A *>(rv->arguments())->arguments;
+
+ // If we cannot set it, then another thread has set it in the mean time.
+ // Just return that one, then. We don't have to delete the arguments object
+ // we've created as it's tracked in a linked list.
+ if (rv->setArguments(args))
+ return args->arguments;
+ else
+ return rv->arguments()->arguments;
} else {
QMetaMethod m = _m.asT2()->method(index);
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index be4a79297c..40300b1fe3 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -1260,6 +1260,16 @@ void QQmlMetaType::unregisterType(int typeIndex)
}
}
+static bool hasActiveInlineComponents(const QQmlTypePrivate *d)
+{
+ for (const QQmlType &ic : qAsConst(d->objectIdToICType)) {
+ const QQmlTypePrivate *icPriv = ic.priv();
+ if (icPriv && icPriv->count() > 1)
+ return true;
+ }
+ return false;
+}
+
void QQmlMetaType::freeUnusedTypesAndCaches()
{
QQmlMetaTypeDataPtr data;
@@ -1274,7 +1284,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
QList<QQmlType>::Iterator it = data->types.begin();
while (it != data->types.end()) {
const QQmlTypePrivate *d = (*it).priv();
- if (d && d->count() == 1) {
+ if (d && d->count() == 1 && !hasActiveInlineComponents(d)) {
deletedAtLeastOneType = true;
removeQQmlTypePrivate(data->idToType, d);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 1c8f2b2091..33fbab6e6b 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -280,7 +280,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
if (binding) {
Q_ASSERT(qmlProperty);
- Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding);
+ Q_ASSERT(binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding));
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
@@ -342,7 +342,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
int propertyType = property->propType();
if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) {
propertyType = QMetaType::Int;
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
@@ -356,18 +356,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
auto assertOrNull = [&](bool ok)
{
- Q_ASSERT(ok || binding->type == QV4::CompiledData::Binding::Type_Null);
+ Q_ASSERT(ok || binding->type() == QV4::CompiledData::Binding::Type_Null);
Q_UNUSED(ok);
};
- auto assertType = [&](QV4::CompiledData::Binding::ValueType type)
+ auto assertType = [&](QV4::CompiledData::Binding::Type type)
{
- Q_ASSERT(binding->type == type || binding->type == QV4::CompiledData::Binding::Type_Null);
+ Q_ASSERT(binding->type()== type || binding->type() == QV4::CompiledData::Binding::Type_Null);
Q_UNUSED(type);
};
if (property->isQObject()) {
- if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
QObject *value = nullptr;
const bool ok = property->writeProperty(_qobject, &value, propertyWriteFlags);
Q_ASSERT(ok);
@@ -378,7 +378,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
switch (propertyType) {
case QMetaType::QVariant: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Number) {
double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) == n) {
if (property->isVarProperty()) {
@@ -396,14 +396,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
}
- } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+ } else if (binding->type() == QV4::CompiledData::Binding::Type_Boolean) {
if (property->isVarProperty()) {
_vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromBoolean(binding->valueAsBoolean()));
} else {
QVariant value(binding->valueAsBoolean());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
- } else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ } else if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
if (property->isVarProperty()) {
_vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::nullValue());
} else {
@@ -659,18 +659,24 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
} else if (property->propType() == qMetaTypeId<QJSValue>()) {
QJSValue value;
- if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Boolean:
value = QJSValue(binding->valueAsBoolean());
- } else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = compilationUnit->bindingValueAsNumber(binding);
- if (double(int(n)) == n) {
+ break;
+ case QV4::CompiledData::Binding::Type_Number: {
+ const double n = compilationUnit->bindingValueAsNumber(binding);
+ if (double(int(n)) == n)
value = QJSValue(int(n));
- } else
+ else
value = QJSValue(n);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ break;
+ }
+ case QV4::CompiledData::Binding::Type_Null:
value = QJSValue::NullValue;
- } else {
+ break;
+ default:
value = QJSValue(compilationUnit->bindingValueAsString(binding));
+ break;
}
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -679,8 +685,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
// otherwise, try a custom type assignment
QString stringValue = compilationUnit->bindingValueAsString(binding);
QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
- Q_ASSERT(converter);
- QVariant value = (*converter)(stringValue);
+ QVariant value = converter ? (*converter)(stringValue) : QVariant();
QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex());
if (value.isNull() || metaProperty.userType() != property->propType()) {
@@ -718,8 +723,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) {
QV4::CompiledData::Binding idBinding;
idBinding.propertyNameIndex = 0; // Not used
- idBinding.flags = 0;
- idBinding.type = QV4::CompiledData::Binding::Type_String;
+ idBinding.clearFlags();
+ idBinding.setType(QV4::CompiledData::Binding::Type_String);
idBinding.stringIndex = _compiledObject->idNameIndex;
idBinding.location = _compiledObject->location; // ###
setPropertyValue(idProperty, &idBinding);
@@ -773,10 +778,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
}
- if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)) {
if (!applyDeferredBindings)
continue;
} else {
@@ -804,7 +809,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding)
{
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
Q_ASSERT(tr);
@@ -827,11 +833,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) {
QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding),
context->asQQmlContext(), _scopeObject);
- ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
- ss.d.data()->lineNumber = binding->location.line;
- ss.d.data()->columnNumber = binding->location.column;
- ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String;
- ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number;
+ ss.d.data()->bindingId = bindingType == QV4::CompiledData::Binding::Type_Script
+ ? binding->value.compiledScriptIndex
+ : (quint32)QQmlBinding::Invalid;
+ ss.d.data()->lineNumber = binding->location.line();
+ ss.d.data()->columnNumber = binding->location.column();
+ ss.d.data()->isStringLiteral = bindingType == QV4::CompiledData::Binding::Type_String;
+ ss.d.data()->isNumberLiteral = bindingType == QV4::CompiledData::Binding::Type_Number;
ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding);
QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
@@ -843,7 +851,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
QObject *createdSubObject = nullptr;
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget);
if (!createdSubObject)
return false;
@@ -852,7 +860,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
if (!bindingProperty) // ### error
return true;
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ if (bindingType == QV4::CompiledData::Binding::Type_GroupProperty) {
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex);
if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) {
@@ -893,13 +902,15 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
}
- if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ if (_ddata->hasBindingBit(bindingProperty->coreIndex())
+ && !(bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
+ && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment)
&& !_valueTypeProperty)
QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Script
+ || binding->isTranslationBinding()) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression) {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex());
QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine);
@@ -955,8 +966,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
return true;
}
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) {
// ### determine value source and interceptor casts ahead of time.
QQmlType type = qmlTypeForObject(createdSubObject);
Q_ASSERT(type.isValid());
@@ -1014,7 +1025,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
// Assigning object to signal property?
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
if (!bindingProperty->isFunction()) {
recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject)));
return false;
@@ -1136,16 +1147,16 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location,
{
QQmlError error;
error.setUrl(compilationUnit->url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
errors << error;
}
void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const
{
- if (object->id >= 0)
- context->setIdProperty(object->id, instance);
+ if (object->objectId() >= 0)
+ context->setIdProperty(object->objectId(), instance);
}
void QQmlObjectCreator::createQmlContext()
@@ -1170,7 +1181,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QQmlParserStatus *parserStatus = nullptr;
bool installPropertyCache = true;
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)) {
isComponent = true;
QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent);
typeName = QStringLiteral("<component>");
@@ -1262,13 +1273,13 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
compilationUnit.data(), obj, typeName, context->url()));
Q_UNUSED(typeName); // only relevant for tracing
- ddata->lineNumber = obj->location.line;
- ddata->columnNumber = obj->location.column;
+ ddata->lineNumber = obj->location.line();
+ ddata->columnNumber = obj->location.column();
ddata->setImplicitDestructible();
// inline components are root objects, but their index is != 0, so we need
// an additional check
- const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
+ const bool isInlineComponent = obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) {
if (ddata->context) {
Q_ASSERT(ddata->context != context);
@@ -1301,7 +1312,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (isContextObject)
context->contextObject = instance;
- if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) {
+ if (customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) {
customParser->engine = QQmlEnginePrivate::get(engine);
customParser->imports = compilationUnit->typeNameCache.data();
@@ -1309,9 +1320,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
const QV4::CompiledData::Binding *binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
- if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
bindings << binding;
- }
}
customParser->applyBindings(instance, compilationUnit.data(), bindings);
@@ -1507,7 +1517,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
qSwap(_propertyCache, cache);
qSwap(_vmeMetaObject, vmeMetaObject);
- if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings)
+ if (_compiledObject->hasFlag(QV4::CompiledData::Object::HasDeferredBindings))
_ddata->deferData(_compiledObjectIndex, compilationUnit, context);
QSet<QString> postHocRequired;
@@ -1520,7 +1530,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex);
// only compute stringAt if there's a chance for the lookup to succeed
auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex));
- if (!property->isRequired && postHocRequired.end() == postHocIt)
+ if (!property->isRequired() && postHocRequired.end() == postHocIt)
continue;
if (postHocIt != postHocRequired.end())
postHocRequired.erase(postHocIt);
@@ -1557,12 +1567,12 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) {
const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex;
const auto originalAlias = alias;
- while (alias->aliasToLocalAlias)
+ while (alias->isAliasToLocalAlias())
alias = _compiledObject->aliasesBegin() + alias->localAliasIndex;
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
if (!context->idValues->wasSet())
continue;
- QObject *target = context->idValues[alias->targetObjectId].data();
+ QObject *target = context->idValues[alias->targetObjectId()].data();
if (!target)
continue;
QQmlData *targetDData = QQmlData::get(target, /*create*/false);
@@ -1574,7 +1584,11 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
continue;
auto it = sharedState->requiredProperties.find(targetProperty);
if (it != sharedState->requiredProperties.end())
- it->aliasesToRequired.push_back(AliasToRequiredInfo {compilationUnit->stringAt(originalAlias->nameIndex), compilationUnit->finalUrl()});
+ it->aliasesToRequired.push_back(
+ AliasToRequiredInfo {
+ compilationUnit->stringAt(originalAlias->nameIndex()),
+ compilationUnit->finalUrl()
+ });
}
qSwap(_vmeMetaObject, vmeMetaObject);
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index d6a9e73763..db0535c292 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -187,6 +187,23 @@ namespace QQmlPrivate
= QQmlPrivate::createSingletonInstance<T>;
};
+ // from https://en.cppreference.com/w/cpp/language/static:
+ // If a const non-inline (since C++17) static data member or a constexpr
+ // static data member (since C++11)(until C++17) is odr-used, a definition
+ // at namespace scope is still required, but it cannot have an initializer.
+ //
+ // If a static data member is declared constexpr, it is implicitly inline
+ // and does not need to be redeclared at namespace scope. This redeclaration
+ // without an initializer (formerly required as shown above) is still
+ // permitted, but is deprecated.
+ //
+ // TL;DR: redundant definitions for static constexpr are required in c++11
+ // but deprecated in c++17.
+ template<typename T>
+ constexpr CreateIntoFunction Constructors<T, true>::createInto;
+ template<typename T>
+ constexpr CreateSingletonFunction Constructors<T, true>::createSingletonInstance;
+
template<typename T>
struct Constructors<T, false>
{
@@ -194,6 +211,12 @@ namespace QQmlPrivate
static constexpr CreateSingletonFunction createSingletonInstance = nullptr;
};
+ // see comment above over the Constructors<T, true> definitions.
+ template<typename T>
+ constexpr CreateIntoFunction Constructors<T, false>::createInto;
+ template<typename T>
+ constexpr CreateSingletonFunction Constructors<T, false>::createSingletonInstance;
+
template<typename T, bool IsVoid = std::is_void<T>::value>
struct ExtendedType;
@@ -205,6 +228,12 @@ namespace QQmlPrivate
static constexpr const QMetaObject *staticMetaObject = nullptr;
};
+ // see comment above over the Constructors<T, true> definitions.
+ template<typename T>
+ constexpr const CreateParentFunction ExtendedType<T, true>::createParent;
+ template<typename T>
+ constexpr const QMetaObject* ExtendedType<T, true>::staticMetaObject;
+
// If it's not void, we actually want an error if the ctor or the metaobject is missing.
template<typename T>
struct ExtendedType<T, false>
@@ -213,6 +242,12 @@ namespace QQmlPrivate
static constexpr const QMetaObject *staticMetaObject = &T::staticMetaObject;
};
+ // see comment above over the Constructors<T, true> definitions.
+ template<typename T>
+ constexpr const CreateParentFunction ExtendedType<T, false>::createParent;
+ template<typename T>
+ constexpr const QMetaObject* ExtendedType<T, false>::staticMetaObject;
+
template<class From, class To, int N>
struct StaticCastSelectorClass
{
@@ -577,6 +612,10 @@ namespace QQmlPrivate
static constexpr bool Value = false;
};
+ // see comment above over the Constructors<T, true> definitions.
+ template<typename T, class C>
+ constexpr bool QmlSingleton<T, C>::Value;
+
template<class T>
struct QmlSingleton<T, QmlVoidT<typename T::QmlIsSingleton>>
{
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 8521de6ab3..92bd369ccc 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -598,12 +598,8 @@ QObject *QQmlProperty::object() const
*/
QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
{
- if (d)
- d->release();
- d = other.d;
- if (d)
- d->addref();
-
+ QQmlProperty copied(other);
+ qSwap(d, copied.d);
return *this;
}
@@ -918,6 +914,8 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
if (!(that.type() & QQmlProperty::SignalProperty))
return nullptr;
+ if (!that.d->object)
+ return nullptr;
QQmlData *data = QQmlData::get(that.d->object);
if (!data)
return nullptr;
@@ -957,6 +955,8 @@ void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
return;
}
+ if (!that.d->object)
+ return;
QQmlData *data = QQmlData::get(that.d->object, nullptr != expr);
if (!data)
return;
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 8abd83d7b4..b76a216cf3 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -71,7 +71,7 @@ class QQmlMetaObject;
class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount
{
public:
- QQmlContextData *context;
+ QQmlGuardedContextData context;
QPointer<QQmlEngine> engine;
QPointer<QObject> object;
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 0911c06cd0..8a0abf8bdd 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -51,8 +51,8 @@
#include <QtCore/qdebug.h>
#include <QtCore/QCryptographicHash>
+#include <QtCore/private/qtools_p.h>
-#include <ctype.h> // for toupper
#include <limits.h>
#include <algorithm>
@@ -138,17 +138,17 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
{
populate(this, p);
int type = static_cast<int>(p.userType());
- if (type == QMetaType::QObjectStar) {
- setPropType(type);
+
+ if (type >= QMetaType::User || type == 0)
+ return; // Resolve later
+
+ if (type == QMetaType::QObjectStar)
m_flags.type = Flags::QObjectDerivedType;
- } else if (type == QMetaType::QVariant) {
- setPropType(type);
+ else if (type == QMetaType::QVariant)
m_flags.type = Flags::QVariantType;
- } else if (type >= QMetaType::User || type == 0) {
- m_flags.notFullyResolved = true;
- } else {
- setPropType(type);
- }
+
+ // This is OK because lazyLoad is done before exposing the property data.
+ setPropType(type);
}
void QQmlPropertyData::load(const QMetaProperty &p)
@@ -158,45 +158,48 @@ void QQmlPropertyData::load(const QMetaProperty &p)
flagsForPropertyType(propType(), m_flags);
}
-void QQmlPropertyData::load(const QMetaMethod &m)
+static void populate(QQmlPropertyData *data, const QMetaMethod &m)
{
- setCoreIndex(m.methodIndex());
- setArguments(nullptr);
+ data->setCoreIndex(m.methodIndex());
- setPropType(m.returnType());
-
- m_flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal) {
- m_flags.setIsSignal(true);
- } else if (m.methodType() == QMetaMethod::Constructor) {
- m_flags.setIsConstructor(true);
- setPropType(QMetaType::QObjectStar);
- }
+ QQmlPropertyData::Flags flags = data->flags();
+ flags.type = QQmlPropertyData::Flags::FunctionType;
+ if (m.methodType() == QMetaMethod::Signal)
+ flags.setIsSignal(true);
+ else if (m.methodType() == QMetaMethod::Constructor)
+ flags.setIsConstructor(true);
const int paramCount = m.parameterCount();
if (paramCount) {
- m_flags.setHasArguments(true);
+ flags.setHasArguments(true);
if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*"))
- m_flags.setIsV4Function(true);
+ flags.setIsV4Function(true);
}
if (m.attributes() & QMetaMethod::Cloned)
- m_flags.setIsCloned(true);
+ flags.setIsCloned(true);
+
+ data->setFlags(flags);
Q_ASSERT(m.revision() <= Q_INT16_MAX);
- setRevision(m.revision());
+ data->setRevision(m.revision());
}
-void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
+void QQmlPropertyData::load(const QMetaMethod &m)
{
- load(m);
+ populate(this, m);
+ setPropType(m.methodType() == QMetaMethod::Constructor
+ ? QMetaType::QObjectStar
+ : m.returnType());
+}
+void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
+{
const char *returnType = m.typeName();
- if (!returnType)
- returnType = "\0";
- if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
- m_flags.notFullyResolved = true;
- }
+ if (!returnType || *returnType != 'v' || qstrcmp(returnType + 1, "oid") != 0)
+ populate(this, m);
+ else
+ load(m); // If it's void, resolve it right away
}
/*!
@@ -318,7 +321,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
data.setPropType(QMetaType::UnknownType);
data.setCoreIndex(coreIndex);
data.setFlags(flags);
- data.setArguments(nullptr);
QQmlPropertyData handler = data;
handler.m_flags.setIsSignalHandler(true);
@@ -327,7 +329,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
int argumentCount = *types;
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
- args->argumentsValid = true;
data.setArguments(args);
}
@@ -361,7 +362,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
for (int ii = 0; ii < argumentCount; ++ii)
args->arguments[ii + 1] = parameterTypes.at(ii);
- args->argumentsValid = true;
data.setArguments(args);
data.setFlags(flags);
@@ -571,7 +571,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QVarLengthArray<char, 128> str(length+3);
str[0] = 'o';
str[1] = 'n';
- str[2] = toupper(rawName[0]);
+ str[2] = QtMiscUtils::toAsciiUpper(rawName[0]);
if (length > 1)
memcpy(&str[3], &rawName[1], length - 1);
str[length + 2] = '\0';
@@ -650,25 +650,23 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
}
-void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
+int QQmlPropertyCache::findPropType(const QQmlPropertyData *data) const
{
- Q_ASSERT(data->notFullyResolved());
- data->m_flags.notFullyResolved = false;
-
+ int type = QMetaType::UnknownType;
const QMetaObject *mo = firstCppMetaObject();
if (data->isFunction()) {
auto metaMethod = mo->method(data->coreIndex());
const char *retTy = metaMethod.typeName();
if (!retTy)
retTy = "\0";
- data->setPropType(QMetaType::type(retTy));
+ type = QMetaType::type(retTy);
} else {
auto metaProperty = mo->property(data->coreIndex());
- data->setPropType(QMetaType::type(metaProperty.typeName()));
+ type = QMetaType::type(metaProperty.typeName());
}
if (!data->isFunction()) {
- if (data->propType() == QMetaType::UnknownType) {
+ if (type == QMetaType::UnknownType) {
QQmlPropertyCache *p = _parent;
while (p && (!mo || _ownMetaObject)) {
mo = p->_metaObject;
@@ -684,11 +682,33 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
int registerResult = -1;
void *argv[] = { &registerResult };
- mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv);
- data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult);
+ mo->static_metacall(QMetaObject::RegisterPropertyMetaType,
+ data->coreIndex() - propOffset, argv);
+ type = registerResult == -1 ? QMetaType::UnknownType : registerResult;
}
}
- flagsForPropertyType(data->propType(), data->m_flags);
+ }
+
+ return type;
+}
+
+void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
+{
+ const int type = findPropType(data);
+
+ // Setting the flags unsynchronized is somewhat dirty but unlikely to cause trouble
+ // in practice. We have to do this before setting the property type because otherwise
+ // a consumer of the flags might see outdated flags even after the property type has
+ // become valid. The flags should only depend on the property type and the property
+ // type should be the same across different invocations. So, setting this concurrently
+ // should be a noop.
+ if (!data->isFunction())
+ flagsForPropertyType(type, data->m_flags);
+
+ // This is the one place where we can update the property type after exposing the data.
+ if (!data->m_propTypeAndRelativePropIndex.testAndSetOrdered(
+ 0, type > 0 ? quint32(type) : quint32(QQmlPropertyData::PropTypeUnknown))) {
+ return; // Someone else is resolving it already
}
}
@@ -873,12 +893,11 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a
typedef QQmlPropertyCacheMethodArguments A;
A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int)));
args->arguments[0] = argc;
- args->argumentsValid = false;
args->signalParameterStringForJS = nullptr;
- args->parameterError = false;
args->names = argc ? new QList<QByteArray>(names) : nullptr;
- args->next = argumentsCache;
- argumentsCache = args;
+ do {
+ args->next = argumentsCache;
+ } while (!argumentsCache.testAndSetRelease(args->next, args));
return args;
}
@@ -1172,7 +1191,6 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
QQmlPropertyCacheMethodArguments *arguments = nullptr;
if (data->hasArguments()) {
arguments = (QQmlPropertyCacheMethodArguments *)data->arguments();
- Q_ASSERT(arguments->argumentsValid);
for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
if (ii != 0) signature.append(',');
signature.append(QMetaType::typeName(arguments->arguments[1 + ii]));
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index a5340cec37..1563bc0a41 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -220,6 +220,8 @@ private:
_hasPropertyOverrides |= isOverride;
}
+ int findPropType(const QQmlPropertyData *data) const;
+
private:
QQmlPropertyCache *_parent;
int propertyIndexCacheStart;
@@ -239,14 +241,18 @@ private:
QByteArray _dynamicClassName;
QByteArray _dynamicStringData;
QString _defaultPropertyName;
- QQmlPropertyCacheMethodArguments *argumentsCache;
+ QAtomicPointer<QQmlPropertyCacheMethodArguments> argumentsCache;
int _jsFactoryMethodIndex;
QByteArray _checksum;
};
inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
- if (p && Q_UNLIKELY(p->notFullyResolved()))
+ // Avoid resolve() in the common case where it's already initialized and we don't
+ // run into a data race. resolve() checks again, with an atomic operation.
+ // If there is no coreIndex, there is no point in trying to resolve anything. In that
+ // case it's a default-constructed instance that never got load()'ed or lazyLoad()'ed.
+ if (p && p->coreIndex() != -1 && Q_UNLIKELY(p->m_propTypeAndRelativePropIndex == 0))
resolve(p);
return p;
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index 88d80d88ab..de02636d95 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -110,11 +110,15 @@ QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencing
bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
{
- if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ if (!instantiatingBinding
+ || instantiatingBinding->type() != QV4::CompiledData::Binding::Type_GroupProperty) {
return true;
+ }
+
+ if (!referencingObjectPropertyCache)
+ return false;
Q_ASSERT(referencingObjectIndex >= 0);
- Q_ASSERT(referencingObjectPropertyCache);
Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
bool notInRevision = false;
@@ -144,6 +148,11 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePr
if (propertyCaches->at(groupPropertyObjectIndex))
continue;
+ if (!pendingBinding.referencingObjectPropertyCache) {
+ pendingBinding.referencingObjectPropertyCache
+ = propertyCaches->at(pendingBinding.referencingObjectIndex);
+ }
+
if (!pendingBinding.resolveInstantiatingProperty())
continue;
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 77e3763a49..363e4d17fe 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -67,8 +67,8 @@ inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location,
const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
return error;
}
@@ -196,7 +196,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
// create meta objects for inline components before compiling actual root component
for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
- const auto &ic = allICs[nodeIt->index];
+ const auto &ic = allICs[nodeIt->index()];
QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr);
Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet
@@ -226,7 +226,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
|| obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
- || (((obj->flags & QV4::CompiledData::Object::IsComponent)
+ || ((obj->hasFlag(QV4::CompiledData::Object::IsComponent)
|| (objectIndex == 0 && isAddressable(objectContainer->url())))
&& !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType);
@@ -234,7 +234,8 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
auto binding = obj->bindingsBegin();
auto end = obj->bindingsEnd();
for ( ; binding != end; ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Object
+ && (binding->flags() & QV4::CompiledData::Binding::IsOnAssignment)) {
// If the on assignment is inside a group property, we need to distinguish between QObject based
// group properties and value type group properties. For the former the base type is derived from
// the property that references us, for the latter we only need a meta-object on the referencing object
@@ -276,24 +277,24 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
}
}
- if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
- auto binding = obj->bindingsBegin();
- auto end = obj->bindingsEnd();
- for ( ; binding != end; ++binding)
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
- QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
-
- // Binding to group property where we failed to look up the type of the
- // property? Possibly a group property that is an alias that's not resolved yet.
- // Let's attempt to resolve it after we're done with the aliases and fill in the
- // propertyCaches entry then.
- if (!context.resolveInstantiatingProperty())
- pendingGroupPropertyBindings->append(context);
-
- QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
- if (error.isValid())
- return error;
- }
+ QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex);
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for ( ; binding != end; ++binding) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
+ QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+
+ // Binding to group property where we failed to look up the type of the
+ // property? Possibly a group property that is an alias that's not resolved yet.
+ // Let's attempt to resolve it after we're done with the aliases and fill in the
+ // propertyCaches entry then.
+ if (!thisCache || !context.resolveInstantiatingProperty())
+ pendingGroupPropertyBindings->append(context);
+
+ QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
+ if (error.isValid())
+ return error;
+ }
}
QQmlError noError;
@@ -390,7 +391,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
auto aend = obj->aliasesEnd();
for ( ; a != aend; ++a) {
bool notInRevision = false;
- QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
+ QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex()), &notInRevision);
if (d && d->isFinal())
return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
}
@@ -438,7 +439,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
for ( ; a != aend; ++a) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
+ QString changedSigName = stringAt(a->nameIndex()) + QLatin1String("Changed");
seenSignals.insert(changedSigName);
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
@@ -556,12 +557,13 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
if (type == QV4::CompiledData::BuiltinType::Variant)
propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
} else {
- Q_ASSERT(!p->isBuiltinType);
+ Q_ASSERT(!p->isBuiltinType());
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr,
- nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
+ if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex()), &qmltype,
+ nullptr, nullptr, nullptr, nullptr,
+ QQmlType::AnyRegistrationType, &selfReference)) {
return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
@@ -592,13 +594,13 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
typeIds = compilationUnit->typeIdsForComponent();
}
- if (p->isList) {
+ if (p->isList()) {
propertyType = typeIds.listId;
} else {
propertyType = typeIds.id;
}
} else {
- if (p->isList) {
+ if (p->isList()) {
propertyType = qmltype.qListTypeId();
} else {
propertyType = qmltype.typeId();
@@ -606,18 +608,17 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
}
}
- if (p->isList)
+ if (p->isList())
propertyFlags.type = QQmlPropertyData::Flags::QListType;
else
propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
}
- if (!p->isReadOnly && !p->isList)
+ if (!p->isReadOnly() && !p->isList())
propertyFlags.setIsWritable(true);
-
QString propertyName = stringAt(p->nameIndex);
- if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ if (!obj->hasAliasAsDefaultProperty() && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
cache->_defaultPropertyName = propertyName;
cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
propertyType, propertTypeMinorVersion, effectiveSignalIndex);
@@ -633,13 +634,14 @@ template <typename ObjectContainer>
inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
QString *customTypeName)
{
- if (param.indexIsBuiltinType) {
+ if (param.indexIsBuiltinType()) {
// built-in type
- return metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType)));
+ return metaTypeForPropertyType(
+ static_cast<QV4::CompiledData::BuiltinType>(param.typeNameIndexOrBuiltinType()));
}
// lazily resolved type
- const QString typeName = stringAt(param.typeNameIndexOrBuiltinType);
+ const QString typeName = stringAt(param.typeNameIndexOrBuiltinType());
if (customTypeName)
*customTypeName = typeName;
QQmlType qmltype;
@@ -702,7 +704,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
// from a binding.
for (int i = 1; i < objectContainer->objectCount(); ++i) {
const CompiledObject &component = *objectContainer->objectAt(i);
- if (!(component.flags & QV4::CompiledData::Object::IsComponent))
+ if (!component.hasFlag(QV4::CompiledData::Object::IsComponent))
continue;
const auto rootBinding = component.bindingsBegin();
@@ -725,12 +727,12 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
auto alias = object.aliasesBegin();
auto end = object.aliasesEnd();
for ( ; alias != end; ++alias) {
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
- const int targetObjectIndex = objectForId(component, alias->targetObjectId);
+ const int targetObjectIndex = objectForId(component, alias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
- if (alias->aliasToLocalAlias)
+ if (alias->isAliasToLocalAlias())
continue;
if (alias->encodedMetaPropertyIndex == -1)
@@ -772,18 +774,21 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
objectsWithAliases->append(objectIndex);
// Stop at Component boundary
- if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
+ if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
return;
auto binding = object.bindingsBegin();
auto end = object.bindingsEnd();
for (; binding != end; ++binding) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object
- && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
- continue;
-
- collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ break;
+ default:
+ break;
+ }
}
}
@@ -797,12 +802,12 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
propertyFlags->setIsAlias(true);
- if (alias.aliasToLocalAlias) {
+ if (alias.isAliasToLocalAlias()) {
const QV4::CompiledData::Alias *lastAlias = &alias;
QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
do {
- const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId);
+ const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
Q_ASSERT(targetObject);
@@ -819,17 +824,17 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
seenAliases.append(targetAlias);
lastAlias = targetAlias;
- } while (lastAlias->aliasToLocalAlias);
+ } while (lastAlias->isAliasToLocalAlias());
return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv);
}
- const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
if (alias.encodedMetaPropertyIndex == -1) {
- Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ Q_ASSERT(alias.hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject));
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
if (!typeRef) {
// Can be caused by the alias target not being a valid id or property. E.g.:
@@ -865,6 +870,10 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
Q_ASSERT(targetCache);
targetProperty = targetCache->property(valueTypeIndex);
+ if (targetProperty == nullptr) {
+ return qQmlCompileError(alias.referenceLocation,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
*type = targetProperty->propType();
writable = targetProperty->isWritable();
@@ -897,7 +906,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
}
}
- propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable);
+ propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly))
+ && writable);
propertyFlags->setIsResettable(resettable);
return QQmlError();
}
@@ -920,7 +930,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
auto alias = object.aliasesBegin();
auto end = object.aliasesEnd();
for ( ; alias != end; ++alias, ++aliasIndex) {
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
int type = 0;
int minorVersion = 0;
@@ -929,9 +939,9 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
if (error.isValid())
return error;
- const QString propertyName = objectContainer->stringAt(alias->nameIndex);
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex());
- if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
propertyCache->_defaultPropertyName = propertyName;
propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
@@ -947,7 +957,7 @@ inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const Com
for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
const int candidateIndex = component.namedObjectsInComponentTable()[i];
const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
- if (candidate.id == id)
+ if (candidate.objectId() == id)
return candidateIndex;
}
return -1;
diff --git a/src/qml/qml/qqmlpropertycachemethodarguments_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
index 62f09bdfff..32affe6d9c 100644
--- a/src/qml/qml/qqmlpropertycachemethodarguments_p.h
+++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
@@ -64,8 +64,6 @@ public:
//for signal handler rewrites
QString *signalParameterStringForJS;
- int parameterError:1;
- int argumentsValid:1;
QList<QByteArray> *names;
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index d9855797cd..2f1b6f62f1 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -84,14 +84,6 @@ public:
QVariantType = 9 // Property is a QVariant
};
- // The _otherBits (which "pad" the Flags struct to align it nicely) are used
- // to store the relative property index. It will only get used when said index fits. See
- // trySetStaticMetaCallFunction for details.
- // (Note: this padding is done here, because certain compilers have surprising behavior
- // when an enum is declared in-between two bit fields.)
- enum { BitsLeftInFlags = 15 };
- unsigned otherBits : BitsLeftInFlags; // align to 32 bits
-
// Members of the form aORb can only be a when type is not FunctionType, and only be
// b when type equals FunctionType. For that reason, the semantic meaning of the bit is
// overloaded, and the accessor functions are used to get the correct value
@@ -102,25 +94,24 @@ public:
//
// Lastly, isDirect and isOverridden apply to both functions and non-functions
private:
- unsigned isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML
- unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
- unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal
- unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
- unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
- unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
- unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
- unsigned isConstructor : 1; // The function was marked is a constructor
- unsigned isDirect : 1; // Exists on a C++ QMetaObject
- unsigned isOverridden : 1; // Is overridden by a extension property
+ quint16 isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML
+ quint16 isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
+ quint16 isResettableORisSignal : 1; // Has RESET function OR Function is a signal
+ quint16 isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
+ quint16 isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
+ quint16 isSignalHandler : 1; // Function is a signal handler
+ quint16 isOverload : 1; // Function is an overload of another function
+ quint16 isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
+ quint16 isConstructor : 1; // The function was marked is a constructor
+ quint16 isDirect : 1; // Exists on a C++ QMetaObject
+ quint16 isOverridden : 1; // Is overridden by a extension property
public:
- unsigned type : 4; // stores an entry of Types
+ quint16 type : 4; // stores an entry of Types
// Apply only to IsFunctions
// Internal QQmlPropertyCache flags
- unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
- unsigned overrideIndexIsProperty: 1;
+ quint16 overrideIndexIsProperty: 1;
inline Flags();
inline bool operator==(const Flags &other) const;
@@ -208,16 +199,12 @@ public:
};
+ Q_STATIC_ASSERT(sizeof(Flags) == sizeof(quint16));
inline bool operator==(const QQmlPropertyData &) const;
Flags flags() const { return m_flags; }
- void setFlags(Flags f)
- {
- unsigned otherBits = m_flags.otherBits;
- m_flags = f;
- m_flags.otherBits = otherBits;
- }
+ void setFlags(Flags f) { m_flags = f; }
bool isValid() const { return coreIndex() != -1; }
@@ -253,14 +240,26 @@ public:
bool hasOverride() const { return overrideIndex() >= 0; }
bool hasRevision() const { return revision() != 0; }
- bool isFullyResolved() const { return !m_flags.notFullyResolved; }
+ // This is unsafe in the general case. The property might be in the process of getting
+ // resolved. Only use it if this case has been taken into account.
+ bool isResolved() const { return m_propTypeAndRelativePropIndex != 0; }
+
+ int propType() const
+ {
+ const quint32 type = m_propTypeAndRelativePropIndex & PropTypeMask;
+ Q_ASSERT(type > 0); // Property has to be fully resolved.
+ return type == PropTypeUnknown ? 0 : type;
+ }
- int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; }
void setPropType(int pt)
{
+ // You can only directly set the property type if you own the QQmlPropertyData.
+ // It must not be exposed to other threads before setting the type!
Q_ASSERT(pt >= 0);
- Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
- m_propType = quint16(pt);
+ Q_ASSERT(uint(pt) < PropTypeUnknown);
+ m_propTypeAndRelativePropIndex
+ = (m_propTypeAndRelativePropIndex & RelativePropIndexMask)
+ | (pt == 0 ? PropTypeUnknown : quint32(pt));
}
int notifyIndex() const { return m_notifyIndex; }
@@ -323,7 +322,10 @@ public:
}
QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+ bool setArguments(QQmlPropertyCacheMethodArguments *args)
+ {
+ return m_arguments.testAndSetRelease(nullptr, args);
+ }
int metaObjectOffset() const { return m_metaObjectOffset; }
void setMetaObjectOffset(int off)
@@ -336,12 +338,26 @@ public:
StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; }
void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
{
- if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
- m_flags.otherBits = relativePropertyIndex;
+ if (relativePropertyIndex > std::numeric_limits<quint16>::max())
+ return;
+
+ const quint16 propType = m_propTypeAndRelativePropIndex & PropTypeMask;
+ if (propType > 0) {
+ // We can do this because we know that resolve() has run at this point
+ // and we don't need to synchronize anymore. If we get a 0, that means it hasn't
+ // run or is currently in progress. We don't want to interfer and just go through
+ // the meta object.
+ m_propTypeAndRelativePropIndex
+ = propType | (relativePropertyIndex << RelativePropIndexShift);
m_staticMetaCallFunction = f;
}
}
- quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; }
+
+ quint16 relativePropertyIndex() const
+ {
+ Q_ASSERT(hasStaticMetaCallFunction());
+ return m_propTypeAndRelativePropIndex >> 16;
+ }
static Flags flagsForProperty(const QMetaProperty &);
void load(const QMetaProperty &);
@@ -401,11 +417,17 @@ private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return m_flags.notFullyResolved; }
+
+ enum {
+ PropTypeMask = 0x0000ffff,
+ RelativePropIndexMask = 0xffff0000,
+ RelativePropIndexShift = 16,
+ PropTypeUnknown = std::numeric_limits<quint16>::max(),
+ };
+ QAtomicInteger<quint32> m_propTypeAndRelativePropIndex;
Flags m_flags;
qint16 m_coreIndex = -1;
- quint16 m_propType = 0;
// The notify index is in the range returned by QObjectPrivate::signalIndex().
// This is different from QMetaMethod::methodIndex().
@@ -416,7 +438,7 @@ private:
quint8 m_typeMinorVersion = 0;
qint16 m_metaObjectOffset = -1;
- QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
+ QAtomicPointer<QQmlPropertyCacheMethodArguments> m_arguments;
StaticMetaCallFunction m_staticMetaCallFunction = nullptr;
};
@@ -436,8 +458,7 @@ bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
}
QQmlPropertyData::Flags::Flags()
- : otherBits(0)
- , isConstantORisVMEFunction(false)
+ : isConstantORisVMEFunction(false)
, isWritableORhasArguments(false)
, isResettableORisSignal(false)
, isAliasORisVMESignal(false)
@@ -449,7 +470,6 @@ QQmlPropertyData::Flags::Flags()
, isDirect(false)
, isOverridden(false)
, type(OtherType)
- , notFullyResolved(false)
, overrideIndexIsProperty(false)
{}
@@ -465,7 +485,6 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c
isRequiredORisCloned == other.isRequiredORisCloned &&
type == other.type &&
isConstructor == other.isConstructor &&
- notFullyResolved == other.notFullyResolved &&
overrideIndexIsProperty == other.overrideIndexIsProperty;
}
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index 3a1f33113f..ae8e6e3982 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -104,10 +104,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr);
}
- if (obj->flags & QV4::CompiledData::Object::IsComponent && !(obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)) {
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)
+ && !obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot)) {
Q_ASSERT(obj->nBindings == 1);
const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
return validateObject(componentBinding->value.objectIndex, componentBinding);
}
@@ -131,7 +132,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!binding->isGroupProperty())
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment))
continue;
if (populatingValueTypeGroupProperty) {
@@ -160,9 +161,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
QString name = stringAt(binding->propertyNameIndex);
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
if (customParser) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
customBindings << binding;
continue;
@@ -175,13 +178,14 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
}
bool bindingToDefaultProperty = false;
- bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
+ bool isGroupProperty = instantiatingBinding
+ && instantiatingBinding->type() == QV4::CompiledData::Binding::Type_GroupProperty;
bool notInRevision = false;
QQmlPropertyData *pd = nullptr;
if (!name.isEmpty()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
pd = propertyResolver.signal(name, &notInRevision);
} else {
pd = propertyResolver.property(name, &notInRevision,
@@ -218,11 +222,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(binding->location, tr("Invalid attached object assignment"));
}
- if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
const bool populatingValueTypeGroupProperty
= pd
&& QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
+ && !binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment);
const QVector<QQmlError> subObjectValidatorErrors
= validateObject(binding->value.objectIndex, binding,
populatingValueTypeGroupProperty);
@@ -231,11 +235,12 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
}
// Signal handlers were resolved and checked earlier in the signal handler conversion pass.
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerExpression)
+ || binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject)) {
continue;
+ }
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
return recordError(binding->location, tr("Attached properties cannot be used here"));
}
@@ -249,15 +254,15 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!pd->isWritable()
&& !pd->isQList()
&& !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ && !(bindingFlags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
) {
- if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
+ if (assigningToGroupProperty && bindingType < QV4::CompiledData::Binding::Type_Object)
return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
}
- if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
+ if (!pd->isQList() && (bindingFlags & QV4::CompiledData::Binding::IsListItem)) {
QString error;
if (pd->propType() == qMetaTypeId<QQmlScriptString>())
error = tr( "Cannot assign multiple values to a script property");
@@ -268,7 +273,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!bindingToDefaultProperty
&& !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment)
&& assigningToGroupProperty) {
QV4::CompiledData::Location loc = binding->valueLocation;
if (loc < (*assignedGroupProperty)->valueLocation)
@@ -279,11 +284,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
}
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType < QV4::CompiledData::Binding::Type_Script) {
QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding);
if (bindingError.isValid())
return recordError(bindingError);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Object) {
QQmlError bindingError = validateObjectBinding(pd, name, binding);
if (bindingError.isValid())
return recordError(bindingError);
@@ -366,7 +371,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
QQmlError noError;
if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum))
return noError;
QString value = compilationUnit->bindingValueAsString(binding);
@@ -384,11 +389,13 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
auto warnOrError = [&](const QString &error) {
- if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
QQmlError warning;
warning.setUrl(compilationUnit->url());
- warning.setLine(qmlConvertSourceCoordinate<quint32, int>(binding->valueLocation.line));
- warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(binding->valueLocation.column));
+ warning.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ binding->valueLocation.line()));
+ warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ binding->valueLocation.column()));
warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
"is deprecated. This will become a compile error in "
"future versions of Qt."));
@@ -398,6 +405,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
return qQmlCompileError(binding->valueLocation, error);
};
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
switch (property->propType()) {
case QMetaType::QVariant:
break;
@@ -414,19 +422,17 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::QByteArray: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String)
return warnOrError(tr("Invalid property assignment: byte array expected"));
- }
}
break;
case QMetaType::QUrl: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String)
return warnOrError(tr("Invalid property assignment: url expected"));
- }
}
break;
case QMetaType::UInt: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(uint(d)) == d)
return noError;
@@ -435,7 +441,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Int: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(int(d)) == d)
return noError;
@@ -444,13 +450,13 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Float: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number expected"));
}
}
break;
case QMetaType::Double: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number expected"));
}
}
@@ -538,7 +544,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Bool: {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
return warnOrError(tr("Invalid property assignment: boolean expected"));
}
}
@@ -594,12 +600,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
default: {
// generate single literal value assignment to a list property if required
if (property->propType() == qMetaTypeId<QList<qreal> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number or array of numbers expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
- bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
+ bool ok = (bindingType == QV4::CompiledData::Binding::Type_Number);
if (ok) {
double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) != n)
@@ -609,12 +615,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
return warnOrError(tr("Invalid property assignment: int or array of ints expected"));
break;
} else if (property->propType() == qMetaTypeId<QList<bool> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
return warnOrError(tr("Invalid property assignment: bool or array of bools expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String) {
return warnOrError(tr("Invalid property assignment: url or array of urls expected"));
}
break;
@@ -628,7 +634,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
break;
} else if (property->isQObject()
- && binding->type == QV4::CompiledData::Binding::Type_Null) {
+ && bindingType == QV4::CompiledData::Binding::Type_Null) {
break;
}
@@ -690,8 +696,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert
{
QQmlError noError;
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Object);
bool isValueSource = false;
bool isPropertyInterceptor = false;
@@ -740,7 +746,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert
}
}
return noError;
- } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
+ } else if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject)
+ && property->isFunction()) {
return noError;
} else if (isPrimitiveType(propType)) {
auto typeName = QString::fromUtf8(QMetaType::typeName(propType));
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index dac21f7ee7..18ea5db4c6 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -168,8 +168,8 @@ void QQmlScriptBlob::done()
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.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);
@@ -237,8 +237,8 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(import->location.line());
+ error.setColumn(import->location.column());
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return;
diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp
index ae268ca904..9ef49e9641 100644
--- a/src/qml/qml/qqmlscriptdata.cpp
+++ b/src/qml/qml/qqmlscriptdata.cpp
@@ -113,10 +113,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle();
QV4::Scope scope(v4);
- if (!hasEngine()) {
- addToEngine(parentQmlContextData->engine);
- addref();
- }
QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData);
QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope);
@@ -154,13 +150,4 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
return value->asReturnedValue();
}
-void QQmlScriptData::clear()
-{
- typeNameCache = nullptr;
- scripts.clear();
-
- // An addref() was made when the QQmlCleanup was added to the engine.
- release();
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
index 80b65b699c..0ace1cd376 100644
--- a/src/qml/qml/qqmlscriptdata_p.h
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -65,14 +65,7 @@ QT_BEGIN_NAMESPACE
class QQmlTypeNameCache;
class QQmlContextData;
-// QQmlScriptData instances are created, uninitialized, by the loader in the
-// load thread. The first time they are used by the VME, they are initialized which
-// creates their v8 objects and they are referenced and added to the engine's cleanup
-// list. During QQmlCleanup::clear() all v8 resources are destroyed, and the
-// reference that was created is released but final deletion only occurs once all the
-// references as released. This is all intended to ensure that the v8 resources are
-// only created and destroyed in the main thread :)
-class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount
+class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlRefCount
{
private:
friend class QQmlTypeLoader;
@@ -89,13 +82,9 @@ public:
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; }
-protected:
- void clear() override; // From QQmlCleanup
-
private:
friend class QQmlScriptBlob;
- void initialize(QQmlEngine *);
QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData);
bool m_loaded;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index f1a6b3bff2..c3c80a2ac5 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -174,8 +174,8 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
error.setUrl(url());
errors << error;
@@ -335,7 +335,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
QString propertyName = stringAt(binding->propertyNameIndex);
// Attached property?
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type : QQmlType();
@@ -438,13 +439,13 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
// Binding object to signal means connect the signal to the object's default method.
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject;
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
+ binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject);
continue;
}
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType < QV4::CompiledData::Binding::Type_Script) {
COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)"));
} else {
COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment"));
@@ -489,7 +490,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
foe->node = functionDeclaration;
binding->propertyNameIndex = compiler->registerString(propertyName);
- binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
+ binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerExpression);
}
return true;
}
@@ -513,11 +514,13 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
QQmlPropertyResolver resolver(propertyCache);
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
continue;
+ }
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
const QString propertyName = stringAt(binding->propertyNameIndex);
@@ -548,10 +551,10 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS
if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) {
COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
}
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
// binding->setNumberValueInternal((double)enumValue);
- binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
+ binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum);
return true;
}
@@ -561,10 +564,13 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
if (!prop->isEnum() && !isIntProp)
return true;
- if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))
- COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex)));
+ if (!prop->isWritable()
+ && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) {
+ COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property")
+ .arg(stringAt(binding->propertyNameIndex)));
+ }
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
if (!string.constData()->isUpper())
return true;
@@ -694,15 +700,21 @@ void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool
if (!annotateScriptBindings)
annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex);
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Script:
+ if (annotateScriptBindings) {
+ binding->stringIndex = compiler->registerString(
+ compiler->bindingAsString(obj, binding->value.compiledScriptIndex));
+ }
+ break;
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings);
- continue;
- } else if (binding->type != QV4::CompiledData::Binding::Type_Script)
- continue;
- if (!annotateScriptBindings)
- continue;
- const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
- binding->stringIndex = compiler->registerString(script);
+ break;
+ default:
+ break;
+ }
}
}
@@ -731,7 +743,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases()
bool notInRevision = false;
QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
if (pd && pd->isAlias())
- binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
+ binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias);
}
}
}
@@ -758,7 +770,7 @@ void QQmlScriptStringScanner::scan()
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
bool notInRevision = false;
QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
@@ -804,9 +816,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Object)
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
continue;
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
@@ -871,7 +883,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
*syntheticBinding = *binding;
- syntheticBinding->type = QV4::CompiledData::Binding::Type_Object;
+ syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
Q_ASSERT(error.isEmpty());
Q_UNUSED(error);
@@ -932,7 +944,7 @@ bool QQmlComponentAndAliasResolver::resolve()
COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
}
- if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
+ if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object)
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
// For the root object, we are going to collect ids/aliases and resolve them for as a separate
@@ -1000,13 +1012,16 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
return true;
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object
- && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
- continue;
-
- if (!collectIdsAndAliases(binding->value.objectIndex))
- return false;
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ if (!collectIdsAndAliases(binding->value.objectIndex))
+ return false;
+ break;
+ default:
+ break;
+ }
}
return true;
@@ -1054,7 +1069,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
recordError(alias->location, tr("Circular alias reference detected"));
return false;
}
@@ -1076,7 +1091,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
bool seenUnresolvedAlias = false;
for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
- if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ if (alias->hasFlag(QV4::CompiledData::Alias::Resolved))
continue;
seenUnresolvedAlias = true;
@@ -1092,8 +1107,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
Q_ASSERT(targetObject->id >= 0);
- alias->targetObjectId = targetObject->id;
- alias->aliasToLocalAlias = false;
+ alias->setTargetObjectId(targetObject->id);
+ alias->setIsAliasToLocalAlias(false);
const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
@@ -1110,7 +1125,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
QQmlPropertyIndex propIdx;
if (property.isEmpty()) {
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
} else {
QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
if (!targetCache) {
@@ -1129,7 +1144,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
bool aliasPointsToOtherAlias = false;
int localAliasIndex = 0;
for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
- if (stringAt(targetAlias->nameIndex) == property) {
+ if (stringAt(targetAlias->nameIndex()) == property) {
aliasPointsToOtherAlias = true;
break;
}
@@ -1137,8 +1152,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (aliasPointsToOtherAlias) {
if (targetObjectIndex == objectIndex) {
alias->localAliasIndex = localAliasIndex;
- alias->aliasToLocalAlias = true;
- alias->flags |= QV4::CompiledData::Alias::Resolved;
+ alias->setIsAliasToLocalAlias(true);
+ alias->setFlag(QV4::CompiledData::Alias::Resolved);
++numResolvedAliases;
continue;
}
@@ -1200,12 +1215,12 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
}
} else {
if (targetProperty->isQObject())
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
}
}
alias->encodedMetaPropertyIndex = propIdx.toEncoded();
- alias->flags |= QV4::CompiledData::Alias::Resolved;
+ alias->setFlag(QV4::CompiledData::Alias::Resolved);
numResolvedAliases++;
}
@@ -1241,7 +1256,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
return scanObject(componentBinding->value.objectIndex);
}
@@ -1279,16 +1294,16 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
continue;
}
} else if (QmlIR::IRBuilder::isSignalPropertyName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
continue;
}
}
@@ -1307,7 +1322,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
bool seenSubObjectWithId = false;
- if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object
+ && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
const bool subObjectValid = scanObject(binding->value.objectIndex);
qSwap(_seenObjectWithId, seenSubObjectWithId);
@@ -1316,21 +1332,24 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
_seenObjectWithId |= seenSubObjectWithId;
}
- if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
+ if (!seenSubObjectWithId
+ && binding->type() != QV4::CompiledData::Binding::Type_GroupProperty
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsDeferredBinding);
obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
continue;
+ }
if (!pd) {
if (customParser) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
}
}
}
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 92a90ea677..c7bfcc55b1 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -203,26 +203,20 @@ bool QQmlTypeData::tryLoadFromDiskCache()
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return false;
}
}
- QQmlType containingType;
- auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
- int major = -1, minor = -1;
- QQmlImportNamespace *ns = nullptr;
- m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns);
for (auto&& ic: ics) {
QString const nameString = m_compiledData->stringAt(ic.nameIndex);
- QByteArray const name = nameString.toUtf8();
auto importUrl = finalUrl();
importUrl.setFragment(QString::number(ic.objectIndex));
auto import = new QQmlImportInstance();
- m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType);
+ m_importCache.addInlineComponentImport(import, nameString, importUrl, QQmlType());
}
return true;
@@ -330,8 +324,8 @@ void QQmlTypeData::done()
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.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);
@@ -354,8 +348,8 @@ void QQmlTypeData::done()
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.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(typeName.leftRef(lastDot), type.type.pendingResolutionName()));
errors.prepend(error);
setError(errors);
@@ -370,8 +364,8 @@ void QQmlTypeData::done()
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.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);
@@ -389,8 +383,8 @@ void QQmlTypeData::done()
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.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);
@@ -702,8 +696,8 @@ void QQmlTypeData::continueLoadFromIR()
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return;
@@ -729,8 +723,10 @@ void QQmlTypeData::allDependenciesDone()
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));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.column()));
errors.prepend(error);
}
}
@@ -864,8 +860,8 @@ void QQmlTypeData::resolveTypes()
bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference;
- if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line,
- unresolvedRef->location.column, reportErrors,
+ if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line(),
+ unresolvedRef->location.column(), reportErrors,
QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
return;
@@ -887,8 +883,7 @@ void QQmlTypeData::resolveTypes()
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
- ref.location.line = unresolvedRef->location.line;
- ref.location.column = unresolvedRef->location.column;
+ ref.location = unresolvedRef->location;
ref.needsCreation = unresolvedRef->needsCreation;
m_resolvedTypes.insert(unresolvedRef.key(), ref);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 1d66e756fa..01aba47a9a 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -697,8 +697,8 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
}
diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
index af97643163..bcc5306cf8 100644
--- a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
+++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
@@ -72,3 +72,5 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
}
QT_END_NAMESPACE
+
+#include "moc_qqmltypeloadernetworkreplyproxy_p.cpp"
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index fa5d36503d..a6ba4b8cb3 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -419,8 +419,10 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return Encode(false);
QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
- ExecutableCompilationUnit *cu = td->compilationUnit();
- myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
+ if (ExecutableCompilationUnit *cu = td->compilationUnit())
+ myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
+ else
+ return Encode(false); // It seems myQmlType has some errors, so we could not compile it.
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId);
}
@@ -458,11 +460,8 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
if (property) {
ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
- lookup->qobjectLookup.qmlTypeIc = This->internalClass();
- lookup->qobjectLookup.ic = val->objectValue()->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
+ setupQObjectLookup(lookup, ddata, property,
+ val->objectValue(), This);
lookup->getter = QQmlTypeWrapper::lookupSingletonProperty;
return lookup->getter(lookup, engine, *object);
}
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index aa9f4bc1bd..1e0e4e419f 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -231,17 +231,14 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId];
if (!aliasData->isObjectAlias()) {
QQmlContextData *ctxt = metaObject->ctxt;
- QObject *target = ctxt->idValues[aliasData->targetObjectId].data();
+ QObject *target = ctxt->idValues[aliasData->targetObjectId()].data();
if (!target)
return;
- QQmlData *targetDData = QQmlData::get(target, /*create*/false);
- if (!targetDData)
- return;
QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
int coreIndex = encodedIndex.coreIndex();
int valueTypeIndex = encodedIndex.valueTypeIndex();
- const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
+ const QQmlPropertyData *pd = QQmlData::ensurePropertyCache(qmlEngine(target), target)->property(coreIndex);
if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) {
// deep alias
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine());
@@ -758,7 +755,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
break;
case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
+ if (property.isList()) {
// when reading from the list, we need to find the correct MetaObject,
// namely this. However, obejct->metaObject might point to any MetaObject
// down the inheritance hierarchy, so we need to store how far we have
@@ -866,7 +863,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
break;
case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
+ if (property.isList()) {
// Writing such a property is not supported. Content is added through the list property
// methods.
} else {
@@ -888,18 +885,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (id < aliasCount) {
const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id];
- if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty)
- *reinterpret_cast<void **>(a[0]) = nullptr;
+ if (aliasData->hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject)
+ && c == QMetaObject::ReadProperty){
+ *reinterpret_cast<void **>(a[0]) = nullptr;
+ }
if (!ctxt) return -1;
- while (aliasData->aliasToLocalAlias)
+ while (aliasData->isAliasToLocalAlias())
aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
QQmlContext *context = ctxt->asQQmlContext();
QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context);
- QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data();
+ QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId()].data();
if (!target)
return -1;
@@ -1260,9 +1259,9 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex,
const int aliasId = index - propOffset() - compiledObject->nProperties;
const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
- while (aliasData->aliasToLocalAlias)
+ while (aliasData->isAliasToLocalAlias())
aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
- *target = ctxt->idValues[aliasData->targetObjectId].data();
+ *target = ctxt->idValues[aliasData->targetObjectId()].data();
if (!*target)
return false;
@@ -1290,7 +1289,7 @@ void QQmlVMEMetaObject::connectAlias(int aliasId)
}
endpoint->metaObject = this;
- endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings);
+ endpoint->connect(&ctxt->idValues[aliasData->targetObjectId()].bindings);
endpoint->tryConnect();
}
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 7b0910fa13..76479431dc 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -70,4 +70,26 @@ GHS_KEEP_REFERENCE(qml_register_types_QtQml);
# define Q_QML_AUTOTEST_EXPORT
#endif
+// When doing macOS universal builds, JIT needs to be disabled for the ARM slice.
+// Because both arm and x86_64 slices are built in one clang frontend invocation
+// we need this hack to ensure each backend invocation sees the correct value
+// of the feature definition.
+
+// Unset dummy value
+#undef QT_QML_JIT_SUPPORTED_IMPL
+// Compute per-arch value and save in extra define
+#if QT_CONFIG(qml_jit) && !(defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM))
+# define QT_QML_JIT_SUPPORTED_IMPL 1
+#else
+# define QT_QML_JIT_SUPPORTED_IMPL 0
+#endif
+// Unset original feature value
+#undef QT_FEATURE_qml_jit
+// Set new value based on previous computation
+#if QT_QML_JIT_SUPPORTED_IMPL
+# define QT_FEATURE_qml_jit 1
+#else
+# define QT_FEATURE_qml_jit -1
+#endif
+
#endif // QTQMLGLOBAL_P_H
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index 29ed62cd39..4a4e6ce12c 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -247,17 +247,20 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableC
return;
}
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Script)
+ continue;
+
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex);
if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
error(binding, QQmlConnections::tr("Connections: nested objects not allowed"));
else
error(binding, QQmlConnections::tr("Connections: syntax error"));
return;
- } if (binding->type != QV4::CompiledData::Binding::Type_Script) {
- error(binding, QQmlConnections::tr("Connections: script expected"));
- return;
}
+
+ error(binding, QQmlConnections::tr("Connections: script expected"));
+ return;
}
}
@@ -352,7 +355,7 @@ void QQmlConnections::connectSignalsToBindings()
QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr;
for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex);
QQmlProperty prop(target, propName);
diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp
index 82f048d9d9..5c4cd7352a 100644
--- a/src/qml/util/qqmlpropertymap.cpp
+++ b/src/qml/util/qqmlpropertymap.cpp
@@ -370,3 +370,5 @@ QQmlPropertyMap::QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *p
*/
QT_END_NAMESPACE
+
+#include "moc_qqmlpropertymap.cpp"
diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro
index ac3f3bf3bf..fd15c9054a 100644
--- a/src/qmldebug/qmldebug.pro
+++ b/src/qmldebug/qmldebug.pro
@@ -16,6 +16,7 @@ SOURCES += \
qqmlprofilerclient.cpp \
qqmlprofilerevent.cpp \
qqmlprofilereventlocation.cpp \
+ qqmlprofilereventreceiver.cpp \
qqmlprofilereventtype.cpp \
qqmlprofilertypedevent.cpp \
qv4debugclient.cpp
diff --git a/src/qmldebug/qqmldebugclient.cpp b/src/qmldebug/qqmldebugclient.cpp
index 03123cc6e0..13785dd6db 100644
--- a/src/qmldebug/qqmldebugclient.cpp
+++ b/src/qmldebug/qqmldebugclient.cpp
@@ -123,3 +123,5 @@ void QQmlDebugClient::messageReceived(const QByteArray &message)
}
QT_END_NAMESPACE
+
+#include "moc_qqmldebugclient_p.cpp"
diff --git a/src/qmldebug/qqmldebugmessageclient.cpp b/src/qmldebug/qqmldebugmessageclient.cpp
index 0892404194..ce7a3cb2bb 100644
--- a/src/qmldebug/qqmldebugmessageclient.cpp
+++ b/src/qmldebug/qqmldebugmessageclient.cpp
@@ -88,3 +88,5 @@ void QQmlDebugMessageClient::messageReceived(const QByteArray &data)
}
QT_END_NAMESPACE
+
+#include "moc_qqmldebugmessageclient_p.cpp"
diff --git a/src/qmldebug/qqmldebugtranslationclient.cpp b/src/qmldebug/qqmldebugtranslationclient.cpp
index 1fd0748fa0..486fc3316e 100644
--- a/src/qmldebug/qqmldebugtranslationclient.cpp
+++ b/src/qmldebug/qqmldebugtranslationclient.cpp
@@ -74,3 +74,5 @@ void QQmlDebugTranslationClient::triggerLanguage(const QUrl &url, const QString
}
QT_END_NAMESPACE
+
+#include "moc_qqmldebugtranslationclient_p.cpp"
diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp
index 0ca3f573d9..fd467337d8 100644
--- a/src/qmldebug/qqmlenginedebugclient.cpp
+++ b/src/qmldebug/qqmlenginedebugclient.cpp
@@ -566,3 +566,5 @@ qint32 QQmlEngineDebugClient::getId()
}
QT_END_NAMESPACE
+
+#include "moc_qqmlenginedebugclient_p.cpp"
diff --git a/src/qmldebug/qqmlinspectorclient.cpp b/src/qmldebug/qqmlinspectorclient.cpp
index 1de52bd0c1..a3f5c68f0d 100644
--- a/src/qmldebug/qqmlinspectorclient.cpp
+++ b/src/qmldebug/qqmlinspectorclient.cpp
@@ -148,3 +148,5 @@ void QQmlInspectorClient::messageReceived(const QByteArray &message)
}
QT_END_NAMESPACE
+
+#include "moc_qqmlinspectorclient_p.cpp"
diff --git a/src/qmldebug/qqmlpreviewclient.cpp b/src/qmldebug/qqmlpreviewclient.cpp
index 60937b9cfd..3a76476700 100644
--- a/src/qmldebug/qqmlpreviewclient.cpp
+++ b/src/qmldebug/qqmlpreviewclient.cpp
@@ -137,3 +137,5 @@ void QQmlPreviewClient::triggerLanguage(const QUrl &url, const QString &locale)
}
QT_END_NAMESPACE
+
+#include "moc_qqmlpreviewclient_p.cpp"
diff --git a/src/qmldebug/qqmlprofilerevent_p.h b/src/qmldebug/qqmlprofilerevent_p.h
index a7e37d1964..01b2f58f16 100644
--- a/src/qmldebug/qqmlprofilerevent_p.h
+++ b/src/qmldebug/qqmlprofilerevent_p.h
@@ -48,6 +48,7 @@
#include <QtCore/qmetatype.h>
#include <initializer_list>
+#include <limits>
#include <type_traits>
//
diff --git a/src/qmldebug/qqmlprofilereventreceiver.cpp b/src/qmldebug/qqmlprofilereventreceiver.cpp
new file mode 100644
index 0000000000..cb88f821f8
--- /dev/null
+++ b/src/qmldebug/qqmlprofilereventreceiver.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlprofilereventreceiver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlProfilerEventReceiver::~QQmlProfilerEventReceiver()
+ = default;
+
+QT_END_NAMESPACE
+
+#include "moc_qqmlprofilereventreceiver_p.cpp"
diff --git a/src/qmldebug/qqmlprofilereventreceiver_p.h b/src/qmldebug/qqmlprofilereventreceiver_p.h
index defe64a42e..45d9abf71d 100644
--- a/src/qmldebug/qqmlprofilereventreceiver_p.h
+++ b/src/qmldebug/qqmlprofilereventreceiver_p.h
@@ -62,7 +62,8 @@ class QQmlProfilerEventReceiver : public QObject
{
Q_OBJECT
public:
- QQmlProfilerEventReceiver(QObject *parent = nullptr) : QObject(parent) {}
+ explicit QQmlProfilerEventReceiver(QObject *parent = nullptr) : QObject(parent) {}
+ ~QQmlProfilerEventReceiver() override;
virtual int numLoadedEventTypes() const = 0;
virtual void addEventType(const QQmlProfilerEventType &type) = 0;
diff --git a/src/qmldebug/qv4debugclient.cpp b/src/qmldebug/qv4debugclient.cpp
index 76c2f1ebea..127e4a405e 100644
--- a/src/qmldebug/qv4debugclient.cpp
+++ b/src/qmldebug/qv4debugclient.cpp
@@ -576,3 +576,5 @@ QByteArray QV4DebugClientPrivate::packMessage(const QByteArray &type, const QJso
}
QT_END_NAMESPACE
+
+#include "moc_qv4debugclient_p.cpp"
diff --git a/src/qmlmodels/qqmlabstractdelegatecomponent.cpp b/src/qmlmodels/qqmlabstractdelegatecomponent.cpp
index 1058d87485..869c6a4fa9 100644
--- a/src/qmlmodels/qqmlabstractdelegatecomponent.cpp
+++ b/src/qmlmodels/qqmlabstractdelegatecomponent.cpp
@@ -59,3 +59,5 @@ QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, in
}
QT_END_NAMESPACE
+
+#include "moc_qqmlabstractdelegatecomponent_p.cpp"
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index 2126ad3dc5..991a5c9e08 100644
--- a/src/qmlmodels/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -962,30 +962,36 @@ QQmlAdaptorModel::~QQmlAdaptorModel()
accessors->cleanup(*this);
}
-void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine)
+void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *, QQmlEngine *engine)
{
accessors->cleanup(*this);
list.setList(variant, engine);
+ modelStrongReference.clear();
if (QObject *object = qvariant_cast<QObject *>(list.list())) {
- setObject(object, parent);
+ if (QQmlData *ddata = QQmlData::get(object))
+ modelStrongReference = ddata->jsWrapper;
+ setObject(object);
if (qobject_cast<QAbstractItemModel *>(object))
accessors = new VDMAbstractItemModelDataType(this);
else
accessors = new VDMObjectDelegateDataType;
} else if (list.type() == QQmlListAccessor::ListProperty) {
- setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent);
+ auto object = static_cast<const QQmlListReference *>(variant.constData())->object();
+ if (QQmlData *ddata = QQmlData::get(object))
+ modelStrongReference = ddata->jsWrapper;
+ setObject(object);
accessors = new VDMObjectDelegateDataType;
} else if (list.type() == QQmlListAccessor::ObjectList) {
- setObject(nullptr, parent);
+ setObject(nullptr);
accessors = new VDMObjectDelegateDataType;
} else if (list.type() != QQmlListAccessor::Invalid
&& list.type() != QQmlListAccessor::Instance) { // Null QObject
- setObject(nullptr, parent);
+ setObject(nullptr);
accessors = new VDMListDelegateDataType;
} else {
- setObject(nullptr, parent);
+ setObject(nullptr);
accessors = &qt_vdm_null_accessors;
}
}
diff --git a/src/qmlmodels/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h
index ba54c864c6..4dbb6ed54e 100644
--- a/src/qmlmodels/qqmladaptormodel_p.h
+++ b/src/qmlmodels/qqmladaptormodel_p.h
@@ -70,7 +70,7 @@ class QQmlDelegateModel;
class QQmlDelegateModelItem;
class QQmlDelegateModelItemMetaType;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject>
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlGuard<QObject>
{
public:
class Accessors
@@ -114,6 +114,10 @@ public:
const Accessors *accessors;
QPersistentModelIndex rootIndex;
QQmlListAccessor list;
+ // we need to ensure that a JS created model does not get gced, but cannot
+ // arbitrarily set the parent (using QQmlStrongJSQObjectReference) of QObject based models,
+ // as that causes issues with singletons
+ QV4::PersistentValue modelStrongReference;
int modelItemRevision = 0;
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index 12c3d11937..4fcff70de6 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -258,7 +258,7 @@ QQmlDelegateModel::~QQmlDelegateModel()
{
Q_D(QQmlDelegateModel);
d->disconnectFromAbstractItemModel();
- d->m_adaptorModel.setObject(nullptr, this);
+ d->m_adaptorModel.setObject(nullptr);
for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) {
if (cacheItem->object) {
@@ -968,6 +968,17 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
contextData->extraObject = modelItemToIncubate;
}
+ // If we have required properties, we clear the context object
+ // so that the model role names are not polluting the context
+ if (incubating) {
+ Q_ASSERT(incubating->contextData);
+ incubating->contextData->contextObject = nullptr;
+ }
+
+ if (proxyContext) {
+ proxyContext->contextObject = nullptr;
+ }
+
if (incubatorPriv->requiredProperties().empty())
return;
RequiredProperties &requiredProperties = incubatorPriv->requiredProperties();
@@ -1279,6 +1290,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
QQmlContextData *ctxt = new QQmlContextData;
ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data()));
+ ctxt->contextObject = cacheItem;
cacheItem->contextData = ctxt;
if (m_adaptorModel.hasProxyObject()) {
@@ -1289,6 +1301,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
QObject *proxied = proxy->proxiedObject();
cacheItem->incubationTask->proxiedObject = proxied;
cacheItem->incubationTask->proxyContext = ctxt;
+ ctxt->contextObject = cacheItem;
// We don't own the proxied object. We need to clear it if it goes away.
QObject::connect(proxied, &QObject::destroyed,
cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed);
@@ -1343,6 +1356,8 @@ QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incu
QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
{
Q_D(QQmlDelegateModel);
+ if (d->m_compositor.count(d->m_compositorGroup) <= index)
+ return QQmlIncubator::Null;
Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index);
if (!it->inCache())
return QQmlIncubator::Null;
@@ -1609,7 +1624,7 @@ void QQmlDelegateModelPrivate::itemsRemoved(
removed[i] = 0;
for (const Compositor::Remove &remove : removes) {
- for (; cacheIndex < remove.cacheIndex; ++cacheIndex)
+ for (; cacheIndex < remove.cacheIndex && cacheIndex < m_cache.size(); ++cacheIndex)
incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed);
for (int i = 1; i < m_groupCount; ++i) {
@@ -1713,6 +1728,7 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count)
return;
d->m_count -= count;
+ Q_ASSERT(d->m_count >= 0);
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
//Prevents items being deleted in remove loop
for (QQmlDelegateModelItem *item : cache)
@@ -1875,6 +1891,9 @@ void QQmlDelegateModel::_q_modelReset()
d->m_count = d->adaptorModelCount();
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
+ for (QQmlDelegateModelItem *item : cache)
+ item->referenceObject();
+
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
// layout change triggered by changing the modelIndex might have
@@ -1886,6 +1905,8 @@ void QQmlDelegateModel::_q_modelReset()
item->setModelIndex(-1, -1, -1);
}
+ for (QQmlDelegateModelItem *item : cache)
+ item->releaseObject();
QVector<Compositor::Remove> removes;
QVector<Compositor::Insert> inserts;
if (oldCount)
@@ -3902,4 +3923,6 @@ QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV4::ExecutionEngine *v4,
QT_END_NAMESPACE
+#include "moc_qqmldelegatemodel_p_p.cpp"
+
#include "moc_qqmldelegatemodel_p.cpp"
diff --git a/src/qmlmodels/qqmlinstantiator.cpp b/src/qmlmodels/qqmlinstantiator.cpp
index 3a0d746eb6..1d5249d4c0 100644
--- a/src/qmlmodels/qqmlinstantiator.cpp
+++ b/src/qmlmodels/qqmlinstantiator.cpp
@@ -147,7 +147,7 @@ void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bo
{
Q_Q(QQmlInstantiator);
- if (!componentComplete || effectiveReset)
+ if (!componentComplete || effectiveReset || !active)
return;
if (reset) {
diff --git a/src/qmlmodels/qqmllistcompositor_p.h b/src/qmlmodels/qqmllistcompositor_p.h
index 6639726850..ccd565a5bd 100644
--- a/src/qmlmodels/qqmllistcompositor_p.h
+++ b/src/qmlmodels/qqmllistcompositor_p.h
@@ -311,6 +311,10 @@ Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE);
inline QQmlListCompositor::iterator::iterator() {}
+QT_WARNING_PUSH
+// GCC isn't wrong, as groupCount is public in iterator, but we tried Q_ASSUME(),
+// right in front of the loops, and it didn't help, so we disable the warning:
+QT_WARNING_DISABLE_GCC("-Warray-bounds")
inline QQmlListCompositor::iterator::iterator(
Range *range, int offset, Group group, int groupCount)
: range(range)
@@ -338,6 +342,7 @@ inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint
index[i] -= difference;
}
}
+QT_WARNING_POP // -Warray-bounds
inline QQmlListCompositor::insert_iterator::insert_iterator(
Range *range, int offset, Group group, int groupCount)
diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index 8830e08097..c181cf15c7 100644
--- a/src/qmlmodels/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -446,7 +446,8 @@ bool ListModel::sync(ListModel *src, ListModel *target)
// to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent
// model indices are updated correctly
int rowsInserted = 0;
- for (int i = 0 ; i < target->elements.count() ; ++i) {
+ const int targetElementCount = target->elements.count();
+ for (int i = 0 ; i < targetElementCount ; ++i) {
ListElement *element = target->elements.at(i);
ElementSync &s = elementHash.find(element->getUid()).value();
Q_ASSERT(s.srcIndex >= 0);
@@ -456,13 +457,33 @@ bool ListModel::sync(ListModel *src, ListModel *target)
if (s.targetIndex == -1) {
targetModel->beginInsertRows(QModelIndex(), i, i);
targetModel->endInsertRows();
+ ++rowsInserted;
} else {
- targetModel->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
+ bool validMove = targetModel->beginMoveRows(QModelIndex(), s.targetIndex, s.targetIndex, QModelIndex(), i);
+ Q_ASSERT(validMove);
+ Q_UNUSED(validMove); // fixes unused variable warning if asserts are disabled
targetModel->endMoveRows();
+ // fixup target indices of elements that still need to move
+ for (int j=i+1; j < targetElementCount; ++j) {
+ ListElement *eToFix = target->elements.at(j);
+ ElementSync &sToFix = elementHash.find(eToFix->getUid()).value();
+ if (i < s.targetIndex) {
+ // element was moved down
+ if (sToFix.targetIndex > s.targetIndex || sToFix.targetIndex < i)
+ continue; // unaffected by reordering
+ else
+ sToFix.targetIndex += 1;
+ } else {
+ // element was moved up
+ if (sToFix.targetIndex < s.targetIndex || sToFix.targetIndex > i)
+ continue; // unaffected by reordering
+ else
+ sToFix.targetIndex -= 1;
+ }
+ }
}
}
hasChanges = true;
- ++rowsInserted;
}
if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
QModelIndex idx = targetModel->createIndex(i, 0);
@@ -2698,7 +2719,7 @@ void QQmlListModel::sync()
bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
@@ -2726,7 +2747,7 @@ bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::ExecutableCom
if (!verifyProperty(compilationUnit, binding))
return false;
}
- } else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
+ } else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
QByteArray script = scriptStr.toUtf8();
@@ -2749,7 +2770,8 @@ bool QQmlListModelParser::applyProperty(
const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
bool roleSet = false;
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
@@ -2782,13 +2804,13 @@ bool QQmlListModelParser::applyProperty(
value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
} else if (binding->evaluatesToString()) {
value = compilationUnit->bindingValueAsString(binding);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
value = compilationUnit->bindingValueAsNumber(binding);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
value = binding->valueAsBoolean();
- } else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
value = QVariant::fromValue(nullptr);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
if (definesEmptyList(scriptStr)) {
const ListLayout::Role &role = model->getOrCreateListRole(elementName);
@@ -2851,7 +2873,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::
bool setRoles = false;
for (const QV4::CompiledData::Binding *binding : bindings) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Object)
continue;
setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1);
}
@@ -2921,4 +2943,6 @@ bool QQmlListModelParser::definesEmptyList(const QString &s)
QT_END_NAMESPACE
+#include "moc_qqmllistmodel_p_p.cpp"
+
#include "moc_qqmllistmodel_p.cpp"
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index 312769b8d3..c0cf3e469c 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -309,26 +309,29 @@ private:
if (!object) // Start at root of compilation unit if not enumerating a specific child
object = compilationUnit->objectAt(0);
+ if (object->hasFlag(Object::IsInlineComponentRoot))
+ return result;
if (const auto superTypeUnit = compilationUnit->resolvedTypes.value(
object->inheritedTypeNameIndex)->compilationUnit()) {
// We have a non-C++ super type, which could indicate we're a subtype of a TestCase
if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl())
result.isTestCase = true;
- else
+ else if (superTypeUnit->url() != compilationUnit->url()) { // urls are the same for inline component, avoid infinite recursion
result = enumerateTestCases(superTypeUnit);
+ }
if (result.isTestCase) {
// Look for override of name in this type
for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1String("name")) {
- if (binding->type == QV4::CompiledData::Binding::Type_String) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_String) {
result.testCaseName = compilationUnit->stringAt(binding->stringIndex);
} else {
QQmlError error;
error.setUrl(compilationUnit->url());
- error.setLine(binding->location.line);
- error.setColumn(binding->location.column);
+ error.setLine(binding->location.line());
+ error.setColumn(binding->location.column());
error.setDescription(QStringLiteral("the 'name' property of a TestCase must be a literal string"));
result.errors << error;
}
@@ -352,7 +355,7 @@ private:
}
for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Object) {
const Object *child = compilationUnit->objectAt(binding->value.objectIndex);
result << enumerateTestCases(compilationUnit, child);
}
@@ -647,3 +650,5 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
}
QT_END_NAMESPACE
+
+#include "moc_quicktest_p.cpp"
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index f3a6fea193..6a9dc335ff 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -773,7 +773,8 @@ QObject *QuickTestResult::grabImage(QQuickItem *item)
if (item && item->window()) {
QQuickWindow *window = item->window();
QImage grabbed = window->grabWindow();
- QRectF rf(item->x(), item->y(), item->width(), item->height());
+ const auto dpi = grabbed.devicePixelRatio();
+ QRectF rf(item->x() * dpi, item->y() * dpi, item->width() * dpi, item->height() * dpi);
rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height()));
QObject *o = new QuickTestImageObject(grabbed.copy(rf.toAlignedRect()));
QQmlEngine::setContextForObject(o, qmlContext(this));
diff --git a/src/qmltyperegistrar/qmltypes.prf b/src/qmltyperegistrar/qmltypes.prf
index 2cc0027b7e..471bb6e3b0 100644
--- a/src/qmltyperegistrar/qmltypes.prf
+++ b/src/qmltyperegistrar/qmltypes.prf
@@ -81,12 +81,18 @@ qmltyperegistrar_compiler.output = $$TYPEREGISTRATIONS
qmltyperegistrar_compiler.variable_out = SOURCES
qmltyperegistrar_compiler.name = Automatic QML type registration
qmltyperegistrar_compiler.dependency_type = TYPE_C
-
-qmltyperegistrar_qmltypes.input = METATYPES_JSON
-qmltyperegistrar_qmltypes.depends = $$TYPEREGISTRATIONS
-qmltyperegistrar_qmltypes.output = $$QMLTYPES_FILENAME
-qmltyperegistrar_qmltypes.CONFIG = no_link
-qmltyperegistrar_qmltypes.commands = $$escape_expand(\\n) # force creation of rule
+QMAKE_EXTRA_COMPILERS += qmltyperegistrar_compiler
+
+!contains(TEMPLATE, "vc.*") { # work around QTBUG-91033
+ # Create a fake extra compiler to announce that we generate $$QMLTYPES_FILENAME.
+ # This allows us to use $$QMLTYPES_FILENAME as input in other extra compilers.
+ qmltyperegistrar_qmltypes.input = METATYPES_JSON
+ qmltyperegistrar_qmltypes.depends = $$TYPEREGISTRATIONS
+ qmltyperegistrar_qmltypes.output = $$QMLTYPES_FILENAME
+ qmltyperegistrar_qmltypes.CONFIG = no_link
+ qmltyperegistrar_qmltypes.commands = $$escape_expand(\\n) # force creation of rule
+ QMAKE_EXTRA_COMPILERS += qmltyperegistrar_qmltypes
+}
install_qmltypes {
INSTALL_QML_FILES = false
@@ -109,5 +115,3 @@ install_qmltypes {
else: COPIES += do_install_qmltypes
}
}
-
-QMAKE_EXTRA_COMPILERS += qmltyperegistrar_compiler qmltyperegistrar_qmltypes
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 5e1ae25c38..ae1954ae8d 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -177,6 +177,11 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const
return accessibleUnignoredChildren(item());
}
+static bool isTextRole(QAccessible::Role role)
+{
+ return role == QAccessible::EditableText || role == QAccessible::StaticText;
+}
+
QAccessible::State QAccessibleQuickItem::state() const
{
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
@@ -194,7 +199,7 @@ QAccessible::State QAccessibleQuickItem::state() const
state.offscreen = true;
if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && object()->property("checked").toBool())
state.checked = true;
- if (item()->activeFocusOnTab() || role() == QAccessible::EditableText)
+ if (item()->activeFocusOnTab() || isTextRole(role()))
state.focusable = true;
if (item()->hasActiveFocus())
state.focused = true;
@@ -216,6 +221,8 @@ QAccessible::Role QAccessibleQuickItem::role() const
if (role == QAccessible::NoRole) {
if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
role = QAccessible::StaticText;
+ else if (qobject_cast<QQuickTextInput*>(const_cast<QQuickItem *>(item())))
+ role = QAccessible::EditableText;
else
role = QAccessible::Client;
}
@@ -232,6 +239,7 @@ QStringList QAccessibleQuickItem::actionNames() const
{
QStringList actions;
switch (role()) {
+ case QAccessible::Link:
case QAccessible::PushButton:
actions << QAccessibleActionInterface::pressAction();
break;
diff --git a/src/quick/accessible/qaccessiblequickitem_p.h b/src/quick/accessible/qaccessiblequickitem_p.h
index 931e995f0f..ccc9280ba0 100644
--- a/src/quick/accessible/qaccessiblequickitem_p.h
+++ b/src/quick/accessible/qaccessiblequickitem_p.h
@@ -54,6 +54,7 @@
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickView>
#include <QtGui/qaccessibleobject.h>
+#include <QtQuick/private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -61,7 +62,7 @@ QT_BEGIN_NAMESPACE
class QTextDocument;
-class QAccessibleQuickItem : public QAccessibleObject, public QAccessibleActionInterface, public QAccessibleValueInterface, public QAccessibleTextInterface
+class Q_QUICK_PRIVATE_EXPORT QAccessibleQuickItem : public QAccessibleObject, public QAccessibleActionInterface, public QAccessibleValueInterface, public QAccessibleTextInterface
{
public:
QAccessibleQuickItem(QQuickItem *item);
diff --git a/src/quick/designer/qquickdesignersupportproperties.cpp b/src/quick/designer/qquickdesignersupportproperties.cpp
index fb6a5fb324..479e77bf68 100644
--- a/src/quick/designer/qquickdesignersupportproperties.cpp
+++ b/src/quick/designer/qquickdesignersupportproperties.cpp
@@ -126,16 +126,15 @@ void QQuickDesignerSupportProperties::getPropertyCache(QObject *object, QQmlEngi
QQmlEnginePrivate::get(engine)->cache(object->metaObject());
}
-QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propertyNameListForWritableProperties(QObject *object,
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName,
- QObjectList *inspectedObjects)
+ QObjectList *inspectedObjects,
+ int depth = 0)
{
QQuickDesignerSupport::PropertyNameList propertyNameList;
- QObjectList localObjectList;
-
- if (inspectedObjects == nullptr)
- inspectedObjects = &localObjectList;
+ if (depth > 2)
+ return propertyNameList;
if (!inspectedObjects->contains(object))
inspectedObjects->append(object);
@@ -150,14 +149,16 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propert
if (childObject)
propertyNameList.append(propertyNameListForWritableProperties(childObject,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
} else if (QQmlGadgetPtrWrapper *valueType
= QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.userType())) {
valueType->setValue(metaProperty.read(object));
propertyNameList.append(propertyNameListForWritableProperties(valueType,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
if (metaProperty.isReadable() && metaProperty.isWritable()) {
@@ -169,6 +170,12 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propert
return propertyNameList;
}
+QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propertyNameListForWritableProperties(QObject *object)
+{
+ QObjectList localObjectList;
+ return ::propertyNameListForWritableProperties(object, {}, &localObjectList);
+}
+
bool QQuickDesignerSupportProperties::isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName)
{
if (propertyName.contains(".") && propertyName.contains("__"))
@@ -182,7 +189,8 @@ bool QQuickDesignerSupportProperties::isPropertyBlackListed(const QQuickDesigner
QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allPropertyNames(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName,
- QObjectList *inspectedObjects)
+ QObjectList *inspectedObjects,
+ int depth)
{
QQuickDesignerSupport::PropertyNameList propertyNameList;
@@ -191,6 +199,9 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
if (inspectedObjects == nullptr)
inspectedObjects = &localObjectList;
+ if (depth > 2)
+ return propertyNameList;
+
if (!inspectedObjects->contains(object))
inspectedObjects->append(object);
@@ -214,7 +225,8 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
propertyNameList.append(allPropertyNames(childObject,
baseName
+ QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
}
} else if (QQmlGadgetPtrWrapper *valueType
= QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.userType())) {
@@ -223,7 +235,8 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp
propertyNameList.append(allPropertyNames(valueType,
baseName
+ QQuickDesignerSupport::PropertyName(metaProperty.name())
- + '.', inspectedObjects));
+ + '.', inspectedObjects,
+ depth + 1));
} else {
addToPropertyNameListIfNotBlackListed(&propertyNameList,
baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
diff --git a/src/quick/designer/qquickdesignersupportproperties_p.h b/src/quick/designer/qquickdesignersupportproperties_p.h
index 02e75ea886..5970eca9f1 100644
--- a/src/quick/designer/qquickdesignersupportproperties_p.h
+++ b/src/quick/designer/qquickdesignersupportproperties_p.h
@@ -90,12 +90,11 @@ public:
static void getPropertyCache(QObject *object, QQmlEngine *engine);
static bool isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName);
- static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object,
- const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
- QObjectList *inspectedObjects = nullptr);
+ static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object);
static QQuickDesignerSupport::PropertyNameList allPropertyNames(QObject *object,
const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
- QObjectList *inspectedObjects = nullptr);
+ QObjectList *inspectedObjects = nullptr,
+ int depth = 0);
static bool hasFullImplementedListInterface(const QQmlListReference &list);
};
diff --git a/src/quick/doc/images/containmentMask-circle.gif b/src/quick/doc/images/containmentMask-circle.gif
new file mode 100644
index 0000000000..80abce625f
--- /dev/null
+++ b/src/quick/doc/images/containmentMask-circle.gif
Binary files differ
diff --git a/src/quick/doc/images/containmentMask-shape.gif b/src/quick/doc/images/containmentMask-shape.gif
new file mode 100644
index 0000000000..e7989352eb
--- /dev/null
+++ b/src/quick/doc/images/containmentMask-shape.gif
Binary files differ
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml b/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml
new file mode 100644
index 0000000000..b8a378ddcf
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ width: 150; height: 50; radius: 3
+ color: control.hovered ? "goldenrod" : shift.hovered ? "wheat" : "beige"
+
+ HoverHandler {
+ id: control
+ acceptedModifiers: Qt.ControlModifier
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ HoverHandler {
+ id: shift
+ acceptedModifiers: Qt.ShiftModifier
+ cursorShape: Qt.CrossCursor
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
new file mode 100644
index 0000000000..6bd6a40b1a
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ width: 150; height: 50; radius: 3
+ color: mouse.hovered ? "goldenrod" : stylus.hovered ? "tomato" : "wheat"
+
+ HoverHandler {
+ id: stylus
+ acceptedDevices: PointerDevice.Stylus
+ cursorShape: Qt.CrossCursor
+ }
+
+ HoverHandler {
+ id: mouse
+ acceptedDevices: PointerDevice.Mouse
+ cursorShape: Qt.PointingHandCursor
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml b/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml
new file mode 100644
index 0000000000..5d82158baf
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ id: rect
+ width: 150; height: 150
+
+ HoverHandler {
+ id: stylus
+ acceptedPointerTypes: PointerDevice.Pen
+ cursorShape: Qt.CrossCursor
+ }
+
+ HoverHandler {
+ id: eraser
+ acceptedPointerTypes: PointerDevice.Eraser
+ cursorShape: Qt.BlankCursor
+ target: Image {
+ parent: rect
+ source: "images/cursor-eraser.png"
+ visible: eraser.hovered
+ x: eraser.point.position.x
+ y: eraser.point.position.y - 32
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
new file mode 100644
index 0000000000..1564aa16b5
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.12
+
+Rectangle {
+ id: button
+ signal clicked
+
+ width: 150; height: 50; radius: 3
+ color: tapHandler.pressed ? "goldenrod" : hoverHandler.hovered ? "wheat" : "beige"
+ border.color: activeFocus ? "brown" : "transparent"
+ focus: true
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ id: tapHandler
+ onTapped: button.clicked()
+ }
+
+ Keys.onEnterPressed: button.clicked()
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png b/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png
new file mode 100644
index 0000000000..e5488a89f2
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/externaldrag.qml b/src/quick/doc/snippets/qml/externaldrag.qml
index 97a23293ca..5a88be2d4b 100644
--- a/src/quick/doc/snippets/qml/externaldrag.qml
+++ b/src/quick/doc/snippets/qml/externaldrag.qml
@@ -48,7 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.8
+import QtQuick 2.12
Item {
width: 200; height: 200
@@ -59,7 +59,7 @@ Item {
color: "green"
radius: 5
- Drag.active: dragArea.drag.active
+ Drag.active: dragHandler.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: {
@@ -72,14 +72,14 @@ Item {
text: "Drag me"
}
- MouseArea {
- id: dragArea
- anchors.fill: parent
-
- drag.target: parent
- onPressed: parent.grabToImage(function(result) {
- parent.Drag.imageSource = result.url
- })
+ DragHandler {
+ id: dragHandler
+ onActiveChanged:
+ if (active) {
+ parent.grabToImage(function(result) {
+ parent.Drag.imageSource = result.url;
+ })
+ }
}
}
}
diff --git a/src/quick/doc/snippets/qml/item/childrenRect.qml b/src/quick/doc/snippets/qml/item/childrenRect.qml
new file mode 100644
index 0000000000..769633dc2a
--- /dev/null
+++ b/src/quick/doc/snippets/qml/item/childrenRect.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+//! [local]
+Item {
+ x: 50
+ y: 100
+
+ // prints: QRectF(-10, -20, 30, 40)
+ Component.onCompleted: print(childrenRect)
+
+ Item {
+ x: -10
+ y: -20
+ width: 30
+ height: 40
+ }
+}
+//! [local]
diff --git a/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml b/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml
new file mode 100644
index 0000000000..2d97bcdafa
--- /dev/null
+++ b/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQml 2.12
+import QtQuick 2.12
+
+//![0]
+Rectangle {
+ id: circle
+ width: 100; height: width
+ radius: width / 2
+ color: tapHandler.pressed ? "tomato" : hoverHandler.hovered ? "darkgray" : "lightgray"
+
+ TapHandler { id: tapHandler }
+ HoverHandler { id: hoverHandler }
+
+ containmentMask: QtObject {
+ property alias radius: circle.radius
+ function contains(point: point) : bool {
+ return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2)
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/item/containmentMask-shape.qml b/src/quick/doc/snippets/qml/item/containmentMask-shape.qml
new file mode 100644
index 0000000000..a3da217e73
--- /dev/null
+++ b/src/quick/doc/snippets/qml/item/containmentMask-shape.qml
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Shapes 1.12
+
+//![0]
+Rectangle {
+ width: 90; height: 100
+ color: hoverHandler.hovered ? "wheat" : "lightgray"
+ containmentMask: shape
+
+ HoverHandler { id: hoverHandler }
+
+ Shape {
+ id: shape
+ containsMode: Shape.FillContains
+
+ ShapePath {
+ fillColor: "lightsteelblue"
+ startX: 10; startY: 20
+ PathArc {
+ x: 10; y: 80
+ radiusX: 40; radiusY: 40
+ useLargeArc: true
+ }
+ PathLine {
+ x: 10; y: 20
+ }
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/src/concepts/input/focus.qdoc b/src/quick/doc/src/concepts/input/focus.qdoc
index 9862489f42..21f5f0e225 100644
--- a/src/quick/doc/src/concepts/input/focus.qdoc
+++ b/src/quick/doc/src/concepts/input/focus.qdoc
@@ -196,7 +196,7 @@ property. As the \l ListView is a focus scope, this doesn't affect the
rest of the application. However, if the \l ListView itself has
active focus this causes the delegate itself to receive active focus.
In this example, the root type of the delegate is also a focus scope,
-which in turn gives active focus to the \c {Text} type that actually performs
+which in turn gives active focus to the \l {TextInput} type that actually performs
the work of handling the \c {Return} key.
All of the QML view classes, such as \l PathView and \l GridView, behave
diff --git a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index 2ac9860e6f..bf889a9066 100644
--- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -30,20 +30,21 @@
\title Qt Quick Input Handlers
\brief A module with a set of QML elements that handle events from input devices in a user interface.
- Qt Quick Input Handlers are a set of QML types used to handle events from
- keyboard, touch, mouse, and stylus devices in a UI. In contrast to event-handling
- items, such as \l MouseArea and \l Flickable, input handlers are explicitly non-visual,
- require less memory and are intended to be used in greater numbers: one
- handler instance per aspect of interaction. Each input handler instance
- handles certain events on behalf of its \c parent Item. Thus the visual and
+ Qt Quick Input Handlers are a set of QML types used to handle
+ \l {QInputEvent}{events} from keyboard, touch, mouse, and stylus
+ \l {QInputDevice}{devices} in a UI. In contrast to event-handling
+ items, such as \l MouseArea and \l Flickable, input handlers are explicitly
+ non-visual, require less memory and are intended to be used in greater
+ numbers: one handler instance per aspect of interaction. Each input handler
+ instance handles certain events on behalf of its
+ \l {QQuickPointerHandler::parent()}{parent} Item. Thus the visual and
behavioral concerns are better separated, and the behavior is built up by
finer-grained composition.
- In Qt 5.10, these handlers were introduced in a separate Qt.labs.handlers module.
- Now they are included with Qt Quick since 5.12. The pre-existing
- \l Keys attached property is similar in concept, so we refer to the
- pointing-device-oriented handlers plus \c Keys together as the set of Input Handlers.
- We expect to offer more attached-property use cases in future versions of Qt.
+ The pre-existing \l Keys attached property is similar in concept, so we
+ refer to the pointing-device-oriented handlers plus \c Keys together as the
+ set of Input Handlers. We expect to offer more attached-property use cases
+ in future versions of Qt.
\section1 Input Handlers
@@ -60,7 +61,44 @@
\li Each Item can have unlimited Handlers
\endlist
- \omit TODO actual overview with snippets and stuff \endomit
+ \section1 Handlers Manipulating Items
+
+ Some Handlers add interactivity simply by being declared inside an Item:
+
+ \snippet pointerHandlers/dragHandler.qml 0
+
+ \section1 Handler Properties and Signals
+
+ All Handlers have properties that can be used in bindings, and signals that
+ can be handled to react to input:
+
+ \snippet pointerHandlers/hoverTapKeyButton.qml 0
+
+ \section1 Pointer Grab
+
+ An important concept with Pointer Handlers is the type of grabs that they
+ perform. The only kind of grab an Item can take is the exclusive grab: for
+ example if you call \l QPointerEvent::setExclusiveGrabber(), the following
+ mouse moves and mouse release event will be sent only to that object. (As a
+ workaround to this exclusivity, see \l QQuickItem::setFiltersChildMouseEvents()
+ and \l QQuickItem::childMouseEventFilter().) However Pointer Handlers have
+ an additional mechanism available: the
+ \l {QPointerEvent::addPassiveGrabber()} {passive grab}. Mouse and touch
+ \l {QEventPoint::state()}{press} events are delivered by visiting all the
+ Items in top-down Z order: first each Item's child Handlers, and then the
+ \l {QQuickItem::event()}{Item} itself. At the time a press event is
+ delivered, a Handler can take either a passive or an exclusive grab
+ depending on its needs. If it takes a passive grab, it is guaranteed to
+ receive the updates and the release, even if other Items or Handlers in the
+ scene take any kind of grab, passive or exclusve. Some Handlers (such as
+ PointHandler) can work only with passive grabs; others require exclusive
+ grabs; and others can "lurk" with passive grabs until they detect that a
+ gesture is being performed, and then make the transition from passive to
+ exclusive grab.
+
+ When a grab transition is requested, \l PointerHandler::grabPermissions,
+ \l QQuickItem::keepMouseGrab() and \l QQuickItem::keepTouchGrab() control
+ whether the transition will be allowed.
\section1 Related Information
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index abfff7cc11..8cd51a50c6 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -52,11 +52,16 @@ options that align with the latest UI design trends. If these UI controls do not
satisfy your application's needs, only then it is recommended to create a
custom control.
+You can use the controls when you design UIs in Qt Design Studio. In addition,
+it provides timeline-based animations, visual effects, layouts, and a
+live-preview for prototyping applications.
\section2 Related Information
\list
\li \l{Qt Quick Controls}
+\li \l{Customizing Qt Quick Controls}
\li \l{Qt Quick}
+\li \l{Qt Design Studio Manual}
\endlist
\omit
@@ -146,7 +151,7 @@ specific file managed by the resource system. For example, if we wanted to give
\li \l{The Qt Resource System}
\endlist
-\section1 Separate UI from Logic
+\section1 Separate UI from Business Logic
One of the key goals that most application developers want to achieve is to
create a maintainable application. One of the ways to achieve this goal is
@@ -162,8 +167,8 @@ reasons why an application's UI should be written in QML:
\li JavaScript can easily be used in QML to respond to events.
\endlist
-Being a strongly typed language, C++ is best suited for an application's logic.
-Typically, such code performs tasks such as complex calculations
+Being a strongly typed language, C++ is best suited for an application's
+business logic. Typically, such code performs tasks such as complex calculations
or data processing, which are faster in C++ than QML.
Qt offers various approaches to integrate QML and C++ code in an application.
@@ -326,6 +331,22 @@ see \l {Choosing the Correct Integration Method Between C++ and QML}.
\li \l{Qt Quick Controls - Chat Tutorial}{Chat application tutorial}
\endlist
+\section1 Using Qt Design Studio
+
+Qt Design Studio uses UI files that have the filename extension \e {.ui.qml}
+to separate the visual parts of the UI from the UI logic you implement in
+\e {.qml} files. You should edit UI files only in the \uicontrol {2D} view in
+Qt Design Studio. If you use some other tool to add code that Qt Design Studio
+does not support, it displays error messages. Fix the errors to enable visual
+editing of the UI files again. Typically, you should move the unsupported code
+to a \e {.qml} file.
+
+\section2 Related Information
+
+\list
+ \li \l{Qt Design Studio: UI Files}
+\endlist
+
\section1 Using Qt Quick Layouts
Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout.
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 528444cad3..65de1284a4 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -740,6 +740,73 @@ console.log(c + " " + d); // false true
\li Example
\row
+ \li translate(vector3d vector)
+ \li Multiplies \c this matrix4x4 by another that translates coordinates by the components
+ of \c vector
+ \li \code
+var m = Qt.matrix4x4();
+m.translate(Qt.vector3d(1,2,3));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 1, 0, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li rotate(real angle, vector3d axis)
+ \li Multiples \c this matrix4x4 by another that rotates coordinates through
+ \c angle degrees about \c axis
+ \li \code
+var m = Qt.matrix4x4();
+m.rotate(180,vector3d(1,0,0));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(real factor)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the given \c factor
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(2);
+console.log(m.toString());
+// QMatrix4x4(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(real x, real y, real z)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the components
+ \c x, \c y, and \c z
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(1,2,3);
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li scale(vector3d vector)
+ \li Multiplies \c this matrix4x4 by another that scales coordinates by the components
+ of \c vector
+ \li \code
+var m = Qt.matrix4x4();
+m.scale(Qt.vector3d(1,2,3));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1)
+ \endcode
+
+ \row
+ \li lookAt(vector3d eye, vector3d center, vector3d up)
+ \li Multiplies \c this matrix4x4 by a viewing matrix derived from an \c eye point.
+ The \c center vector3d indicates the center of the view that the \c eye is looking at.
+ The \c up vector3d indicates which direction should be considered up with respect to
+ the \c eye.
+ \li \code
+var m = Qt.matrix4x4();
+m.lookAt(Qt.vector3d(1,2,3),Qt.vector3d(1,2,0),Qt.vector3d(0,1,0));
+console.log(m.toString());
+// QMatrix4x4(1, 0, 0, -1, 0, 1, 0, -2, 0, 0, 1, -3, 0, 0, 0, 1)
+ \endcode
+
+ \row
\li matrix4x4 times(matrix4x4 other)
\li Returns the matrix4x4 result of multiplying \c this matrix4x4 with
the \c other matrix4x4
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
index 88470c8a7d..806b5d705a 100644
--- a/src/quick/handlers/qquickdragaxis.cpp
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -76,3 +76,5 @@ void QQuickDragAxis::setEnabled(bool enabled)
}
QT_END_NAMESPACE
+
+#include "moc_qquickdragaxis_p.cpp"
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 492897b68b..b9a2c183c2 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -343,3 +343,5 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickdraghandler_p.cpp"
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index 72efdfd0f4..7103206470 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -348,3 +348,5 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickhandlerpoint_p.cpp"
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index b12d85784a..a65730607a 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -85,8 +85,11 @@ QQuickHoverHandler::~QQuickHoverHandler()
void QQuickHoverHandler::componentComplete()
{
- parentItem()->setAcceptHoverEvents(true);
- QQuickItemPrivate::get(parentItem())->setHasHoverInChild(true);
+ QQuickSinglePointHandler::componentComplete();
+ if (auto par = parentItem()) {
+ par->setAcceptHoverEvents(true);
+ QQuickItemPrivate::get(par)->setHasHoverInChild(true);
+ }
}
bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
@@ -124,6 +127,13 @@ void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
setPassiveGrab(point);
}
+void QQuickHoverHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
+{
+ QQuickSinglePointHandler::onGrabChanged(grabber, transition, point);
+ if (grabber == this && transition == QQuickEventPoint::CancelGrabPassive)
+ setHovered(false);
+}
+
/*!
\qmlproperty bool QtQuick::HoverHandler::hovered
\readonly
@@ -144,6 +154,98 @@ void QQuickHoverHandler::setHovered(bool hovered)
}
/*!
+ \internal
+ \qmlproperty flags QtQuick::HoverHandler::acceptedButtons
+
+ This property is not used in HoverHandler.
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedDevices
+
+ The types of pointing devices that can activate the pointer handler.
+
+ By default, this property is set to
+ \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
+ If you set it to an OR combination of device types, it will ignore pointer
+ events from the non-matching devices.
+
+ For example, an item could be made to respond to mouse hover in one way,
+ and stylus hover in another way, with two handlers:
+
+ \snippet pointerHandlers/hoverMouseOrStylus.qml 0
+
+ The available device types are as follows:
+
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+ \value PointerDevice.AllDevices Any type of pointing device.
+
+ \sa QInputDevice::DeviceType
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedPointerTypes
+
+ The types of pointing instruments (generic, stylus, eraser, and so on)
+ that can activate the pointer handler.
+
+ By default, this property is set to
+ \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching events.
+
+ For example, you could provide feedback by changing the cursor depending on
+ whether a stylus or eraser is hovering over a graphics tablet:
+
+ \snippet pointerHandlers/hoverStylusOrEraser.qml 0
+
+ The available pointer types are as follows:
+
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen (hover detection is unlikely).
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+ \value PointerDevice.AllPointerTypes Any type of pointing device.
+
+ \sa QPointingDevice::PointerType
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedModifiers
+
+ If this property is set, a hover event is handled only if the given keyboard
+ modifiers are pressed. The event is ignored without the modifiers.
+
+ This property is set to \c Qt.KeyboardModifierMask by default, resulting
+ in handling hover events regardless of any modifier keys.
+
+ For example, an \l[QML]{Item} could have two handlers of the same type, one
+ of which is enabled only if the required keyboard modifiers are pressed:
+
+ \snippet pointerHandlers/hoverModifiers.qml 0
+
+ The available modifiers are as follows:
+
+ \value Qt.NoModifier No modifier key is allowed.
+ \value Qt.ShiftModifier A Shift key on the keyboard must be pressed.
+ \value Qt.ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value Qt.AltModifier An Alt key on the keyboard must be pressed.
+ \value Qt.MetaModifier A Meta key on the keyboard must be pressed.
+ \value Qt.KeypadModifier A keypad button must be pressed.
+ \value Qt.GroupSwitchModifier A Mode_switch key on the keyboard must be pressed.
+ X11 only (unless activated on Windows by a command line argument).
+ \value Qt.KeyboardModifierMask The handler ignores modifier keys.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*!
\since 5.15
\qmlproperty Qt::CursorShape QtQuick::HoverHandler::cursorShape
This property holds the cursor shape that will appear whenever
@@ -192,3 +294,5 @@ void QQuickHoverHandler::setHovered(bool hovered)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickhoverhandler_p.cpp"
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
index 313b87217c..426372d162 100644
--- a/src/quick/handlers/qquickhoverhandler_p.h
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -78,6 +78,7 @@ protected:
void componentComplete() override;
bool wantsPointerEvent(QQuickPointerEvent *event) override;
void handleEventPoint(QQuickEventPoint *point) override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
private:
void setHovered(bool hovered);
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index f404788de4..443cf4ffbc 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -146,7 +146,7 @@ void QQuickMultiPointHandler::onActiveChanged()
}
}
-void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
+void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
Q_D(QQuickMultiPointHandler);
// If another handler or item takes over this set of points, assume it has
@@ -155,6 +155,20 @@ void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventP
// (e.g. between DragHandler and PinchHandler).
if (transition == QQuickEventPoint::UngrabExclusive || transition == QQuickEventPoint::CancelGrabExclusive)
d->currentPoints.clear();
+ if (grabber != this)
+ return;
+ switch (transition) {
+ case QQuickEventPoint::GrabExclusive:
+ case QQuickEventPoint::GrabPassive:
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
+ break;
+ case QQuickEventPoint::OverrideGrabPassive:
+ return; // don't emit
+ }
}
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
@@ -422,3 +436,5 @@ QMetaProperty &QQuickMultiPointHandlerPrivate::yMetaProperty() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickmultipointhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 5b30d08557..16cf2c8100 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -85,7 +85,14 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
\image touchpoints-pinchhandler.png
- \sa PinchArea
+ \note The pinch begins when the number of fingers pressed is between
+ \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and
+ \l {MultiPointHandler::maximumPointCount}{maximumPointCount}, inclusive.
+ Until then, PinchHandler tracks the positions of any pressed fingers,
+ but if it's a disallowed number, it does not scale or rotate
+ its \l target, and the \l active property remains \c false.
+
+ \sa PinchArea, QPointerEvent::pointCount()
*/
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
@@ -249,19 +256,12 @@ bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
*/
/*!
- \qmlproperty int QtQuick::PinchHandler::minimumTouchPoints
-
- The pinch begins when this number of fingers are pressed.
- Until then, PinchHandler tracks the positions of any pressed fingers,
- but if it's an insufficient number, it does not scale or rotate
- its \l target, and the \l active property will remain false.
-*/
-
-/*!
\qmlproperty bool QtQuick::PinchHandler::active
- This property is true when all the constraints (epecially \l minimumTouchPoints)
- are satisfied and the \l target, if any, is being manipulated.
+ This property is \c true when all the constraints (epecially
+ \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and
+ \l {MultiPointHandler::maximumPointCount}{maximumPointCount}) are satisfied
+ and the \l target, if any, is being manipulated.
*/
void QQuickPinchHandler::onActiveChanged()
@@ -543,6 +543,11 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
The translation of the gesture \l centroid. It is \c (0, 0) when the
gesture begins.
+
+ \note On some touchpads, such as on a \macos trackpad, native gestures do
+ not generate any translation values, and this property stays at \c (0, 0).
*/
QT_END_NAMESPACE
+
+#include "moc_qquickpinchhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 90f31bf9fd..b7f0580648 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -314,3 +314,5 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
}
QT_END_NAMESPACE
+
+#include "moc_qquickpointerdevicehandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index adb753e000..67700e04fa 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -207,9 +207,11 @@ void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape)
return;
d->cursorShape = shape;
d->cursorSet = true;
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parentItem());
- itemPriv->hasCursorHandler = true;
- itemPriv->setHasCursorInChild(true);
+ if (auto *par = qmlobject_cast<QQuickItem *>(parent())) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(par);
+ itemPriv->hasCursorHandler = true;
+ itemPriv->setHasCursorInChild(true);
+ }
emit cursorShapeChanged();
}
@@ -220,9 +222,11 @@ void QQuickPointerHandler::resetCursorShape()
return;
d->cursorShape = Qt::ArrowCursor;
d->cursorSet = false;
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parentItem());
- itemPriv->hasCursorHandler = false;
- itemPriv->setHasCursorInChild(itemPriv->hasCursor);
+ if (auto *parent = parentItem()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
+ itemPriv->hasCursorHandler = false;
+ itemPriv->setHasCursorInChild(itemPriv->hasCursor);
+ }
emit cursorShapeChanged();
}
@@ -346,10 +350,16 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
existingPhGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
} else if ((d->grabPermissions & CanTakeOverFromItems)) {
+ allowed = true;
QQuickItem * existingItemGrabber = point->grabberItem();
- if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
- (existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent()))) {
- allowed = true;
+ QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(parentItem()->window());
+ const bool isMouse = point->pointerEvent()->asPointerMouseEvent();
+ const bool isTouch = point->pointerEvent()->asPointerTouchEvent();
+ if (existingItemGrabber &&
+ ((existingItemGrabber->keepMouseGrab() &&
+ (isMouse || winPriv->isDeliveringTouchAsMouse())) ||
+ (existingItemGrabber->keepTouchGrab() && isTouch))) {
+ allowed = false;
// If the handler wants to steal the exclusive grab from an Item, the Item can usually veto
// by having its keepMouseGrab flag set. But an exception is if that Item is a parent that
// normally filters events (such as a Flickable): it needs to be possible for e.g. a
@@ -358,14 +368,19 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
// at first and then expects to be able to steal the grab later on. It cannot respect
// Flickable's wishes in that case, because then it would never have a chance.
if (existingItemGrabber->keepMouseGrab() &&
- !(existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem()))) {
- QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(parentItem()->window());
+ existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem())) {
if (winPriv->isDeliveringTouchAsMouse() && point->pointId() == winPriv->touchMouseId) {
- qCDebug(lcPointerHandlerGrab) << this << "wants to grab touchpoint" << point->pointId()
- << "but declines to steal grab from touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber;
- allowed = false;
+ qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point->pointId()
+ << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber;
+ allowed = true;
}
}
+ if (!allowed) {
+ qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point->pointId()
+ << "but declines to steal from grabber" << existingItemGrabber
+ << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab()
+ << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab();
+ }
}
}
}
@@ -406,6 +421,8 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
This handler can take the exclusive grab from another handler of the same class.
\value PointerHandler.CanTakeOverFromHandlersOfDifferentType
This handler can take the exclusive grab from any kind of handler.
+ \value PointerHandler.CanTakeOverFromItems
+ This handler can take the exclusive grab from any type of Item.
\value PointerHandler.CanTakeOverFromAnything
This handler can take the exclusive grab from any type of Item or Handler.
\value PointerHandler.ApprovesTakeOverByHandlersOfSameType
@@ -446,6 +463,14 @@ void QQuickPointerHandler::classBegin()
void QQuickPointerHandler::componentComplete()
{
+ Q_D(const QQuickPointerHandler);
+ if (d->cursorSet) {
+ if (auto *parent = parentItem()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
+ itemPriv->hasCursorHandler = true;
+ itemPriv->setHasCursorInChild(true);
+ }
+ }
}
QQuickPointerEvent *QQuickPointerHandler::currentEvent()
@@ -503,8 +528,11 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
return false;
if (QQuickItem *par = parentItem()) {
if (par->window()) {
+ QRect windowGeometry = par->window()->geometry();
+ if (!par->window()->isTopLevel())
+ windowGeometry = QRect(QWindowPrivate::get(par->window())->globalPosition(), par->window()->size());
QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint());
- if (!par->window()->geometry().contains(screenPosition))
+ if (!windowGeometry.contains(screenPosition))
return false;
}
QPointF p = par->mapFromScene(point->scenePosition());
@@ -716,3 +744,5 @@ bool QQuickPointerHandlerPrivate::dragOverThreshold(const QQuickEventPoint *poin
}
QT_END_NAMESPACE
+
+#include "moc_qquickpointerhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index 30f62332ba..6b7b70f17a 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -163,3 +163,5 @@ QVector2D QQuickPointHandler::translation() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickpointhandler_p.cpp"
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index b51f53b74f..d785d8c0ca 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -218,3 +218,5 @@ void QQuickSinglePointHandlerPrivate::reset()
}
QT_END_NAMESPACE
+
+#include "moc_qquicksinglepointhandler_p.cpp"
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index f3674d6fa9..b722cf7538 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -209,6 +209,8 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
If the spatial constraint is violated, \l pressed transitions immediately
from true to false, regardless of the time held.
+ The \c gesturePolicy also affects grab behavior as described below.
+
\value TapHandler.DragThreshold
(the default value) The event point must not move significantly.
If the mouse, finger or stylus moves past the system-wide drag
@@ -217,11 +219,13 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
can be useful whenever TapHandler needs to cooperate with other
input handlers (for example \l DragHandler) or event-handling Items
(for example QtQuick Controls), because in this case TapHandler
- will not take the exclusive grab, but merely a passive grab.
+ will not take the exclusive grab, but merely a
+ \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
\value TapHandler.WithinBounds
If the event point leaves the bounds of the \c parent Item, the tap
- gesture is canceled. The TapHandler will take the exclusive grab on
+ gesture is canceled. The TapHandler will take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
press, but will release the grab as soon as the boundary constraint
is no longer satisfied.
@@ -232,8 +236,9 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
typical behavior for button widgets: you can cancel a click by
dragging outside the button, and you can also change your mind by
dragging back inside the button before release. Note that it's
- necessary for TapHandler take the exclusive grab on press and retain
- it until release in order to detect this gesture.
+ necessary for TapHandler to take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
+ and retain it until release in order to detect this gesture.
*/
void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
{
@@ -426,3 +431,5 @@ void QQuickTapHandler::updateTimeHeld()
from the previous \c tapCount.
*/
QT_END_NAMESPACE
+
+#include "moc_qquicktaphandler_p.cpp"
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
index 16f38af962..7e48d67b8b 100644
--- a/src/quick/handlers/qquickwheelhandler.cpp
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -527,3 +527,5 @@ QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickwheelhandler_p.cpp"
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 14fa07099f..28c62bd4f2 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -3892,16 +3892,16 @@ void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
}
-void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
+void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radius)
{
QPointF p0(m_path.currentPosition());
QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
- float p1p0_length = std::sqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
- float p1p2_length = std::sqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
+ qreal p1p0_length = std::hypot(p1p0.x(), p1p0.y());
+ qreal p1p2_length = std::hypot(p1p2.x(), p1p2.y());
- double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
+ qreal cos_phi = QPointF::dotProduct(p1p0, p1p2) / (p1p0_length * p1p2_length);
// The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
// We could have used areCollinear() here, but since we're reusing
@@ -3911,16 +3911,16 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu
return;
}
- float tangent = radius / std::tan(std::acos(cos_phi) / 2);
- float factor_p1p0 = tangent / p1p0_length;
+ qreal tangent = radius / std::tan(std::acos(cos_phi) / 2);
+ qreal factor_p1p0 = tangent / p1p0_length;
QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
QPointF orth_p1p0(p1p0.y(), -p1p0.x());
- float orth_p1p0_length = std::sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
- float factor_ra = radius / orth_p1p0_length;
+ qreal orth_p1p0_length = std::hypot(orth_p1p0.x(), orth_p1p0.y());
+ qreal factor_ra = radius / orth_p1p0_length;
// angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
- double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
+ qreal cos_alpha = QPointF::dotProduct(orth_p1p0, p1p2) / (orth_p1p0_length * p1p2_length);
if (cos_alpha < 0.f)
orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
@@ -3928,20 +3928,15 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu
// calculate angles for addArc
orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
- float sa = std::acos(orth_p1p0.x() / orth_p1p0_length);
- if (orth_p1p0.y() < 0.f)
- sa = 2 * M_PI - sa;
+ qreal sa = std::atan2(orth_p1p0.y(), orth_p1p0.x());
// anticlockwise logic
bool anticlockwise = false;
- float factor_p1p2 = tangent / p1p2_length;
+ qreal factor_p1p2 = tangent / p1p2_length;
QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
- float orth_p1p2_length = std::sqrt(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
- float ea = std::acos(orth_p1p2.x() / orth_p1p2_length);
- if (orth_p1p2.y() < 0)
- ea = 2 * M_PI - ea;
+ qreal ea = std::atan2(orth_p1p2.y(), orth_p1p2.x());
if ((sa > ea) && ((sa - ea) < M_PI))
anticlockwise = true;
if ((sa < ea) && ((ea - sa) > M_PI))
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index b5626dec0c..a00689f694 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -241,7 +241,7 @@ public:
void arc(qreal x, qreal y, qreal radius,
qreal startAngle, qreal endAngle,
bool anticlockwise);
- void addArcTo(const QPointF& p1, const QPointF& p2, float radius);
+ void addArcTo(const QPointF& p1, const QPointF& p2, qreal radius);
bool isPointInPath(qreal x, qreal y) const;
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 55ebbe907c..1d047e3c2f 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -375,7 +375,10 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
}
state.lineDash = pattern;
QPen nPen = p->pen();
- nPen.setDashPattern(pattern);
+ if (count > 0)
+ nPen.setDashPattern(pattern);
+ else
+ nPen.setStyle(Qt::SolidLine);
p->setPen(nPen);
break;
}
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index 0ebd1a66c9..3f28620d90 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -165,8 +165,15 @@ void QQuickContext2DTexture::setItem(QQuickCanvasItem* item)
bool QQuickContext2DTexture::setCanvasWindow(const QRect& r)
{
- qreal canvasDevicePixelRatio = (m_item && m_item->window()) ?
- m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio();
+ bool ok = false;
+ static qreal overriddenDevicePixelRatio =
+ !qEnvironmentVariableIsEmpty("QT_CANVAS_OVERRIDE_DEVICEPIXELRATIO") ?
+ qgetenv("QT_CANVAS_OVERRIDE_DEVICEPIXELRATIO").toFloat(&ok) : 0.0;
+ qreal canvasDevicePixelRatio = overriddenDevicePixelRatio;
+ if (overriddenDevicePixelRatio == 0.0) {
+ canvasDevicePixelRatio = (m_item && m_item->window()) ?
+ m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio();
+ }
if (!qFuzzyCompare(m_canvasDevicePixelRatio, canvasDevicePixelRatio)) {
qCDebug(lcCanvas, "%s device pixel ratio %.1lf -> %.1lf",
(m_item->objectName().isEmpty() ? "Canvas" : qPrintable(m_item->objectName())),
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 67d17c98e5..70dcaa2acf 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -406,9 +406,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role)
m_state.focusable = true;
break;
case QAccessible::StaticText:
- if (!m_stateExplicitlySet.readOnly) {
+ if (!m_stateExplicitlySet.readOnly)
m_state.readOnly = true;
- }
+ if (!m_stateExplicitlySet.focusable)
+ m_state.focusable = true;
break;
default:
break;
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index f239f24367..8818bc5608 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -894,7 +894,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
maybeUpdate();
}
- qreal frameCount = d->m_spriteEngine->spriteFrames();
+ int frameCount = d->m_spriteEngine->spriteFrames();
bool reverse = d->m_spriteEngine->sprite()->reverse();
if (reverse)
frameAt = (frameCount - 1) - frameAt;
@@ -931,15 +931,15 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
x2 = x1 - w;
y2 = y1;
} else {
- x2 = 1.0 - w;
+ x2 = d->m_sheetSize.width() - w;
y2 = y1 - h;
- if (y2 < 0.0) {
+ if (y2 < 0) {
//the last row may not fill the entire width
int maxRowFrames = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
if (d->m_spriteEngine->maxFrames() % maxRowFrames)
x2 = ((d->m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
- y2 = 1.0 - h;
+ y2 = d->m_sheetSize.height() - h;
}
}
} else {
@@ -947,10 +947,10 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
x2 = x1 + w;
y2 = y1;
} else {
- x2 = 0.0;
+ x2 = 0;
y2 = y1 + h;
- if (y2 >= 1.0)
- y2 = 0.0;
+ if (y2 >= d->m_sheetSize.height())
+ y2 = 0;
}
}
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index d90ee209d6..e503cd7815 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -91,6 +91,7 @@ QQuickDropAreaPrivate::~QQuickDropAreaPrivate()
/*!
\qmltype DropArea
\instantiates QQuickDropArea
+ \inherits Item
\inqmlmodule QtQuick
\ingroup qtquick-input
\brief For specifying drag and drop handling in an area.
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 950afaed52..ac962f6f61 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -712,6 +712,8 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event
switch (event->pointerType()) {
case QTabletEvent::Pen:
ptype = Pen;
+ if (type == QQuickPointerDevice::UnknownDevice)
+ type = QQuickPointerDevice::Stylus;
break;
case QTabletEvent::Eraser:
ptype = Eraser;
@@ -813,7 +815,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event
\readonly
\qmlproperty int QtQuick::EventPoint::pointId
- This property holds the ID of the event, if any.
+ This property holds the ID of the point, if any.
Touchpoints have automatically-incrementing IDs: each time the user
presses a finger against the touchscreen, it will be a larger number.
@@ -826,12 +828,8 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event
\readonly
\qmlproperty bool QtQuick::EventPoint::accepted
- Setting \a accepted to true prevents the event from being propagated to
- Items below the PointerHandler's Item.
-
- Generally, if the handler acts on the mouse event, then it should be
- accepted so that items lower in the stacking order do not also respond to
- the same event.
+ Indicates whether this point has been accepted during delivery thus far.
+ This flag cannot be usefully set from QML.
*/
/*!
@@ -2276,3 +2274,5 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *eve
#endif
QT_END_NAMESPACE
+
+#include "moc_qquickevents_p_p.cpp"
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 9a68be4c49..2634b68248 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -62,6 +62,8 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
+Q_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
+Q_LOGGING_CATEGORY(lcWheel, "qt.quick.flickable.wheel")
// FlickThreshold determines how far the "mouse" must have moved
// before we perform a flick.
@@ -263,7 +265,8 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, deceleration(QML_FLICK_DEFAULTDECELERATION)
, maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
, delayedPressEvent(nullptr), pressDelay(0), fixupDuration(400)
- , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(nullptr)
+ , flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
+ , fixupMode(Normal), vTime(0), visibleArea(nullptr)
, flickableDirection(QQuickFlickable::AutoFlickDirection)
, boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
, boundsMovement(QQuickFlickable::FollowBoundsBehavior)
@@ -323,7 +326,7 @@ void QQuickFlickablePrivate::AxisData::updateVelocity()
}
}
-void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
+void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeom)
{
Q_Q(QQuickFlickable);
if (item == contentItem) {
@@ -332,8 +335,14 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr
orient |= Qt::Horizontal;
if (change.yChange())
orient |= Qt::Vertical;
- if (orient)
+ if (orient) {
q->viewportMoved(orient);
+ const QPointF deltaMoved = item->position() - oldGeom.topLeft();
+ if (hData.contentPositionChangedExternallyDuringDrag)
+ hData.pressPos += deltaMoved.x();
+ if (vData.contentPositionChangedExternallyDuringDrag)
+ vData.pressPos += deltaMoved.y();
+ }
if (orient & Qt::Horizontal)
emit q->contentXChanged();
if (orient & Qt::Vertical)
@@ -531,10 +540,14 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != vData.atBeginning) {
vData.atBeginning = atBeginning;
atYBeginningChange = true;
+ if (!vData.moving && atBeginning)
+ vData.smoothVelocity.setValue(0);
}
if (atEnd != vData.atEnd) {
vData.atEnd = atEnd;
atYEndChange = true;
+ if (!vData.moving && atEnd)
+ vData.smoothVelocity.setValue(0);
}
// Horizontal
@@ -547,10 +560,14 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != hData.atBeginning) {
hData.atBeginning = atBeginning;
atXBeginningChange = true;
+ if (!hData.moving && atBeginning)
+ hData.smoothVelocity.setValue(0);
}
if (atEnd != hData.atEnd) {
hData.atEnd = atEnd;
atXEndChange = true;
+ if (!hData.moving && atEnd)
+ hData.smoothVelocity.setValue(0);
}
if (vData.extentsChanged) {
@@ -785,8 +802,11 @@ void QQuickFlickable::setContentX(qreal pos)
d->hData.vTime = d->timeline.time();
if (isMoving() || isFlicking())
movementEnding(true, false);
- if (!qFuzzyCompare(-pos, d->hData.move.value()))
+ if (!qFuzzyCompare(-pos, d->hData.move.value())) {
+ d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
d->hData.move.setValue(-pos);
+ d->hData.contentPositionChangedExternallyDuringDrag = false;
+ }
}
qreal QQuickFlickable::contentY() const
@@ -803,8 +823,11 @@ void QQuickFlickable::setContentY(qreal pos)
d->vData.vTime = d->timeline.time();
if (isMoving() || isFlicking())
movementEnding(false, true);
- if (!qFuzzyCompare(-pos, d->vData.move.value()))
+ if (!qFuzzyCompare(-pos, d->vData.move.value())) {
+ d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
d->vData.move.setValue(-pos);
+ d->vData.contentPositionChangedExternallyDuringDrag = false;
+ }
}
/*!
@@ -1489,6 +1512,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
d->hData.velocity = 0;
d->timer.start();
d->maybeBeginDrag(currentTimestamp, event->position());
+ d->lastPosTime = -1;
break;
case Qt::NoScrollPhase: // default phase with an ordinary wheel mouse
case Qt::ScrollUpdate:
@@ -1515,20 +1539,34 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
return;
}
+ qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / qreal(1000);
+ if (elapsed <= 0) {
+ d->lastPosTime = currentTimestamp;
+ qCDebug(lcWheel) << "insufficient elapsed time: can't calculate velocity" << elapsed;
+ return;
+ }
+
if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull()) {
- // physical mouse wheel, so use angleDelta
+ // no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
int xDelta = event->angleDelta().x();
int yDelta = event->angleDelta().y();
+ // For a single "clicky" wheel event (angleDelta +/- 120),
+ // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
+ // The decel algo from there is
+ // qreal dist = v2 / (accel * 2.0);
+ // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
+ // now solve for dt:
+ // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
+ if (!isMoving())
+ elapsed = 120 / qSqrt(d->deceleration * 2 * d->initialWheelFlickDistance);
if (yflick() && yDelta != 0) {
- bool valid = false;
- if (yDelta > 0 && contentY() > -minYExtent()) {
- d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
- valid = true;
- } else if (yDelta < 0 && contentY() < -maxYExtent()) {
- d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
- valid = true;
- }
- if (valid) {
+ qreal instVelocity = yDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
+ d->vData.velocityBuffer.clear();
+ d->vData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->vData.updateVelocity();
+ if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
d->flickY(d->vData.velocity);
d->flickingStarted(false, true);
if (d->vData.flicking) {
@@ -1539,15 +1577,13 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
}
}
if (xflick() && xDelta != 0) {
- bool valid = false;
- if (xDelta > 0 && contentX() > -minXExtent()) {
- d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
- valid = true;
- } else if (xDelta < 0 && contentX() < -maxXExtent()) {
- d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
- valid = true;
- }
- if (valid) {
+ qreal instVelocity = xDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
+ d->hData.velocityBuffer.clear();
+ d->hData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->hData.updateVelocity();
+ if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
d->flickX(d->hData.velocity);
d->flickingStarted(true, false);
if (d->hData.flicking) {
@@ -1562,18 +1598,13 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
int xDelta = event->pixelDelta().x();
int yDelta = event->pixelDelta().y();
- qreal elapsed = qreal(currentTimestamp - d->lastPosTime) / 1000.;
- if (elapsed <= 0) {
- d->lastPosTime = currentTimestamp;
- return;
- }
QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
- d->lastPosTime = currentTimestamp;
d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
true, !d->scrollingPhase, true, velocity);
event->accept();
}
+ d->lastPosTime = currentTimestamp;
if (!event->isAccepted())
QQuickItem::wheelEvent(event);
@@ -1744,6 +1775,10 @@ void QQuickFlickable::componentComplete()
setContentX(-minXExtent());
if (!d->vData.explicitValue && d->vData.startMargin != 0.)
setContentY(-minYExtent());
+ if (lcWheel().isDebugEnabled() || lcVel().isDebugEnabled()) {
+ d->timeline.setObjectName(QLatin1String("timeline for Flickable ") + objectName());
+ d->velocityTimeline.setObjectName(QLatin1String("velocity timeline for Flickable ") + objectName());
+ }
}
void QQuickFlickable::viewportMoved(Qt::Orientations orient)
@@ -2445,7 +2480,20 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickFlickable);
- if (!isVisible() || !isEnabled() || !isInteractive() || !d->wantsPointerEvent(e)) {
+ auto wantsPointerEvent_helper = [=]() {
+ bool wants = true;
+ if (e->type() >= QEvent::MouseButtonPress && e->type() <= QEvent::MouseMove) {
+ QMouseEvent *me = static_cast<QMouseEvent*>(e);
+ QPointF itemLocalPos = me->localPos();
+ me->setLocalPos(mapFromItem(i, itemLocalPos));
+ wants = d->wantsPointerEvent(e);
+ // re-localize event back to \a i before returning
+ me->setLocalPos(itemLocalPos);
+ }
+ return wants;
+ };
+
+ if (!isVisible() || !isEnabled() || !isInteractive() || !wantsPointerEvent_helper()) {
d->cancelInteraction();
return QQuickItem::childMouseEventFilter(i, e);
}
@@ -2491,9 +2539,23 @@ void QQuickFlickable::setMaximumFlickVelocity(qreal v)
/*!
\qmlproperty real QtQuick::Flickable::flickDeceleration
- This property holds the rate at which a flick will decelerate.
-
- The default value is platform dependent.
+ This property holds the rate at which a flick will decelerate:
+ the higher the number, the faster it slows down when the user stops
+ flicking via touch, touchpad or mouse wheel. For example 0.0001 is nearly
+ "frictionless", and 10000 feels quite "sticky".
+
+ The default value is platform dependent. Values of zero or less are not allowed.
+
+ \note For touchpad flicking, some platforms drive Flickable directly by
+ sending QWheelEvents with QWheelEvent::phase() being \c Qt::ScrollMomentum,
+ after the user has released all fingers from the touchpad. In that case,
+ the operating system is controlling the deceleration, and this property has
+ no effect.
+
+ \note For mouse wheel scrolling, and for gesture scrolling on touchpads
+ that do not have a momentum phase, extremely large values of
+ flickDeceleration can make Flickable very resistant to scrolling,
+ especially if \l maximumFlickVelocity is too small.
*/
qreal QQuickFlickable::flickDeceleration() const
{
@@ -2506,7 +2568,7 @@ void QQuickFlickable::setFlickDeceleration(qreal deceleration)
Q_D(QQuickFlickable);
if (deceleration == d->deceleration)
return;
- d->deceleration = deceleration;
+ d->deceleration = qMax(0.001, deceleration);
emit flickDecelerationChanged();
}
@@ -2716,6 +2778,12 @@ void QQuickFlickable::movementStarting()
if (!wasMoving && (d->hData.moving || d->vData.moving)) {
emit movingChanged();
emit movementStarted();
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ QAccessibleEvent ev(this, QAccessible::ScrollingStart);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
}
}
@@ -2760,6 +2828,12 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
if (wasMoving && !isMoving()) {
emit movingChanged();
emit movementEnded();
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ QAccessibleEvent ev(this, QAccessible::ScrollingEnd);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
}
if (hMovementEnding) {
@@ -2887,4 +2961,6 @@ void QQuickFlickable::setBoundsMovement(BoundsMovement movement)
QT_END_NAMESPACE
+#include "moc_qquickflickable_p_p.cpp"
+
#include "moc_qquickflickable_p.cpp"
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 414c9c33d6..aef15e150a 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -109,6 +109,7 @@ public:
, fixingUp(false), inOvershoot(false), inRebound(false), moving(false), flicking(false)
, dragging(false), extentsChanged(false)
, explicitValue(false), minExtentDirty(true), maxExtentDirty(true)
+ , contentPositionChangedExternallyDuringDrag(false)
, unused(0)
{}
@@ -119,6 +120,7 @@ public:
dragStartOffset = 0;
fixingUp = false;
inOvershoot = false;
+ contentPositionChangedExternallyDuringDrag = false;
}
void markExtentsDirty() {
@@ -169,7 +171,8 @@ public:
bool explicitValue : 1;
mutable bool minExtentDirty : 1;
mutable bool maxExtentDirty : 1;
- uint unused : 19;
+ bool contentPositionChangedExternallyDuringDrag : 1;
+ uint unused : 18;
};
bool flickX(qreal velocity);
@@ -241,6 +244,7 @@ public:
int pressDelay;
int fixupDuration;
qreal flickBoost;
+ qreal initialWheelFlickDistance;
enum FixupMode { Normal, Immediate, ExtentChanged };
FixupMode fixupMode;
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 7ca5b0c34c..0a1f7681d6 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1653,6 +1653,7 @@ void QQuickGridView::setCellWidth(qreal cellWidth)
d->updateViewport();
emit cellWidthChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
}
}
@@ -1670,6 +1671,7 @@ void QQuickGridView::setCellHeight(qreal cellHeight)
d->updateViewport();
emit cellHeightChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
}
}
/*!
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 1882ec8997..bf6e17a5d7 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -343,9 +343,9 @@ void QQuickImage::setFillMode(FillMode mode)
}
/*!
-
\qmlproperty real QtQuick::Image::paintedWidth
\qmlproperty real QtQuick::Image::paintedHeight
+ \readonly
These properties hold the size of the image that is actually painted.
In most cases it is the same as \c width and \c height, but when using an
@@ -367,6 +367,7 @@ qreal QQuickImage::paintedHeight() const
/*!
\qmlproperty enumeration QtQuick::Image::status
+ \readonly
This property holds the status of image loading. It can be one of:
\list
@@ -404,6 +405,7 @@ qreal QQuickImage::paintedHeight() const
/*!
\qmlproperty real QtQuick::Image::progress
+ \readonly
This property holds the progress of image loading, from 0.0 (nothing loaded)
to 1.0 (finished).
@@ -425,7 +427,7 @@ qreal QQuickImage::paintedHeight() const
*/
/*!
- \qmlproperty QSize QtQuick::Image::sourceSize
+ \qmlproperty size QtQuick::Image::sourceSize
This property holds the scaled width and height of the full-frame image.
@@ -943,3 +945,7 @@ void QQuickImage::setMipmap(bool use)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickimage_p_p.cpp"
+
+#include "moc_qquickimage_p.cpp"
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 8849c2005c..d7b5709bb0 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -56,8 +56,8 @@ QT_BEGIN_NAMESPACE
bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio)
{
// QQuickImageProvider and SVG and PDF can generate a high resolution image when
- // sourceSize is set (this function is only called if it's set).
- // If sourceSize is not set then the provider default size will be used, as usual.
+ // sourceSize is set. If sourceSize is not set then the provider default size will
+ // be used, as usual.
bool setDevicePixelRatio = false;
if (url.scheme() == QLatin1String("image")) {
setDevicePixelRatio = true;
@@ -418,10 +418,14 @@ void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value)
Q_D(QQuickImageBase);
// If the screen DPI changed, reload image.
if (change == ItemDevicePixelRatioHasChanged && value.realValue != d->devicePixelRatio) {
+ const auto oldDpr = d->devicePixelRatio;
// ### how can we get here with !qmlEngine(this)? that implies
// itemChange() on an item pending deletion, which seems strange.
if (qmlEngine(this) && isComponentComplete() && d->url.isValid()) {
load();
+ // not changed when loading (sourceSize might not be set)
+ if (d->devicePixelRatio == oldDpr)
+ d->updateDevicePixelRatio(value.realValue);
}
}
QQuickItem::itemChange(change, value);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index ddd67522b9..33da9762d3 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQuick module of the Qt Toolkit.
+** This file is part of the QtQuick module of the Qt Toolkit.fset
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -905,7 +905,7 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
*/
/*!
- \qmlproperty list<Object> QtQuick::Keys::forwardTo
+ \qmlproperty list<Item> QtQuick::Keys::forwardTo
This property provides a way to forward key presses, key releases, and keyboard input
coming from input methods to other items. This can be useful when you want
@@ -1883,7 +1883,23 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\endqml
- \section2 Key Handling
+ \section2 Event Handling
+
+ All Item-based visual types can use \l {Qt Quick Input Handlers}{Input Handlers}
+ to handle incoming input events (subclasses of QInputEvent), such as mouse,
+ touch and key events. This is the preferred declarative way to handle events.
+
+ An alternative way to handle touch events is to subclass QQuickItem, call
+ setAcceptTouchEvents() in the constructor, and override touchEvent().
+ \l {QEvent::setAccepted()}{Accept} the entire event to stop delivery to
+ items underneath, and to exclusively grab all the event's touch points.
+
+ Likewise, a QQuickItem subclass can call setAcceptedMouseButtons()
+ to register to receive mouse button events, setAcceptHoverEvents()
+ to receive hover events (mouse movements while no button is pressed),
+ and override the virtual functions mousePressEvent(), mouseMoveEvent(), and
+ mouseReleaseEvent(). Those can also accept the event to prevent further
+ delivery and get an implicit grab at the same time.
Key handling is available to all Item-based visual types via the \l Keys
attached property. The \e Keys attached property provides basic signals
@@ -2079,7 +2095,7 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
/*!
\class QQuickItem::ItemChangeData
\inmodule QtQuick
- \brief Adds supplimentary information to the QQuickItem::itemChange()
+ \brief Adds supplementary information to the QQuickItem::itemChange()
function.
The meaning of each member of this class is defined by the change type.
@@ -2109,25 +2125,31 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
/*!
\variable QQuickItem::ItemChangeData::realValue
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The numeric value that has changed: \l {QQuickItem::opacity()}{opacity},
+ \l {QQuickItem::rotation()}{rotation} or
+ \l {QScreen::devicePixelRatio}{device pixel ratio}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::boolValue
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The boolean value that has changed: \l {QQuickItem::visible()}{visible},
+ \l {QQuickItem::enabled()}{enabled}, \l {QQuickItem::activeFocus()}{activeFocus}
+ or \l {QQuickItem::antialiasing()}{antialiasing}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::item
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The item that has been added or removed as a \l{QQuickItem::childItems()}{child},
+ or the new \l{QQuickItem::parentItem()}{parent}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::window
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The \l{QQuickWindow}{window} in which the item has been shown, or \c nullptr
+ if the item has been removed from a window.
\sa QQuickItem::ItemChange
*/
@@ -3055,8 +3077,9 @@ Returns a transform that maps points from item space into window space.
*/
QTransform QQuickItemPrivate::itemToWindowTransform() const
{
- // XXX todo
- QTransform rv = parentItem?QQuickItemPrivate::get(parentItem)->itemToWindowTransform():QTransform();
+ // item's parent must not be itself, otherwise calling itemToWindowTransform() on it is infinite recursion
+ Q_ASSERT(!parentItem || QQuickItemPrivate::get(parentItem) != this);
+ QTransform rv = parentItem ? QQuickItemPrivate::get(parentItem)->itemToWindowTransform() : QTransform();
itemToParentTransform(rv);
return rv;
}
@@ -3650,6 +3673,10 @@ QQmlListProperty<QObject> QQuickItemPrivate::data()
This property is useful if you need to access the collective geometry
of an item's children in order to correctly size the item.
+
+ The geometry that is returned is local to the item. For example:
+
+ \snippet qml/item/childrenRect.qml local
*/
/*!
\property QQuickItem::childrenRect
@@ -3659,6 +3686,10 @@ QQmlListProperty<QObject> QQuickItemPrivate::data()
This property is useful if you need to access the collective geometry
of an item's children in order to correctly size the item.
+
+ The geometry that is returned is local to the item. For example:
+
+ \snippet qml/item/childrenRect.qml local
*/
QRectF QQuickItem::childrenRect()
{
@@ -3686,6 +3717,9 @@ QList<QQuickItem *> QQuickItem::childItems() const
If clipping is enabled, an item will clip its own painting, as well
as the painting of its children, to its bounding rectangle.
+
+ \note Clipping can affect rendering performance. See \l {Clipping} for more
+ information.
*/
/*!
\property QQuickItem::clip
@@ -4509,10 +4543,10 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
}
/*!
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in \a
item's coordinate system, to this item's coordinate system, and returns a \l point or \l rect
@@ -4564,10 +4598,10 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
}
/*!
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in this
item's coordinate system, to \a item's coordinate system, and returns a \l point or \l rect
@@ -4649,7 +4683,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapFromGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromGlobal(real x, real y)
Maps the point (\a x, \a y), which is in the global coordinate system, to the
item's coordinate system, and returns a \l point matching the mapped coordinate.
@@ -4676,7 +4710,7 @@ void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapToGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapToGlobal(real x, real y)
Maps the point (\a x, \a y), which is in this item's coordinate system, to the
global coordinate system, and returns a \l point matching the mapped coordinate.
@@ -4759,14 +4793,24 @@ void QQuickItem::forceActiveFocus()
void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
+ Q_D(QQuickItem);
setFocus(true, reason);
QQuickItem *parent = parentItem();
+ QQuickItem *scope = nullptr;
while (parent) {
if (parent->flags() & QQuickItem::ItemIsFocusScope) {
parent->setFocus(true, reason);
+ if (!scope)
+ scope = parent;
}
parent = parent->parentItem();
}
+ // In certain reparenting scenarios, d->focus might be true and the scope
+ // might also have focus, so that setFocus() returns early without actually
+ // acquiring active focus, because it thinks it already has it. In that
+ // case, try to set the DeliveryAgent's active focus. (QTBUG-89736).
+ if (scope && !d->activeFocus && d->window)
+ QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this, Qt::OtherFocusReason);
}
/*!
@@ -7301,7 +7345,9 @@ bool QQuickItem::isAncestorOf(const QQuickItem *child) const
If an item does not accept the mouse button for a particular mouse event,
the mouse event will not be delivered to the item and will be delivered
to the next item in the item hierarchy instead.
- */
+
+ \sa acceptTouchEvents()
+*/
Qt::MouseButtons QQuickItem::acceptedMouseButtons() const
{
Q_D(const QQuickItem);
@@ -7310,7 +7356,13 @@ Qt::MouseButtons QQuickItem::acceptedMouseButtons() const
/*!
Sets the mouse buttons accepted by this item to \a buttons.
- */
+
+ \note In Qt 5, calling setAcceptedMouseButtons() implicitly caused
+ an item to receive touch events as well as mouse events; but it was
+ recommended to call setAcceptTouchEvents() to subscribe for them.
+ In Qt 6, it is necessary to call setAcceptTouchEvents() to continue
+ to receive them.
+*/
void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
{
Q_D(QQuickItem);
@@ -7360,6 +7412,12 @@ bool QQuickItem::isUnderMouse() const
if (!d->window)
return false;
+ // QQuickWindow handles QEvent::Leave to reset the lastMousePosition
+ // FIXME: Using QPointF() as the reset value means an item will not be
+ // under the mouse if the mouse is at 0,0 of the window.
+ if (QQuickWindowPrivate::get(d->window)->lastMousePosition == QPointF())
+ return false;
+
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
return contains(mapFromScene(d->window->mapFromGlobal(cursorPos.toPoint())));
}
@@ -7774,22 +7832,23 @@ void QQuickItem::setKeepTouchGrab(bool keep)
}
/*!
- \qmlmethod bool QtQuick::Item::contains(point point)
+ \qmlmethod bool QtQuick::Item::contains(point point)
- Returns true if this item contains \a point, which is in local coordinates;
- returns false otherwise.
- */
+ Returns \c true if this item contains \a point, which is in local coordinates;
+ returns \c false otherwise. This is the same check that is used for
+ hit-testing a QEventPoint during event delivery, and is affected by
+ containmentMask() if it is set.
+*/
/*!
- Returns true if this item contains \a point, which is in local coordinates;
- returns false otherwise.
+ Returns \c true if this item contains \a point, which is in local coordinates;
+ returns \c false otherwise.
- This function can be overwritten in order to handle point collisions in items
- with custom shapes. The default implementation checks if the point is inside
- the item's bounding rect.
+ This function can be overridden in order to handle point collisions in items
+ with custom shapes. The default implementation checks whether the point is inside
+ containmentMask() if it is set, or inside the bounding box otherwise.
- Note that this method is generally used to check whether the item is under the mouse cursor,
- and for that reason, the implementation of this function should be as light-weight
- as possible.
+ \note This method is used for hit-testing each QEventPoint during event
+ delivery, so the implementation should be kept as lightweight as possible.
*/
bool QQuickItem::contains(const QPointF &point) const
{
@@ -7812,22 +7871,64 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains method.
- QtQuick::Item::contains main use is currently to determine whether
- an input event has landed into the item or not.
+ QtQuick::Item::contains() method. Its main use is currently to determine
+ whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
By default the \l contains method will return true for any point
- within the Item's bounding box. \c containmentMask allows for a
- more fine-grained control. For example, the developer could
- define and use an AnotherItem element as containmentMask,
- which has a specialized contains method, like:
+ within the Item's bounding box. \c containmentMask allows for
+ more fine-grained control. For example, if a custom C++
+ QQuickItem subclass with a specialized contains() method
+ is used as containmentMask:
\code
Item { id: item; containmentMask: AnotherItem { id: anotherItem } }
\endcode
- \e{item}'s contains method would then return true only if
- \e{anotherItem}'s contains implementation returns true.
+ \e{item}'s contains method would then return \c true only if
+ \e{anotherItem}'s contains() implementation returns \c true.
+
+ A \l Shape can be used as a mask, to make an item react to
+ \l {QPointerEvent}{pointer events} only within a non-rectangular region:
+
+ \table
+ \row
+ \li \image containmentMask-shape.gif
+ \li \snippet qml/item/containmentMask-shape.qml 0
+ \endtable
+
+ It is also possible to define the contains method in QML. For example,
+ to create a circular item that only responds to events within its
+ actual bounds:
+
+ \table
+ \row
+ \li \image containmentMask-circle.gif
+ \li \snippet qml/item/containmentMask-circle-js.qml 0
+ \endtable
+
+ \sa {Qt Quick Examples - Shapes}
+*/
+/*!
+ \property QQuickItem::containmentMask
+ \since 5.11
+ This property holds an optional mask to be used in the contains() method,
+ which is mainly used for hit-testing each \l QPointerEvent.
+
+ By default, \l contains() will return \c true for any point
+ within the Item's bounding box. But any QQuickItem, or any QObject
+ that implements a function of the form
+ \code
+ Q_INVOKABLE bool contains(const QPointF &point) const;
+ \endcode
+ can be used as a mask, to defer hit-testing to that object.
+
+ \note contains() is called frequently during event delivery.
+ Deferring hit-testing to another object slows it down somewhat.
+ containmentMask() can cause performance problems if that object's
+ contains() method is not efficient. If you implement a custom
+ QQuickItem subclass, you can alternatively override contains().
+
+ \sa contains()
*/
QObject *QQuickItem::containmentMask() const
{
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 23694e2de3..63359a7683 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -230,8 +230,8 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
{
Q_D(QQuickParentAnimation);
- QQuickParentAnimationData *data = new QQuickParentAnimationData;
- QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
+ std::unique_ptr<QQuickParentAnimationData> data(new QQuickParentAnimationData);
+ std::unique_ptr<QQuickParentAnimationData> viaData(new QQuickParentAnimationData);
bool hasExplicit = false;
if (d->target && d->newParent) {
@@ -377,8 +377,8 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
if (d->via)
- viaAction->setAnimAction(viaData);
- targetAction->setAnimAction(data);
+ viaAction->setAnimAction(viaData.release());
+ targetAction->setAnimAction(data.release());
//take care of any child animations
bool valid = d->defaultProperty.isValid();
@@ -405,9 +405,6 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
}
return initInstance(topLevelGroup);
- } else {
- delete data;
- delete viaData;
}
return nullptr;
}
@@ -532,8 +529,8 @@ QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &act
data->interpolatorType = QMetaType::QReal;
data->interpolator = d->interpolator;
data->reverse = direction == Backward ? true : false;
- data->fromSourced = false;
- data->fromDefined = false;
+ data->fromIsSourced = false;
+ data->fromIsDefined = false;
for (int ii = 0; ii < actions.count(); ++ii) {
QQuickStateAction &action = actions[ii];
@@ -546,7 +543,7 @@ QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &act
QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
if (data->actions.count()) {
animator->setAnimValue(data);
- animator->setFromSourcedValue(&data->fromSourced);
+ animator->setFromIsSourcedValue(&data->fromIsSourced);
} else {
delete data;
}
@@ -867,9 +864,9 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio
data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
data->endRotation = d->endRotation;
data->reverse = direction == Backward ? true : false;
- data->fromSourced = false;
- data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
- data->toDefined = d->path ? true : false;
+ data->fromIsSourced = false;
+ data->fromIsDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
+ data->toIsDefined = d->path ? true : false;
int origModifiedSize = modified.count();
for (int i = 0; i < actions.count(); ++i) {
@@ -888,8 +885,7 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio
}
}
- if (target && d->path &&
- (modified.count() > origModifiedSize || data->toDefined)) {
+ if (target && d->path && (modified.count() > origModifiedSize || data->toIsDefined)) {
data->target = target;
data->path = d->path;
data->path->invalidateSequentialHistory();
@@ -900,13 +896,13 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio
// treat interruptions specially, otherwise we end up with strange paths
if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
- if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
+ if (!data->fromIsDefined && !data->toIsDefined && !prevData.painterPath.isEmpty()) {
QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
if (!prevData.anchorPoint.isNull())
pathPos -= prevData.anchorPoint;
if (pathPos == data->target->position()) { //only treat as interruption if we interrupted ourself
data->painterPath = prevData.painterPath;
- data->toDefined = data->fromDefined = data->fromSourced = true;
+ data->toIsDefined = data->fromIsDefined = data->fromIsSourced = true;
data->prevBez.isValid = false;
data->interruptStart = prevData.currentV;
data->startRotation = prevData.startRotation;
@@ -916,13 +912,13 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio
}
}
}
- pa->setFromSourcedValue(&data->fromSourced);
+ pa->setFromIsSourcedValue(&data->fromIsSourced);
pa->setAnimValue(data);
pa->setDuration(d->duration);
pa->setEasingCurve(d->easingCurve);
return initInstance(pa);
} else {
- pa->setFromSourcedValue(nullptr);
+ pa->setFromIsSourcedValue(nullptr);
pa->setAnimValue(nullptr);
delete pa;
delete data;
@@ -942,7 +938,7 @@ void QQuickPathAnimationUpdater::setValue(qreal v)
}
currentV = v;
bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
- if (!fromSourced && (!fromDefined || !toDefined)) {
+ if (!fromIsSourced && (!fromIsDefined || !toIsDefined)) {
qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
@@ -950,7 +946,7 @@ void QQuickPathAnimationUpdater::setValue(qreal v)
prevBez.isValid = false;
painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
- fromSourced = true;
+ fromIsSourced = true;
}
qreal angle;
diff --git a/src/quick/items/qquickitemanimation_p_p.h b/src/quick/items/qquickitemanimation_p_p.h
index 83b9899197..351a0cb2bc 100644
--- a/src/quick/items/qquickitemanimation_p_p.h
+++ b/src/quick/items/qquickitemanimation_p_p.h
@@ -92,7 +92,7 @@ class QQuickPathAnimationUpdater : public QQuickBulkValueUpdater
{
public:
QQuickPathAnimationUpdater() : path(nullptr), pathLength(0), target(nullptr), reverse(false),
- fromSourced(false), fromDefined(false), toDefined(false),
+ fromIsSourced(false), fromIsDefined(false), toIsDefined(false),
toX(0), toY(0), currentV(0), orientation(QQuickPathAnimation::Fixed),
entryInterval(0), exitInterval(0) {}
~QQuickPathAnimationUpdater() {}
@@ -108,9 +108,9 @@ public:
QQuickItem *target;
bool reverse;
- bool fromSourced;
- bool fromDefined;
- bool toDefined;
+ bool fromIsSourced;
+ bool fromIsDefined;
+ bool toIsDefined;
qreal toX;
qreal toY;
qreal currentV;
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 125518e51b..f14b29b1b8 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -128,6 +128,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
QT_END_NAMESPACE
+#include "moc_qquickitemsmodule_p.cpp"
+
static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent)
{
// When setting a parent (especially during dynamic object creation) in QML,
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 2b4ca9e256..29afaae93b 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -290,8 +290,6 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate)
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
int oldCount = dataModel->count();
dataModel->setDelegate(delegate);
- if (isComponentComplete())
- d->applyDelegateChange();
if (oldCount != dataModel->count())
emit countChanged();
}
@@ -1785,7 +1783,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
do {
bufferPause.stop();
- if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()) {
+ if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
currentChanges.reset();
bufferedChanges.reset();
releaseVisibleItems(reusableFlag);
@@ -2346,7 +2344,9 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
inRequest = true;
- QObject* object = model->object(modelIndex, incubationMode);
+ // The model will run this same range check internally but produce a warning and return nullptr.
+ // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves.
+ QObject* object = modelIndex < model->count() ? model->object(modelIndex, incubationMode) : nullptr;
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
if (!item) {
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index b7649c9952..78b379d5a2 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -125,6 +125,8 @@ void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableIt
actions << QQuickStateAction(item->item, QLatin1String("x"), QVariant(to.x()));
actions << QQuickStateAction(item->item, QLatin1String("y"), QVariant(to.y()));
+ actions[0].fromValue = item->itemX();
+ actions[1].fromValue = item->itemY();
m_transitioner->runningJobs << this;
QQuickTransitionManager::transition(actions, trans, item->item);
}
@@ -555,9 +557,15 @@ void QQuickItemViewTransitionableItem::stopTransition()
QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
- : QObject(parent), m_item(nullptr), m_index(-1)
+ : QObject(parent), m_index(-1)
{
}
+
+QQuickItem *QQuickViewTransitionAttached::item() const
+{
+ return m_item.data();
+}
+
/*!
\qmltype ViewTransition
\instantiates QQuickViewTransitionAttached
diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h
index 5f4e74171e..bedef93d0b 100644
--- a/src/quick/items/qquickitemviewtransition_p.h
+++ b/src/quick/items/qquickitemviewtransition_p.h
@@ -202,7 +202,7 @@ public:
QQuickViewTransitionAttached(QObject *parent);
int index() const { return m_index; }
- QQuickItem *item() const { return m_item; }
+ QQuickItem *item() const;
QPointF destination() const { return m_destination; }
QList<int> targetIndexes() const { return m_targetIndexes; }
@@ -224,7 +224,7 @@ private:
QList<int> m_targetIndexes;
QList<QObject *> m_targetItems;
- QQuickItem *m_item;
+ QPointer<QQuickItem> m_item;
int m_index;
};
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 0f9394f695..1001b34c3c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -382,6 +382,83 @@ private:
}
};
+/*! \internal
+ \brief A helper class for iterating over a model that might change
+
+ When populating the ListView from a model under normal
+ circumstances, we would iterate over the range of model indices
+ correspondning to the visual range, and basically call
+ createItem(index++) in order to create each item.
+
+ This will also emit Component.onCompleted() for each item, which
+ might do some weird things... For instance, it might remove itself
+ from the model, and this might change model count and the indices
+ of the other subsequent entries in the model.
+
+ This class takes such changes to the model into consideration while
+ iterating, and will adjust the iterator index and keep track of
+ whether the iterator has reached the end of the range.
+
+ It keeps track of changes to the model by connecting to
+ QQmlInstanceModel::modelUpdated() from its constructor.
+ When destroyed, it will automatically disconnect. You can
+ explicitly disconnect earlier by calling \fn disconnect().
+*/
+class MutableModelIterator {
+public:
+ MutableModelIterator(QQmlInstanceModel *model, int iBegin, int iEnd)
+ : removedAtIndex(false)
+ , backwards(iEnd < iBegin)
+ {
+ conn = QObject::connect(model, &QQmlInstanceModel::modelUpdated,
+ [&] (const QQmlChangeSet &changeSet, bool /*reset*/)
+ {
+ for (const QQmlChangeSet::Change &rem : changeSet.removes()) {
+ idxEnd -= rem.count;
+ if (rem.start() <= index) {
+ index -= rem.count;
+ if (index < rem.start() + rem.count)
+ removedAtIndex = true; // model index was removed
+ }
+ }
+ for (const QQmlChangeSet::Change &ins : changeSet.inserts()) {
+ idxEnd += ins.count;
+ if (ins.start() <= index)
+ index += ins.count;
+ }
+ }
+ );
+ index = iBegin;
+ idxEnd = iEnd;
+ }
+
+ bool hasNext() const {
+ return backwards ? index > idxEnd : index < idxEnd;
+ }
+
+ void next() { index += (backwards ? -1 : +1); }
+
+ ~MutableModelIterator()
+ {
+ disconnect();
+ }
+
+ void disconnect()
+ {
+ if (conn) {
+ QObject::disconnect(conn);
+ conn = QMetaObject::Connection(); // set to nullptr
+ }
+ }
+ int index = 0;
+ int idxEnd;
+ unsigned removedAtIndex : 1;
+ unsigned backwards : 1;
+private:
+ QMetaObject::Connection conn;
+};
+
+
//----------------------------------------------------------------------------
bool QQuickListViewPrivate::isContentFlowReversed() const
@@ -3570,7 +3647,6 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
// Insert items before the visible item.
int insertionIdx = index;
- int i = 0;
qreal from = tempPos - displayMarginBeginning - buffer;
if (insertionIdx < visibleIndex) {
@@ -3579,15 +3655,18 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->sizeChangesBeforeVisiblePos += count * (averageSize + spacing);
}
} else {
- for (i = count-1; i >= 0 && pos >= from; --i) {
+ MutableModelIterator it(model, modelIndex + count - 1, modelIndex -1);
+ for (; it.hasNext() && pos >= from; it.next()) {
// item is before first visible e.g. in cache buffer
FxViewItem *item = nullptr;
- if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
- item->index = modelIndex + i;
+ if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
+ item->index = it.index;
if (!item)
- item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
+ item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
+ if (it.removedAtIndex)
+ continue;
visibleAffected = true;
visibleItems.insert(insertionIdx, item);
@@ -3620,16 +3699,20 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
}
} else {
- for (int i = 0; i < count && pos <= lastVisiblePos; ++i) {
+ MutableModelIterator it(model, modelIndex, modelIndex + count);
+ for (; it.hasNext() && pos <= lastVisiblePos; it.next()) {
visibleAffected = true;
FxViewItem *item = nullptr;
- if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
- item->index = modelIndex + i;
+ if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
+ item->index = it.index;
bool newItem = !item;
+ it.removedAtIndex = false;
if (!item)
- item = createItem(modelIndex + i, QQmlIncubator::Synchronous);
+ item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
+ if (it.removedAtIndex)
+ continue;
visibleItems.insert(index, item);
if (index == 0)
@@ -3650,6 +3733,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
pos += item->size() + spacing;
++index;
}
+ it.disconnect();
if (0 < index && index < visibleItems.count()) {
FxViewItem *prevItem = visibleItems.at(index - 1);
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 8cd63a4236..0a5514d5f9 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -439,9 +439,8 @@ void QQuickLoader::loadFromSource()
}
if (isComponentComplete()) {
- QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
+ d->createComponent();
d->load();
}
}
@@ -718,7 +717,6 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
emit q->progressChanged();
if (status == QQmlIncubator::Ready)
emit q->loaded();
- disposeInitialPropertyValues(); // cleanup
}
void QQuickLoaderPrivate::_q_sourceLoaded()
@@ -738,6 +736,9 @@ void QQuickLoaderPrivate::_q_sourceLoaded()
return;
}
+ if (!active)
+ return;
+
QQmlContext *creationContext = component->creationContext();
if (!creationContext) creationContext = qmlContext(q);
itemContext = new QQmlContext(creationContext);
@@ -804,11 +805,8 @@ void QQuickLoader::componentComplete()
Q_D(QQuickLoader);
QQuickItem::componentComplete();
if (active()) {
- if (d->loadingFromSource) {
- QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
- if (!d->component)
- d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
- }
+ if (d->loadingFromSource && !d->component)
+ d->createComponent();
d->load();
}
}
@@ -944,7 +942,7 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
}
/*!
- \qmlproperty object QtQuick::Loader::item
+ \qmlproperty QtObject QtQuick::Loader::item
This property holds the top-level object that is currently loaded.
Since \c {QtQuick 2.0}, Loader can load any object type.
@@ -1042,6 +1040,22 @@ void QQuickLoaderPrivate::updateStatus()
}
}
+void QQuickLoaderPrivate::createComponent()
+{
+ Q_Q(QQuickLoader);
+ const QQmlComponent::CompilationMode mode = asynchronous
+ ? QQmlComponent::Asynchronous
+ : QQmlComponent::PreferSynchronous;
+ if (QQmlContext *context = qmlContext(q)) {
+ if (QQmlEngine *engine = context->engine()) {
+ component.setObject(new QQmlComponent(engine, source, mode, q), q);
+ return;
+ }
+ }
+
+ qmlWarning(q) << "createComponent: Cannot find a QML engine.";
+}
+
#include <moc_qquickloader_p.cpp>
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 39d50280c5..b178803c7d 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -98,6 +98,7 @@ public:
QV4::ReturnedValue extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error);
QQuickLoader::Status computeStatus() const;
void updateStatus();
+ void createComponent();
qreal getImplicitWidth() const override;
qreal getImplicitHeight() const override;
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 982d089b5f..3e2e4fc1cf 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -806,7 +806,8 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event)
QQuickWindow *w = window();
if (w && w->mouseGrabberItem() == this)
ungrabMouse();
- setKeepMouseGrab(false);
+ if (!d->preventStealing)
+ setKeepMouseGrab(false);
}
}
d->doubleClick = false;
@@ -825,7 +826,8 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
emit this->doubleClicked(&me);
if (!me.isAccepted())
d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
- d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
+ if (d->pressed)
+ d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
}
QQuickItem::mouseDoubleClickEvent(event);
}
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 383718c979..4d420969cf 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -569,7 +569,7 @@ void QQuickMultiPointTouchArea::grabGesture()
setKeepTouchGrab(true);
}
-void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
+void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints remap)
{
bool ended = false;
bool moved = false;
@@ -578,12 +578,14 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
clearTouchLists();
QList<QTouchEvent::TouchPoint> touchPoints;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window());
+ bool touchPointsFromEvent = false;
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
touchPoints = static_cast<QTouchEvent*>(event)->touchPoints();
+ touchPointsFromEvent = true;
break;
case QEvent::MouseButtonPress:
_mouseQpaTouchPoint = QTouchEvent::TouchPoint(windowPriv->touchMouseId);
@@ -614,6 +616,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
qWarning("updateTouchData: unhandled event type %d", event->type());
break;
}
+ if (!touchPointsFromEvent)
+ remap = RemapEventPoints::No;
int numTouchPoints = touchPoints.count();
//always remove released touches, and make sure we handle all releases before adds.
@@ -624,7 +628,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
if (!dtp)
continue;
- updateTouchPoint(dtp, &p);
+ updateTouchPoint(dtp, &p, RemapEventPoints::No);
dtp->setPressed(false);
_releasedTouchPoints.append(dtp);
_touchPoints.remove(id);
@@ -639,19 +643,19 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
//handled above
} else if (!_touchPoints.contains(id)) { //could be pressed, moved, or stationary
// (we may have just obtained enough points to start tracking them -- in that case moved or stationary count as newly pressed)
- addTouchPoint(&p);
+ addTouchPoint(&p, remap);
started = true;
} else if ((touchPointState & Qt::TouchPointMoved) || p.d->stationaryWithModifiedProperty) {
// React to a stationary point with a property change (velocity, pressure) as if the point moved. (QTBUG-77142)
QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
_movedTouchPoints.append(dtp);
- updateTouchPoint(dtp,&p);
+ updateTouchPoint(dtp, &p, remap);
moved = true;
} else {
QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
- updateTouchPoint(dtp,&p);
+ updateTouchPoint(dtp, &p, remap);
}
}
@@ -685,7 +689,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
emit released(_releasedTouchPoints);
if (moved)
emit updated(_movedTouchPoints);
- if (started)
+ if (started && !_pressedTouchPoints.isEmpty())
emit pressed(_pressedTouchPoints);
if (ended || moved || started) emit touchUpdated(_touchPoints.values());
}
@@ -707,7 +711,7 @@ void QQuickMultiPointTouchArea::clearTouchLists()
_movedTouchPoints.clear();
}
-void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
+void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p, RemapEventPoints remap)
{
QQuickTouchPoint *dtp = nullptr;
for (QQuickTouchPoint* tp : qAsConst(_touchPrototypes)) {
@@ -721,7 +725,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
if (dtp == nullptr)
dtp = new QQuickTouchPoint(false);
dtp->setPointId(p->id());
- updateTouchPoint(dtp,p);
+ updateTouchPoint(dtp, p, remap);
dtp->setPressed(true);
_touchPoints.insert(p->id(),dtp);
_pressedTouchPoints.append(dtp);
@@ -730,12 +734,15 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
{
QQuickTouchPoint *dtp = nullptr;
- for (QQuickTouchPoint *tp : qAsConst(_touchPrototypes))
+ for (QQuickTouchPoint *tp : qAsConst(_touchPrototypes)) {
if (!tp->inUse()) {
tp->setInUse(true);
dtp = tp;
break;
+ } else if (_mouseTouchPoint == tp) {
+ return; // do not allow more than one touchpoint to react to the mouse (QTBUG-83662)
}
+ }
if (dtp == nullptr)
dtp = new QQuickTouchPoint(false);
@@ -779,12 +786,12 @@ void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype)
_touchPrototypes.insert(id, prototype);
}
-void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p)
+void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p, RemapEventPoints remap)
{
//TODO: if !qmlDefined, could bypass setters.
// also, should only emit signals after all values have been set
dtp->setUniqueId(p->uniqueId());
- dtp->setPosition(p->pos());
+ dtp->setPosition(remap == RemapEventPoints::ToLocal ? mapFromScene(p->scenePos()) : p->pos());
dtp->setEllipseDiameters(p->ellipseDiameters());
dtp->setPressure(p->pressure());
dtp->setRotation(p->rotation());
@@ -972,12 +979,12 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve
}
if (!shouldFilter(event))
return false;
- updateTouchData(event);
+ updateTouchData(event, RemapEventPoints::ToLocal);
return _stealMouse;
case QEvent::TouchEnd: {
if (!shouldFilter(event))
return false;
- updateTouchData(event);
+ updateTouchData(event, RemapEventPoints::ToLocal);
ungrab(true);
}
break;
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index e34a3faad6..85c1e2258e 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -267,14 +267,15 @@ protected:
void mouseUngrabEvent() override;
void touchUngrabEvent() override;
+ enum class RemapEventPoints { No, ToLocal };
void addTouchPrototype(QQuickTouchPoint* prototype);
- void addTouchPoint(const QTouchEvent::TouchPoint *p);
+ void addTouchPoint(const QTouchEvent::TouchPoint *p, RemapEventPoints remap);
void addTouchPoint(const QMouseEvent *e);
void clearTouchLists();
- void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*);
+ void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*, RemapEventPoints remap);
void updateTouchPoint(QQuickTouchPoint *dtp, const QMouseEvent *e);
- void updateTouchData(QEvent*);
+ void updateTouchData(QEvent*, RemapEventPoints remap = RemapEventPoints::No);
bool sendMouseEvent(QMouseEvent *event);
bool shouldFilter(QEvent *event);
diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index 5721f116e8..bc50099903 100644
--- a/src/quick/items/qquickopenglshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -120,10 +120,13 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
: QQuickShaderEffect::Error);
}
+ if (newEffect != oldEffect)
+ m_initialized = false;
+
int textureProviderIndex = 0;
if (!m_initialized) {
for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
- Q_ASSERT(m_uniformLocs[shaderType].isEmpty());
+ m_uniformLocs[shaderType].clear();
m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size());
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index 0692a1da42..df6f271b0d 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -556,7 +556,10 @@ void QQuickPinchArea::updatePinch()
d->inPinch = true;
d->stealMouse = true;
if (d->pinch && d->pinch->target()) {
- d->pinchStartPos = pinch()->target()->position();
+ auto targetParent = pinch()->target()->parentItem();
+ d->pinchStartPos = targetParent ?
+ targetParent->mapToScene(pinch()->target()->position()) :
+ pinch()->target()->position();
d->pinchStartScale = d->pinch->target()->scale();
d->pinchStartRotation = d->pinch->target()->rotation();
d->pinch->setActive(true);
@@ -604,6 +607,9 @@ void QQuickPinchArea::updatePinchTarget()
s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
pinch()->target()->setScale(s);
QPointF pos = d->sceneLastCenter - d->sceneStartCenter + d->pinchStartPos;
+ if (auto targetParent = pinch()->target()->parentItem())
+ pos = targetParent->mapFromScene(pos);
+
if (pinch()->axis() & QQuickPinch::XAxis) {
qreal x = pos.x();
if (x < pinch()->xmin())
@@ -704,6 +710,8 @@ bool QQuickPinchArea::event(QEvent *event)
clearPinch();
break;
case Qt::ZoomNativeGesture: {
+ if (d->pinchRejected)
+ break;
qreal scale = d->pinchLastScale * (1.0 + gesture->value());
QQuickPinchEvent pe(d->pinchStartCenter, scale, d->pinchLastAngle, 0.0);
pe.setStartCenter(d->pinchStartCenter);
@@ -721,7 +729,10 @@ bool QQuickPinchArea::event(QEvent *event)
else
emit pinchStarted(&pe);
d->inPinch = true;
- updatePinchTarget();
+ if (pe.accepted())
+ updatePinchTarget();
+ else
+ d->pinchRejected = true;
} break;
case Qt::SmartZoomNativeGesture: {
if (gesture->value() > 0.0 && d->pinch && d->pinch->target()) {
@@ -745,6 +756,8 @@ bool QQuickPinchArea::event(QEvent *event)
emit smartZoom(&pe);
} break;
case Qt::RotateNativeGesture: {
+ if (d->pinchRejected)
+ break;
qreal angle = d->pinchLastAngle + gesture->value();
QQuickPinchEvent pe(d->pinchStartCenter, d->pinchLastScale, angle, 0.0);
pe.setStartCenter(d->pinchStartCenter);
@@ -763,7 +776,10 @@ bool QQuickPinchArea::event(QEvent *event)
emit pinchStarted(&pe);
d->inPinch = true;
d->pinchRotation = angle;
- updatePinchTarget();
+ if (pe.accepted())
+ updatePinchTarget();
+ else
+ d->pinchRejected = true;
} break;
default:
return QQuickItem::event(event);
diff --git a/src/quick/items/qquickscalegrid.cpp b/src/quick/items/qquickscalegrid.cpp
index 23f179be1d..82875c9e52 100644
--- a/src/quick/items/qquickscalegrid.cpp
+++ b/src/quick/items/qquickscalegrid.cpp
@@ -217,3 +217,5 @@ QString QQuickGridScaledImage::pixmapUrl() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickscalegrid_p_p.cpp"
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 4f61d61309..b298ed74da 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -344,7 +344,6 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
d->refFromEffectItem(m_hideSource);
d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
- connect(m_sourceItem, SIGNAL(parentChanged(QQuickItem*)), this, SLOT(sourceItemParentChanged(QQuickItem*)));
} else {
qWarning("ShaderEffectSource: sourceItem and ShaderEffectSource must both be children of the same window.");
m_sourceItem = nullptr;
@@ -364,13 +363,6 @@ void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
}
-void QQuickShaderEffectSource::sourceItemParentChanged(QQuickItem *parent)
-{
- if (!parent && m_texture)
- m_texture->setItem(0);
-}
-
-
/*!
\qmlproperty rect QtQuick::ShaderEffectSource::sourceRect
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 4deb6c70a3..c0a1ccab78 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -173,7 +173,6 @@ Q_SIGNALS:
private Q_SLOTS:
void sourceItemDestroyed(QObject *item);
void invalidateSceneGraph();
- void sourceItemParentChanged(QQuickItem *parent);
protected:
void releaseResources() override;
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index ddaa1979b6..a832f53e39 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -573,7 +573,7 @@ void QQuickParentChange::rewind()
The AnchorChanges type is used to modify the anchors of an item in a \l State.
AnchorChanges cannot be used to modify the margins on an item. For this, use
- PropertyChanges intead.
+ PropertyChanges instead.
In the following example we change the top and bottom anchors of an item
using AnchorChanges, and the top and bottom anchor margins using
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 1349d308d7..3c10a65450 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -642,6 +642,28 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex)
return foundIndex;
}
+bool QQuickTableViewPrivate::allColumnsLoaded()
+{
+ // Returns true if all the columns in the model (that are not
+ // hidden by the columnWidthProvider) are currently loaded and visible.
+ const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
+ if (!firstColumnLoaded)
+ return false;
+ bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd;
+ return lastColumnLoaded;
+}
+
+bool QQuickTableViewPrivate::allRowsLoaded()
+{
+ // Returns true if all the rows in the model (that are not hidden
+ // by the columnWidthProvider) are currently loaded and visible.
+ const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
+ if (!firstColumnLoaded)
+ return false;
+ bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd;
+ return lastColumnLoaded;
+}
+
void QQuickTableViewPrivate::updateContentWidth()
{
// Note that we actually never really know what the content size / size of the full table will
@@ -927,43 +949,70 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilityChanges()
{
- // Go through all columns from first to last, find the columns that used
- // to be hidden and not loaded, and check if they should become visible
- // (and vice versa). If there is a change, we need to rebuild.
- RebuildOptions rebuildOptions = RebuildOption::None;
+ // This function will check if there are any visibility changes among
+ // the _already loaded_ rows and columns. Note that there can be rows
+ // and columns to the bottom or right that was not loaded, but should
+ // now become visible (in case there is free space around the table).
+ if (loadedItems.isEmpty()) {
+ // Report no changes
+ return RebuildOption::None;
+ }
- for (int column = leftColumn(); column <= rightColumn(); ++column) {
- const bool wasVisibleFromBefore = loadedColumns.contains(column);
- const bool isVisibleNow = !qFuzzyIsNull(getColumnWidth(column));
- if (wasVisibleFromBefore == isVisibleNow)
- continue;
+ RebuildOptions rebuildOptions = RebuildOption::None;
- // A column changed visibility. This means that it should
- // either be loaded or unloaded. So we need a rebuild.
- qCDebug(lcTableViewDelegateLifecycle) << "Column" << column << "changed visibility to" << isVisibleNow;
+ if (loadedTableOuterRect.x() == origin.x() && leftColumn() != 0) {
+ // Since the left column is at the origin of the viewport, but still not the first
+ // column in the model, we need to calculate a new left column since there might be
+ // columns in front of it that used to be hidden, but should now be visible (QTBUG-93264).
rebuildOptions.setFlag(RebuildOption::ViewportOnly);
- if (column == leftColumn()) {
- // The first loaded column should now be hidden. This means that we
- // need to calculate which column should now be first instead.
- rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn);
+ rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn);
+ } else {
+ // Go through all loaded columns from first to last, find the columns that used
+ // to be hidden and not loaded, and check if they should become visible
+ // (and vice versa). If there is a change, we need to rebuild.
+ for (int column = leftColumn(); column <= rightColumn(); ++column) {
+ const bool wasVisibleFromBefore = loadedColumns.contains(column);
+ const bool isVisibleNow = !qFuzzyIsNull(getColumnWidth(column));
+ if (wasVisibleFromBefore == isVisibleNow)
+ continue;
+
+ // A column changed visibility. This means that it should
+ // either be loaded or unloaded. So we need a rebuild.
+ qCDebug(lcTableViewDelegateLifecycle) << "Column" << column << "changed visibility to" << isVisibleNow;
+ rebuildOptions.setFlag(RebuildOption::ViewportOnly);
+ if (column == leftColumn()) {
+ // The first loaded column should now be hidden. This means that we
+ // need to calculate which column should now be first instead.
+ rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn);
+ }
+ break;
}
- break;
}
- // Go through all rows from first to last, and do the same as above
- for (int row = topRow(); row <= bottomRow(); ++row) {
- const bool wasVisibleFromBefore = loadedRows.contains(row);
- const bool isVisibleNow = !qFuzzyIsNull(getRowHeight(row));
- if (wasVisibleFromBefore == isVisibleNow)
- continue;
-
- // A row changed visibility. This means that it should
- // either be loaded or unloaded. So we need a rebuild.
- qCDebug(lcTableViewDelegateLifecycle) << "Row" << row << "changed visibility to" << isVisibleNow;
+ if (loadedTableOuterRect.y() == origin.y() && topRow() != 0) {
+ // Since the top row is at the origin of the viewport, but still not the first
+ // row in the model, we need to calculate a new top row since there might be
+ // rows in front of it that used to be hidden, but should now be visible (QTBUG-93264).
rebuildOptions.setFlag(RebuildOption::ViewportOnly);
- if (row == topRow())
- rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow);
- break;
+ rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow);
+ } else {
+ // Go through all loaded rows from first to last, find the rows that used
+ // to be hidden and not loaded, and check if they should become visible
+ // (and vice versa). If there is a change, we need to rebuild.
+ for (int row = topRow(); row <= bottomRow(); ++row) {
+ const bool wasVisibleFromBefore = loadedRows.contains(row);
+ const bool isVisibleNow = !qFuzzyIsNull(getRowHeight(row));
+ if (wasVisibleFromBefore == isVisibleNow)
+ continue;
+
+ // A row changed visibility. This means that it should
+ // either be loaded or unloaded. So we need a rebuild.
+ qCDebug(lcTableViewDelegateLifecycle) << "Row" << row << "changed visibility to" << isVisibleNow;
+ rebuildOptions.setFlag(RebuildOption::ViewportOnly);
+ if (row == topRow())
+ rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow);
+ break;
+ }
}
return rebuildOptions;
@@ -971,9 +1020,6 @@ QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilit
void QQuickTableViewPrivate::forceLayout()
{
- if (loadedItems.isEmpty())
- return;
-
clearEdgeSizeCache();
RebuildOptions rebuildOptions = RebuildOption::None;
@@ -1923,12 +1969,12 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
relayoutTableItems();
syncLoadedTableRectFromLoadedTable();
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth)) {
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) {
updateAverageColumnWidth();
updateContentWidth();
}
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight)) {
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) {
updateAverageRowHeight();
updateContentHeight();
}
@@ -3050,3 +3096,5 @@ QQuickTableSectionSizeProviderPrivate::~QQuickTableSectionSizeProviderPrivate()
#include "moc_qquicktableview_p.cpp"
QT_END_NAMESPACE
+
+#include "moc_qquicktableview_p_p.cpp"
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 403a77c3ea..df52672424 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -368,6 +368,8 @@ public:
int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
+ bool allColumnsLoaded();
+ bool allRowsLoaded();
inline int edgeToArrayIndex(Qt::Edge edge);
void clearEdgeSizeCache();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 581ab9f76a..d8fd2017e3 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -965,7 +965,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
const qreal availWidth = availableWidth();
const qreal availHeight = availableHeight();
- lineWidth = q->widthValid() && availWidth > 0 ? availWidth : naturalWidth;
+ lineWidth = q->widthValid() && q->width() > 0 ? availWidth : naturalWidth;
maxHeight = q->heightValid() ? availHeight : FLT_MAX;
// If the width of the item has changed and it's possible the result of wrapping,
@@ -1179,8 +1179,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
QTextLine firstLine = visibleCount == 1 && elideLayout
? elideLayout->lineAt(0)
: layout.lineAt(0);
- Q_ASSERT(firstLine.isValid());
- *baseline = firstLine.y() + firstLine.ascent();
+ if (firstLine.isValid())
+ *baseline = firstLine.y() + firstLine.ascent();
if (!customLayout)
br.setHeight(height);
@@ -2168,7 +2168,7 @@ void QQuickText::resetMaximumLineCount()
<img src="" align="top,middle,bottom" width="" height=""> - inline images
<ol type="">, <ul type=""> and <li> - ordered and unordered lists
<pre></pre> - preformatted
- &gt; &lt; &amp;
+ &gt; &lt; &amp; &quot; &nbsp; &apos;
\endcode
\c Text.StyledText parser is strict, requiring tags to be correctly nested.
@@ -2409,25 +2409,30 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
goto geomChangeDone;
if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
- if (newGeometry.height() > oldGeometry.height()) {
- if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
- // Height is adequate and growing, and it wasn't 0 previously.
- goto geomChangeDone;
- }
- if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
- goto geomChangeDone;
- } else if (newGeometry.height() < oldGeometry.height()) {
- if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
- goto geomChangeDone;
-
- if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
- && d->elideMode != QQuickText::ElideRight
- && !(d->maximumLineCountValid && d->widthExceeded)) {
- goto geomChangeDone;
+ if (!verticalPositionChanged) {
+ if (newGeometry.height() > oldGeometry.height()) {
+ if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
+ // Height is adequate and growing, and it wasn't 0 previously.
+ goto geomChangeDone;
+ }
+ if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
+ goto geomChangeDone;
+ } else if (newGeometry.height() < oldGeometry.height()) {
+ if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
+ goto geomChangeDone;
+
+ if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
+ && d->elideMode != QQuickText::ElideRight
+ && !(d->maximumLineCountValid && d->widthExceeded)) {
+ goto geomChangeDone;
+ }
}
}
} else if (!heightChanged && widthMaximum) {
- goto geomChangeDone;
+ if (!qFuzzyIsNull(oldGeometry.width())) {
+ // no change to height, width is adequate and wasn't 0 before
+ goto geomChangeDone;
+ }
}
if (d->updateOnComponentComplete || d->textHasChanged) {
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index 0fffc1fb9a..698f9649dd 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -326,6 +326,8 @@ private:
Q_DECLARE_PRIVATE(QQuickText)
};
+Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickText::HAlignment, QQuickText::VAlignment)
+
class QTextLine;
class QQuickTextLine : public QObject
{
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 8db2a38b11..742d36a789 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -949,6 +949,12 @@ void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e)
process:
{
if (q->isAcceptableInput(e)) {
+#if QT_CONFIG(im)
+ // QTBUG-90362
+ // Before key press event will be handled, pre-editing part should be finished
+ if (isPreediting())
+ commitPreedit();
+#endif
if (overwriteMode
// no need to call deleteChar() if we have a selection, insertText
// does it already
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 068571f15e..5dad4d4d42 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -822,6 +822,7 @@ void QQuickTextEditPrivate::setTopPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
q->updateSize();
+ q->updateWholeDocument();
emit q->topPaddingChanged();
}
}
@@ -836,6 +837,7 @@ void QQuickTextEditPrivate::setLeftPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
q->updateSize();
+ q->updateWholeDocument();
emit q->leftPaddingChanged();
}
}
@@ -850,6 +852,7 @@ void QQuickTextEditPrivate::setRightPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
q->updateSize();
+ q->updateWholeDocument();
emit q->rightPaddingChanged();
}
}
@@ -864,6 +867,7 @@ void QQuickTextEditPrivate::setBottomPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
q->updateSize();
+ q->updateWholeDocument();
emit q->bottomPaddingChanged();
}
}
@@ -1200,7 +1204,15 @@ void QQuickTextEdit::setCursorVisible(bool on)
/*!
\qmlproperty int QtQuick::TextEdit::cursorPosition
- The position of the cursor in the TextEdit.
+ The position of the cursor in the TextEdit. The cursor is positioned between
+ characters.
+
+ \note The \e characters in this case refer to the string of \l QChar objects,
+ therefore 16-bit Unicode characters, and the position is considered an index
+ into this string. This does not necessarily correspond to individual graphemes
+ in the writing system, as a single grapheme may be represented by multiple
+ Unicode characters, such as in the case of surrogate pairs, linguistic
+ ligatures or diacritics.
*/
int QQuickTextEdit::cursorPosition() const
{
@@ -2355,6 +2367,14 @@ void QQuickTextEdit::q_textChanged()
d->determineHorizontalAlignment();
d->updateDefaultTextOption();
updateSize();
+
+ markDirtyNodesForRange(0, d->document->characterCount(), 0);
+ polish();
+ if (isComponentComplete()) {
+ d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
+ update();
+ }
+
emit textChanged();
}
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index 227d8cbf51..88ba2f9d0d 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -422,6 +422,8 @@ private:
Q_DECLARE_PRIVATE(QQuickTextEdit)
};
+Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickTextEdit::HAlignment, QQuickTextEdit::VAlignment)
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickTextEdit)
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index bb78ead0e8..9db18d1683 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -518,8 +518,16 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
}
/*!
- \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment
+ \readonly
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+*/
+/*!
+ \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::verticalAlignment
Sets the horizontal alignment of the text within the TextInput item's
@@ -542,7 +550,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
\c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
- of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+ of TextInput, use the read-only property \l effectiveHorizontalAlignment.
*/
QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
{
@@ -831,7 +839,20 @@ void QQuickTextInput::setCursorVisible(bool on)
/*!
\qmlproperty int QtQuick::TextInput::cursorPosition
- The position of the cursor in the TextInput.
+ The position of the cursor in the TextInput. The cursor is positioned between
+ characters.
+
+ \note The \e characters in this case refer to the string of \l QChar objects,
+ therefore 16-bit Unicode characters, and the position is considered an index
+ into this string. This does not necessarily correspond to individual graphemes
+ in the writing system, as a single grapheme may be represented by multiple
+ Unicode characters, such as in the case of surrogate pairs, linguistic
+ ligatures or diacritics.
+
+ \l displayText is different if echoMode is set to \l TextInput.Password: then
+ each passwordMaskCharacter is a "narrow" character
+ (the cursorPosition always moves by 1), even if the text in the TextInput is not.
+
*/
int QQuickTextInput::cursorPosition() const
{
@@ -849,6 +870,7 @@ void QQuickTextInput::setCursorPosition(int cp)
/*!
\qmlproperty rectangle QtQuick::TextInput::cursorRectangle
+ \readonly
The rectangle where the standard text cursor is rendered within the text input. Read only.
@@ -890,6 +912,7 @@ QRectF QQuickTextInput::cursorRectangle() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionEnd, cursorPosition, selectedText
*/
int QQuickTextInput::selectionStart() const
@@ -905,6 +928,7 @@ int QQuickTextInput::selectionStart() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionStart, cursorPosition, selectedText
*/
int QQuickTextInput::selectionEnd() const
@@ -935,6 +959,7 @@ void QQuickTextInput::select(int start, int end)
/*!
\qmlproperty string QtQuick::TextInput::selectedText
+ \readonly
This read-only property provides the text currently selected in the
text input.
@@ -1152,6 +1177,7 @@ void QQuickTextInput::setInputMask(const QString &im)
/*!
\qmlproperty bool QtQuick::TextInput::acceptableInput
+ \readonly
This property is always true unless a validator or input mask has been set.
If a validator or input mask has been set, this property will only be true
@@ -1585,7 +1611,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
d->moveCursor(cursor, mark);
if (d->focusOnPress && !qGuiApp->styleHints()->setFocusOnTouchRelease())
- ensureActiveFocus();
+ ensureActiveFocus(Qt::MouseFocusReason);
event->setAccepted(true);
}
@@ -1637,7 +1663,7 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
#endif
if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
- ensureActiveFocus();
+ ensureActiveFocus(Qt::MouseFocusReason);
if (!event->isAccepted())
QQuickImplicitSizeItem::mouseReleaseEvent(event);
@@ -1872,10 +1898,10 @@ void QQuickTextInput::invalidateFontCaches()
d->m_textLayout.engine()->resetFontEngineCache();
}
-void QQuickTextInput::ensureActiveFocus()
+void QQuickTextInput::ensureActiveFocus(Qt::FocusReason reason)
{
bool hadActiveFocus = hasActiveFocus();
- forceActiveFocus();
+ forceActiveFocus(reason);
#if QT_CONFIG(im)
Q_D(QQuickTextInput);
// re-open input panel on press if already focused
@@ -2105,7 +2131,7 @@ void QQuickTextInput::undo()
{
Q_D(QQuickTextInput);
if (!d->m_readOnly) {
- d->resetInputMethod();
+ d->cancelInput();
d->internalUndo();
d->finishChange(-1, true);
}
@@ -2121,7 +2147,7 @@ void QQuickTextInput::redo()
{
Q_D(QQuickTextInput);
if (!d->m_readOnly) {
- d->resetInputMethod();
+ d->cancelInput();
d->internalRedo();
d->finishChange();
}
@@ -2459,6 +2485,7 @@ void QQuickTextInput::setPersistentSelection(bool on)
/*!
\qmlproperty bool QtQuick::TextInput::canPaste
+ \readonly
Returns true if the TextInput is writable and the content of the clipboard is
suitable for pasting into the TextInput.
@@ -2480,6 +2507,7 @@ bool QQuickTextInput::canPaste() const
/*!
\qmlproperty bool QtQuick::TextInput::canUndo
+ \readonly
Returns true if the TextInput is writable and there are previous operations
that can be undone.
@@ -2493,6 +2521,7 @@ bool QQuickTextInput::canUndo() const
/*!
\qmlproperty bool QtQuick::TextInput::canRedo
+ \readonly
Returns true if the TextInput is writable and there are \l {undo}{undone}
operations that can be redone.
@@ -2506,6 +2535,7 @@ bool QQuickTextInput::canRedo() const
/*!
\qmlproperty real QtQuick::TextInput::contentWidth
+ \readonly
Returns the width of the text, including the width past the width
which is covered due to insufficient wrapping if \l wrapMode is set.
@@ -2519,6 +2549,7 @@ qreal QQuickTextInput::contentWidth() const
/*!
\qmlproperty real QtQuick::TextInput::contentHeight
+ \readonly
Returns the height of the text, including the height past the height
that is covered if the text does not fit within the set height.
@@ -2682,7 +2713,7 @@ void QQuickTextInput::focusOutEvent(QFocusEvent *event)
/*!
\qmlproperty bool QtQuick::TextInput::inputMethodComposing
-
+ \readonly
This property holds whether the TextInput has partial text input from an
input method.
@@ -2748,11 +2779,13 @@ void QQuickTextInputPrivate::init()
m_inputControl = new QInputControl(QInputControl::LineEdit, q);
}
-void QQuickTextInputPrivate::resetInputMethod()
+void QQuickTextInputPrivate::cancelInput()
{
+#if QT_CONFIG(im)
Q_Q(QQuickTextInput);
if (!m_readOnly && q->hasActiveFocus() && qGuiApp)
- QGuiApplication::inputMethod()->reset();
+ cancelPreedit();
+#endif // im
}
void QQuickTextInput::updateCursorRectangle(bool scroll)
@@ -2952,6 +2985,7 @@ void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
updateLayout();
+ q->updateCursorRectangle();
emit q->topPaddingChanged();
}
}
@@ -2966,6 +3000,7 @@ void QQuickTextInputPrivate::setLeftPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
updateLayout();
+ q->updateCursorRectangle();
emit q->leftPaddingChanged();
}
}
@@ -2980,6 +3015,7 @@ void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
updateLayout();
+ q->updateCursorRectangle();
emit q->rightPaddingChanged();
}
}
@@ -2994,6 +3030,7 @@ void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset)
}
if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
updateLayout();
+ q->updateCursorRectangle();
emit q->bottomPaddingChanged();
}
}
@@ -3429,17 +3466,19 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
if (event->replacementStart() <= 0)
c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
- m_cursor += event->replacementStart();
- if (m_cursor < 0)
- m_cursor = 0;
+ int cursorInsertPos = m_cursor + event->replacementStart();
+ if (cursorInsertPos < 0)
+ cursorInsertPos = 0;
// insert commit string
if (event->replacementLength()) {
- m_selstart = m_cursor;
+ m_selstart = cursorInsertPos;
m_selend = m_selstart + event->replacementLength();
m_selend = qMin(m_selend, m_text.length());
removeSelectedText();
}
+ m_cursor = cursorInsertPos;
+
if (!event->commitString().isEmpty()) {
internalInsert(event->commitString());
cursorPositionChanged = true;
@@ -3450,7 +3489,12 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
for (int i = 0; i < event->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::Selection) {
- m_cursor = qBound(0, a.start + a.length, m_text.length());
+ // If we already called internalInsert(), the cursor position will
+ // already be adjusted correctly. The attribute.start does
+ // not seem to take the mask into account, so it will reset cursor
+ // to an invalid position in such case.
+ if (!cursorPositionChanged)
+ m_cursor = qBound(0, a.start + a.length, m_text.length());
if (a.length) {
m_selstart = qMax(0, qMin(a.start, m_text.length()));
m_selend = m_cursor;
@@ -3466,8 +3510,12 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
}
QString oldPreeditString = m_textLayout.preeditAreaText();
m_textLayout.setPreeditArea(m_cursor, event->preeditString());
- if (oldPreeditString != m_textLayout.preeditAreaText())
+ if (oldPreeditString != m_textLayout.preeditAreaText()) {
emit q->preeditTextChanged();
+ if (!event->preeditString().isEmpty() && m_undoPreeditState == -1)
+ // Pre-edit text started. Remember state for undo purpose.
+ m_undoPreeditState = priorState;
+ }
const int oldPreeditCursor = m_preeditCursor;
m_preeditCursor = event->preeditString().length();
hasImState = !event->preeditString().isEmpty();
@@ -3494,11 +3542,10 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
m_textLayout.setFormats(formats);
updateDisplayText(/*force*/ true);
- if ((cursorPositionChanged && !emitCursorPositionChanged())
- || m_preeditCursor != oldPreeditCursor
- || isGettingInput) {
+ if (cursorPositionChanged && emitCursorPositionChanged())
+ q->updateInputMethod(Qt::ImCursorPosition | Qt::ImAnchorPosition);
+ else if (m_preeditCursor != oldPreeditCursor || isGettingInput)
q->updateCursorRectangle();
- }
if (isGettingInput)
finishChange(priorState);
@@ -3510,6 +3557,11 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle
| Qt::ImCurrentSelection);
}
+
+ // Empty pre-edit text handled. Clean m_undoPreeditState
+ if (event->preeditString().isEmpty())
+ m_undoPreeditState = -1;
+
}
#endif // im
@@ -3586,6 +3638,12 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
if (m_maskData)
checkIsValid();
+#if QT_CONFIG(im)
+ // If we were during pre-edit, validateFromState should point to the state before pre-edit
+ // has been started. Choose the correct oldest remembered state
+ if (m_undoPreeditState >= 0 && (m_undoPreeditState < validateFromState || validateFromState < 0))
+ validateFromState = m_undoPreeditState;
+#endif
if (validateFromState >= 0 && wasValidInput && !m_validInput) {
if (m_transactions.count())
return false;
@@ -3658,6 +3716,9 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e
}
m_history.clear();
m_undoState = 0;
+#if QT_CONFIG(im)
+ m_undoPreeditState = -1;
+#endif
m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
m_textDirty = (oldText != m_text);
@@ -4672,7 +4733,7 @@ void QQuickTextInput::ensureVisible(int position)
void QQuickTextInput::clear()
{
Q_D(QQuickTextInput);
- d->resetInputMethod();
+ d->cancelInput();
d->clear();
}
@@ -4713,6 +4774,7 @@ void QQuickTextInput::setPadding(qreal padding)
d->extra.value().padding = padding;
d->updateLayout();
+ updateCursorRectangle();
emit paddingChanged();
if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
emit topPaddingChanged();
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 9f7b82b168..8e97393d10 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -361,7 +361,7 @@ Q_SIGNALS:
private:
void invalidateFontCaches();
- void ensureActiveFocus();
+ void ensureActiveFocus(Qt::FocusReason reason);
protected:
QQuickTextInput(QQuickTextInputPrivate &dd, QQuickItem *parent = nullptr);
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 7fbba49405..6c11ca0a0b 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -110,6 +110,7 @@ public:
, m_cursor(0)
#if QT_CONFIG(im)
, m_preeditCursor(0)
+ , m_undoPreeditState(-1)
#endif
, m_blinkEnabled(false)
, m_blinkTimer(0)
@@ -172,7 +173,7 @@ public:
}
void init();
- void resetInputMethod();
+ void cancelInput();
void startCreatingCursor();
void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0);
void updateHorizontalScroll();
@@ -248,6 +249,7 @@ public:
int m_cursor;
#if QT_CONFIG(im)
int m_preeditCursor;
+ int m_undoPreeditState;
#endif
bool m_blinkEnabled;
int m_blinkTimer;
@@ -335,7 +337,13 @@ public:
bool isUndoAvailable() const { return !m_readOnly && m_undoState; }
bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); }
- void clearUndo() { m_history.clear(); m_undoState = 0; }
+ void clearUndo() {
+ m_history.clear();
+ m_undoState = 0;
+#if QT_CONFIG(im)
+ m_undoPreeditState = -1;
+#endif
+ }
bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); }
bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 9ff91eb9a0..8ab69603ad 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -164,7 +164,7 @@ protected:
public slots:
void incubate() {
- if (incubatingObjectCount()) {
+ if (m_renderLoop && incubatingObjectCount()) {
if (m_renderLoop->interleaveIncubation()) {
incubateFor(m_incubation_time);
} else {
@@ -180,12 +180,12 @@ public slots:
protected:
void incubatingObjectCountChanged(int count) override
{
- if (count && !m_renderLoop->interleaveIncubation())
+ if (count && m_renderLoop && !m_renderLoop->interleaveIncubation())
incubateAgain();
}
private:
- QSGRenderLoop *m_renderLoop;
+ QPointer<QSGRenderLoop> m_renderLoop;
int m_incubation_time;
int m_timer;
};
@@ -450,15 +450,15 @@ void QQuickWindow::physicalDpiChanged()
void QQuickWindow::handleScreenChanged(QScreen *screen)
{
Q_D(QQuickWindow);
+ // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
+ disconnect(d->physicalDpiChangedConnection);
if (screen) {
physicalDpiChanged();
// When physical DPI changes on the same screen, either the resolution or the device pixel
// ratio changed. We must check what it is. Device pixel ratio does not have its own
- // ...Changed() signal.
- d->physicalDpiChangedConnection = connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
- this, SLOT(physicalDpiChanged()));
- } else {
- disconnect(d->physicalDpiChangedConnection);
+ // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
+ d->physicalDpiChangedConnection = connect(screen, &QScreen::physicalDotsPerInchChanged,
+ this, &QQuickWindow::physicalDpiChanged);
}
d->forcePolish();
@@ -708,8 +708,13 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_ASSERT(windowManager || renderControl);
- if (QScreen *screen = q->screen())
- devicePixelRatio = screen->devicePixelRatio();
+ if (QScreen *screen = q->screen()) {
+ devicePixelRatio = screen->devicePixelRatio();
+ // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
+ // and connects to the new screen
+ physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
+ q, &QQuickWindow::physicalDpiChanged);
+ }
QSGContext *sg;
if (renderControl) {
@@ -1489,7 +1494,11 @@ QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
}
/*!
- \internal
+ Constructs a window for displaying a QML scene, whose rendering will
+ be controlled by the \a control object.
+ Please refer to QQuickRenderControl's documentation for more information.
+
+ \since 5.4
*/
QQuickWindow::QQuickWindow(QQuickRenderControl *control)
: QWindow(*(new QQuickWindowPrivate), nullptr)
@@ -1703,6 +1712,8 @@ QQuickItem *QQuickWindow::contentItem() const
\brief The item which currently has active focus or \c null if there is
no item with active focus.
+
+ \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
*/
QQuickItem *QQuickWindow::activeFocusItem() const
{
@@ -2141,6 +2152,15 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ QQuickItemPrivate *hoverLeaveItemPrivate = QQuickItemPrivate::get(hoverLeaveItem);
+ if (hoverLeaveItemPrivate->hasPointerHandlers()) {
+ for (QQuickPointerHandler *handler : hoverLeaveItemPrivate->extra->pointerHandlers) {
+ if (auto *hh = qmlobject_cast<QQuickHoverHandler *>(handler)) {
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->cancelPassiveGrab(hh);
+ }
+ }
+ }
}
if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
@@ -2185,12 +2205,16 @@ bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEven
QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
+ if (!item->window())
+ continue;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
event->localize(item);
// Let Pointer Handlers have the first shot
itemPrivate->handlePointerEvent(event);
if (point->isAccepted())
return true;
+ if (!item->window())
+ continue;
QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint());
#if QT_CONFIG(wheelevent)
// Let the Item have a chance to handle it
@@ -2425,6 +2449,9 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
event->buttons());
deliverPointerEvent(pointerEventInstance(event));
+#if QT_CONFIG(cursor)
+ updateCursor(event->windowPos());
+#endif
break;
case QEvent::MouseButtonDblClick:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
@@ -2596,7 +2623,9 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) con
}
Q_ASSERT(dev);
- return pointerEventInstance(dev, event->type())->reset(event);
+ auto pev = pointerEventInstance(dev, event->type());
+ Q_ASSERT(pev);
+ return pev->reset(event);
}
void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
index b80bacbaa0..af912e1a3f 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
@@ -54,6 +54,8 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgtexturematerial_p.h>
+#include <QtCore/QPointer>
+
QT_BEGIN_NAMESPACE
namespace QSGSoftwareHelpers {
@@ -132,7 +134,7 @@ private:
QRectF m_innerSourceRect;
QRectF m_subSourceRect;
- QSGTexture *m_texture;
+ QPointer<QSGTexture> m_texture;
QPixmap m_cachedMirroredPixmap;
bool m_mirror;
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
index 7c98e2c1e1..13b4c63c3c 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
@@ -180,3 +180,5 @@ QSGTexture *Texture::removedFromAtlas() const
}
QT_END_NAMESPACE
+
+#include "moc_qsgcompressedatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index 65abb2a1af..ce433a0411 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -382,3 +382,5 @@ QSize QSGCompressedTextureFactory::textureSize() const
}
QT_END_NAMESPACE
+
+#include "moc_qsgcompressedtexture_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 3b49b88182..8f24617b24 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1068,11 +1068,10 @@ static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
free(buffer->data);
}
-static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIndexBuffer)
+static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs)
{
qsg_wipeBuffer(&batch->vbo, funcs);
- if (separateIndexBuffer)
- qsg_wipeBuffer(&batch->ibo, funcs);
+ qsg_wipeBuffer(&batch->ibo, funcs);
delete batch->ubuf;
batch->stencilClipState.reset();
delete batch;
@@ -1082,13 +1081,12 @@ Renderer::~Renderer()
{
if (m_rhi || QOpenGLContext::currentContext()) {
// Clean up batches and buffers
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
- qsg_wipeBatch(m_opaqueBatches.at(i), this, separateIndexBuffer);
+ qsg_wipeBatch(m_opaqueBatches.at(i), this);
for (int i = 0; i < m_alphaBatches.size(); ++i)
- qsg_wipeBatch(m_alphaBatches.at(i), this, separateIndexBuffer);
+ qsg_wipeBatch(m_alphaBatches.at(i), this);
for (int i = 0; i < m_batchPool.size(); ++i)
- qsg_wipeBatch(m_batchPool.at(i), this, separateIndexBuffer);
+ qsg_wipeBatch(m_batchPool.at(i), this);
}
for (Node *n : qAsConst(m_nodes))
@@ -1133,6 +1131,9 @@ void Renderer::releaseCachedResources()
if (m_rhi)
m_rhi->releaseCachedResources();
+
+ m_vertexUploadPool.resize(0);
+ m_indexUploadPool.resize(0);
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
@@ -1159,8 +1160,7 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
- QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
- ? m_indexUploadPool : m_vertexUploadPool;
+ QDataBuffer<char> &pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
if (byteSize > pool.size())
pool.resize(byteSize);
buffer->data = pool.data();
@@ -1878,7 +1878,7 @@ bool Renderer::checkOverlap(int first, int last, const Rect &bounds)
{
for (int i=first; i<=last; ++i) {
Element *e = m_alphaRenderList.at(i);
- if (!e || e->batch)
+ if (!e)
continue;
Q_ASSERT(e->boundsComputed);
if (e->bounds.intersects(bounds))
@@ -1949,8 +1949,10 @@ void Renderer::prepareAlphaBatches()
continue;
if (ej->root != ei->root || ej->isRenderNode)
break;
- if (ej->batch)
+ if (ej->batch) {
+ overlapBounds |= ej->bounds;
continue;
+ }
QSGGeometryNode *gnj = ej->node;
if (gnj->geometry()->vertexCount() == 0)
@@ -1958,7 +1960,11 @@ void Renderer::prepareAlphaBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines
+ || (gni->geometry()->lineWidth() == gnj->geometry()->lineWidth()
+ // Must not do overlap checks when the line width is not 1,
+ // we have no knowledge how such lines are rasterized.
+ && gni->geometry()->lineWidth() == 1.0f))
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -2214,11 +2220,7 @@ void Renderer::uploadBatch(Batch *b)
ibufferSize = unmergedIndexSize;
}
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
+ map(&b->ibo, ibufferSize, true);
map(&b->vbo, bufferSize);
if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
@@ -2228,9 +2230,7 @@ void Renderer::uploadBatch(Batch *b)
if (b->merged) {
char *vertexData = b->vbo.data;
char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
+ char *indexData = b->ibo.data;
quint16 iOffset16 = 0;
quint32 iOffset32 = 0;
@@ -2242,8 +2242,8 @@ void Renderer::uploadBatch(Batch *b)
const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
int indicesInSet = 0;
b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ int drawSetIndices = 0;
+ const char *indexBase = b->ibo.data;
b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
while (e) {
verticesInSet += e->node->geometry()->vertexCount();
@@ -2277,8 +2277,7 @@ void Renderer::uploadBatch(Batch *b)
}
} else {
char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
+ char *iboData = b->ibo.data;
Element *e = b->first;
while (e) {
QSGGeometry *g = e->node->geometry();
@@ -2346,9 +2345,7 @@ void Renderer::uploadBatch(Batch *b)
if (!b->drawSets.isEmpty()) {
if (m_uint32IndexForRhi) {
- const quint32 *id = (const quint32 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint32 *id = (const quint32 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2359,9 +2356,7 @@ void Renderer::uploadBatch(Batch *b)
}
}
} else {
- const quint16 *id = (const quint16 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint16 *id = (const quint16 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2382,8 +2377,7 @@ void Renderer::uploadBatch(Batch *b)
#endif // QT_NO_DEBUG_OUTPUT
unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->ibo, true);
if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
@@ -2432,18 +2426,20 @@ ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
// TODO: Check for multisampling and pixel grid alignment.
bool isRectangleWithNoPerspective = clip->isRectangular()
&& qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
- auto noRotate = [] (const QMatrix4x4 &m) { return qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); };
- auto isRotate90 = [] (const QMatrix4x4 &m) { return qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); };
- auto scissorRect = [&] (const QRectF &bbox, const QMatrix4x4 &m) {
+ bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
+ bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
+
+ if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
+ QRectF bbox = clip->clipRect();
qreal invW = 1 / m(3, 3);
qreal fx1, fy1, fx2, fy2;
- if (noRotate(m)) {
+ if (noRotate) {
fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
} else {
- Q_ASSERT(isRotate90(m));
+ Q_ASSERT(isRotate90);
fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
@@ -2462,18 +2458,12 @@ ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
- return QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
- };
-
- if (isRectangleWithNoPerspective && (noRotate(m) || isRotate90(m))) {
- auto rect = scissorRect(clip->clipRect(), m);
-
if (!(clipType & ClipState::ScissorClip)) {
- m_currentScissorRect = rect;
+ m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
glEnable(GL_SCISSOR_TEST);
clipType |= ClipState::ScissorClip;
} else {
- m_currentScissorRect &= rect;
+ m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
}
glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(),
m_currentScissorRect.width(), m_currentScissorRect.height());
@@ -2488,31 +2478,9 @@ ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
m_clipProgram.link();
m_clipMatrixId = m_clipProgram.uniformLocation("matrix");
}
- const QSGClipNode *clipNext = clip->clipList();
- if (clipNext) {
- QMatrix4x4 mNext = m_current_projection_matrix;
- if (clipNext->matrix())
- mNext *= *clipNext->matrix();
-
- auto rect = scissorRect(clipNext->clipRect(), mNext);
-
- ClipState::ClipType clipTypeNext = clipType ;
- clipTypeNext |= ClipState::StencilClip;
- QRect m_next_scissor_rect = m_currentScissorRect;
- if (!(clipTypeNext & ClipState::ScissorClip)) {
- m_next_scissor_rect = rect;
- glEnable(GL_SCISSOR_TEST);
- } else {
- m_next_scissor_rect =
- m_currentScissorRect & rect;
- }
- glScissor(m_next_scissor_rect.x(), m_next_scissor_rect.y(),
- m_next_scissor_rect.width(), m_next_scissor_rect.height());
- }
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
- glDisable(GL_SCISSOR_TEST);
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
@@ -3069,7 +3037,7 @@ void Renderer::renderMergedBatch(const Batch *batch) // legacy (GL-only)
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
char *indexBase = nullptr;
- const Buffer *indexBuf = m_context->separateIndexBuffer() ? &batch->ibo : &batch->vbo;
+ const Buffer *indexBuf = &batch->ibo;
if (m_context->hasBrokenIndexBufferObjects()) {
indexBase = indexBuf->data;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -3162,8 +3130,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only)
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
char *indexBase = nullptr;
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo;
+ const Buffer *indexBuf = &batch->ibo;
if (batch->indexCount) {
if (m_context->hasBrokenIndexBufferObjects()) {
indexBase = indexBuf->data;
@@ -3193,9 +3160,6 @@ void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only)
int vOffset = 0;
char *iOffset = indexBase;
- if (!separateIndexBuffer)
- iOffset += batch->vertexCount * gn->geometry()->sizeOfVertex();
-
QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
while (e) {
@@ -4026,6 +3990,17 @@ void Renderer::renderBatches()
if (m_useDepthBuffer) {
glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
+ glDepthMask(true);
+ }
+ glColorMask(true, true, true, true);
+ glDisable(GL_SCISSOR_TEST);
+
+ bindable()->clear(clearMode());
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ if (m_useDepthBuffer) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(true);
@@ -4039,11 +4014,6 @@ void Renderer::renderBatches()
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
- bindable()->clear(clearMode());
-
- if (m_renderPassRecordingCallbacks.start)
- m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
-
if (Q_LIKELY(renderOpaque)) {
for (int i=0; i<m_opaqueBatches.size(); ++i) {
Batch *b = m_opaqueBatches.at(i);
@@ -4062,12 +4032,14 @@ void Renderer::renderBatches()
if (Q_LIKELY(renderAlpha)) {
for (int i=0; i<m_alphaBatches.size(); ++i) {
Batch *b = m_alphaBatches.at(i);
- if (b->merged)
+ if (b->merged) {
renderMergedBatch(b);
- else if (b->isRenderNode)
+ } else if (b->isRenderNode) {
+ m_current_projection_matrix = projectionMatrix();
renderRenderNode(b);
- else
+ } else {
renderUnmergedBatch(b);
+ }
}
}
@@ -4357,7 +4329,7 @@ void Renderer::render()
if (largestVBO * 2 < m_vertexUploadPool.size())
m_vertexUploadPool.resize(largestVBO * 2);
- if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size())
+ if (largestIBO * 2 < m_indexUploadPool.size())
m_indexUploadPool.resize(largestIBO * 2);
renderBatches();
@@ -4470,6 +4442,9 @@ void Renderer::renderRenderNode(Batch *batch) // legacy (GL-only)
opacity = opacity->parent();
}
+ // having DepthAwareRendering leaves depth test on in the alpha pass
+ const bool depthTestWasEnabled = m_useDepthBuffer;
+
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
@@ -4504,7 +4479,9 @@ void Renderer::renderRenderNode(Batch *batch) // legacy (GL-only)
m_currentClipType = ClipState::NoClip;
}
- if (changes & QSGRenderNode::DepthState)
+ if (depthTestWasEnabled)
+ glEnable(GL_DEPTH_TEST);
+ else if (changes & QSGRenderNode::DepthState)
glDisable(GL_DEPTH_TEST);
if (changes & QSGRenderNode::ColorState)
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index d17915a842..685ac1cd95 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -202,6 +202,7 @@ public:
void setLineWidth(float w);
private:
+ Q_DISABLE_COPY_MOVE(QSGGeometry)
friend class QSGGeometryData;
int m_drawing_mode;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
index c5cbd0c979..d2eb22f45a 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
@@ -105,7 +105,7 @@ void QSGMaterialRhiShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
ubufBinding = -1;
ubufSize = 0;
ubufStages = { };
- memset(combinedImageSamplerBindings, 0, sizeof(combinedImageSamplerBindings));
+ memset(static_cast<void *>(combinedImageSamplerBindings), 0, sizeof(combinedImageSamplerBindings));
vertexShader = fragmentShader = nullptr;
masterUniformData.clear();
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
index 9282b6c308..c7af996a30 100644
--- a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
@@ -130,7 +130,7 @@ void OpenGLVisualizer::visualizeBatch(Batch *b)
if (b->merged) {
shader->setUniformValue(shader->matrix, matrix);
- const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
+ const char *dataStart = b->ibo.data;
for (int ds=0; ds<b->drawSets.size(); ++ds) {
const DrawSet &set = b->drawSets.at(ds);
m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(),
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 742a4d03b2..04f1f2732c 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -465,9 +465,9 @@ public:
{
return pixelSize / QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
}
- int distanceFieldRadius() const
+ qreal distanceFieldRadius() const
{
- return QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
+ return QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
}
int glyphCount() const { return m_glyphCount; }
bool doubleGlyphResolution() const { return m_doubleGlyphResolution; }
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 3c60f830de..d6834b554e 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -119,7 +119,8 @@ public:
protected:
void initialize() override;
- int m_matrix_id;
+ int m_projectionMatrix_id;
+ int m_modelViewMatrix_id;
int m_color_id;
int m_textureScale_id;
float m_devicePixelRatio;
@@ -135,7 +136,8 @@ char const *const *QSGTextMaskShader::attributeNames() const
QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
: QSGMaterialShader(*new QSGMaterialShaderPrivate)
- , m_matrix_id(-1)
+ , m_projectionMatrix_id(-1)
+ , m_modelViewMatrix_id(-1)
, m_color_id(-1)
, m_textureScale_id(-1)
, m_glyphFormat(glyphFormat)
@@ -146,7 +148,8 @@ QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
void QSGTextMaskShader::initialize()
{
- m_matrix_id = program()->uniformLocation("matrix");
+ m_projectionMatrix_id = program()->uniformLocation("projectionMatrix");
+ m_modelViewMatrix_id = program()->uniformLocation("modelViewMatrix");
m_color_id = program()->uniformLocation("color");
m_textureScale_id = program()->uniformLocation("textureScale");
m_devicePixelRatio = (float) qsg_device_pixel_ratio(QOpenGLContext::currentContext());
@@ -184,8 +187,10 @@ void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEf
program()->setUniformValue("dpr", m_devicePixelRatio);
}
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ if (state.isMatrixDirty()) {
+ program()->setUniformValue(m_projectionMatrix_id, state.projectionMatrix());
+ program()->setUniformValue(m_modelViewMatrix_id, state.modelViewMatrix());
+ }
}
class QSG8BitTextMaskShader : public QSGTextMaskShader
@@ -387,8 +392,10 @@ void QSGStyledTextShader::updateState(const RenderState &state,
}
}
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ if (state.isMatrixDirty()) {
+ program()->setUniformValue(m_projectionMatrix_id, state.projectionMatrix());
+ program()->setUniformValue(m_modelViewMatrix_id, state.modelViewMatrix());
+ }
}
class QSGOutlinedTextShader : public QSGStyledTextShader
@@ -428,6 +435,18 @@ QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"));
}
+enum UbufOffset {
+ ModelViewMatrixOffset = 0,
+ ProjectionMatrixOffset = ModelViewMatrixOffset + 64,
+ ColorOffset = ProjectionMatrixOffset + 64,
+ TextureScaleOffset = ColorOffset + 16,
+ DprOffset = TextureScaleOffset + 8,
+
+ // + 1 float padding (vec4 must be aligned to 16)
+ StyleColorOffset = DprOffset + 4 + 4,
+ ShiftOffset = StyleColorOffset + 16
+};
+
bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
@@ -443,11 +462,14 @@ bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= DprOffset + 4);
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ const QMatrix4x4 mv = state.modelViewMatrix();
+ memcpy(buf->data() + ModelViewMatrixOffset, mv.constData(), 64);
+ const QMatrix4x4 p = state.projectionMatrix();
+ memcpy(buf->data() + ProjectionMatrixOffset, p.constData(), 64);
+
changed = true;
}
@@ -456,13 +478,13 @@ bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
if (updated || !oldMat || oldRtex != newRtex) {
const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(),
1.0f / mat->rhiGlyphCache()->height());
- memcpy(buf->data() + 64 + 16, &textureScale, 8);
+ memcpy(buf->data() + TextureScaleOffset, &textureScale, 8);
changed = true;
}
if (!oldMat) {
float dpr = state.devicePixelRatio();
- memcpy(buf->data() + 64 + 16 + 8, &dpr, 4);
+ memcpy(buf->data() + DprOffset, &dpr, 4);
}
// move texture uploads/copies onto the renderer's soon-to-be-committed list
@@ -510,11 +532,11 @@ bool QSG8BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 80);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -553,12 +575,12 @@ bool QSG24BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only; coloring happens via the blend constant
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -608,12 +630,12 @@ bool QSG32BitColorTextRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only
const QVector4D color(0, 0, 0, mat->color().w() * state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -649,20 +671,17 @@ bool QSGStyledTextRhiShader::updateUniformData(RenderState &state,
QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 120);
-
- // matrix..dpr + 1 float padding (vec4 must be aligned to 16)
- const int startOffset = 64 + 16 + 8 + 4 + 4;
+ Q_ASSERT(buf->size() >= ShiftOffset + 8);
if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity());
- memcpy(buf->data() + startOffset, &styleColor, 16);
+ memcpy(buf->data() + StyleColorOffset, &styleColor, 16);
changed = true;
}
if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) {
const QVector2D v = mat->styleShift();
- memcpy(buf->data() + startOffset + 16, &v, 8);
+ memcpy(buf->data() + ShiftOffset, &v, 8);
changed = true;
}
@@ -799,8 +818,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p,
QTextureGlyphCache *cache = glyphCache();
QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
- cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
- fixedPointPositions.data());
+ cache->populate(fontD->fontEngine,
+ glyphIndexes.size(),
+ glyphIndexes.constData(),
+ fixedPointPositions.data(),
+ true);
cache->fillInPendingGlyphs();
int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat());
@@ -820,9 +842,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p,
bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
for (int i=0; i<glyphIndexes.size(); ++i) {
QPointF glyphPosition = glyphPositions.at(i) + position;
+ QFixedPoint fixedPointPosition = fixedPointPositions.at(i);
+
QFixed subPixelPosition;
if (supportsSubPixelPositions)
- subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x()));
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(fixedPointPosition.x.toReal() * glyphCacheScaleX));
QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index bf7bb052bb..0e11a062de 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -415,21 +415,6 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
}
-bool QSGDefaultRenderContext::separateIndexBuffer() const
-{
- if (m_rhi)
- return true;
-
- // WebGL: A given WebGLBuffer object may only be bound to one of
- // the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its
- // lifetime. An attempt to bind a buffer object to the other
- // target will generate an INVALID_OPERATION error, and the
- // current binding will remain untouched.
- static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0
- || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0);
- return isWebGL;
-}
-
void QSGDefaultRenderContext::preprocess()
{
for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index e90a11eda6..6da67264d1 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -140,7 +140,6 @@ public:
bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; }
int maxTextureSize() const override { return m_maxTextureSize; }
- bool separateIndexBuffer() const;
int msaaSampleCount() const { return m_initParams.sampleCount; }
diff --git a/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
index b6b6f3b057..6e9894b10b 100644
--- a/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
@@ -122,7 +122,7 @@ void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs
// We need to add a buffer to avoid glyphs that overlap the border between two
// textures causing the height of the textures to extend beyond the limit.
- m_maxTextureHeight = m_maxTextureWidth - (qCeil(m_referenceFont.pixelSize() * scaleFactor) + distanceFieldRadius() * 2 + padding * 2);
+ m_maxTextureHeight = m_maxTextureWidth - (qCeil(m_referenceFont.pixelSize() * scaleFactor + distanceFieldRadius() * 2) + padding * 2);
}
if (m_areaAllocator == nullptr)
@@ -132,8 +132,8 @@ void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs
glyph_t glyphIndex = *it;
QRectF boundingRect = glyphData(glyphIndex).boundingRect;
- int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
- int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ int glyphWidth = qCeil(boundingRect.width() + distanceFieldRadius()) * 2;
+ int glyphHeight = qCeil(boundingRect.height() + distanceFieldRadius()) * 2;
QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
QRect alloc = m_areaAllocator->allocate(glyphSize);
@@ -144,8 +144,8 @@ void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs
TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
- int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2;
- int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width() + distanceFieldRadius()) * 2;
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height() + distanceFieldRadius()) * 2;
m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
unusedCoord.y - padding,
padding * 2 + unusedGlyphWidth,
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index c96129e660..ee01211545 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -413,13 +413,6 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context");
}
-#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
-#if QT_CONFIG(opengl)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
-#endif
-#endif
-
if (d->swapchain) {
if (window->handle()) {
// We get here when exiting via QCoreApplication::quit() instead of
@@ -432,6 +425,14 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
}
d->cleanupNodesOnShutdown();
+
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
+#endif
+
if (m_windows.size() == 0) {
rc->invalidate();
d->rhi = nullptr;
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
index 53b6fe117f..2325a2665b 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -86,8 +86,8 @@ void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
int padding = QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING;
QRectF boundingRect = glyphData(glyphIndex).boundingRect;
- int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
- int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ int glyphWidth = qCeil(boundingRect.width() + distanceFieldRadius() * 2);
+ int glyphHeight = qCeil(boundingRect.height() + distanceFieldRadius() * 2);
QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
QRect alloc = m_areaAllocator->allocate(glyphSize);
@@ -98,8 +98,8 @@ void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
- int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2;
- int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width() + distanceFieldRadius() * 2);
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height() + distanceFieldRadius() * 2);
m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
unusedCoord.y - padding,
padding * 2 + unusedGlyphWidth,
@@ -446,7 +446,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
const char *textureRecord = allocatorData;
for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
- if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ if (qtdfTableEnd - textureRecord < Qtdf::TextureRecordSize) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
@@ -462,7 +462,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
const char *glyphRecord = textureRecord;
for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
- if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ if (qtdfTableEnd - glyphRecord < Qtdf:: GlyphRecordSize) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
@@ -512,8 +512,8 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
int width = texInfo->allocatedArea.width();
int height = texInfo->allocatedArea.height();
- qint64 size = width * height;
- if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qint64 size = qint64(width) * height;
+ if (qtdfTableEnd - reinterpret_cast<const char *>(textureData) < size) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index e86dae7c09..b0d3ca78f5 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -572,10 +572,10 @@ void QSGRhiShaderEffectMaterial::updateTextureProviders(bool layoutChange)
QSGRhiShaderEffectNode::QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr)
: QSGShaderEffectNode(mgr),
- m_rc(rc),
m_mgr(mgr),
m_material(this)
{
+ Q_UNUSED(rc);
setFlag(UsePreprocess, true);
setMaterial(&m_material);
}
@@ -884,3 +884,5 @@ bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
}
QT_END_NAMESPACE
+
+#include "moc_qsgrhishadereffectnode_p.cpp"
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode_p.h b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
index 26460d24b2..3c382743ee 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode_p.h
+++ b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
@@ -139,7 +139,6 @@ private Q_SLOTS:
void handleTextureProviderDestroyed(QObject *object);
private:
- QSGDefaultRenderContext *m_rc;
QSGRhiGuiThreadShaderEffectManager *m_mgr;
QSGRhiShaderEffectMaterial m_material;
};
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index 89c9ea4d5b..c35d5b0705 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -233,7 +233,10 @@ void QSGRhiTextureGlyphCache::endFillTexture()
int QSGRhiTextureGlyphCache::glyphPadding() const
{
- return 1;
+ if (m_format == QFontEngine::Format_Mono)
+ return 8;
+ else
+ return 1;
}
int QSGRhiTextureGlyphCache::maxTextureWidth() const
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 1c72c4dba6..d47b0d72a5 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -547,17 +547,16 @@ void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
+ // The canvas nodes must be cleaned up regardless if we are in the destructor..
+ if (wipeSG) {
+ dd->cleanupNodesOnShutdown();
#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
#if QT_CONFIG(opengl)
- if (current)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+ if (current)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
#endif
-
- // The canvas nodes must be cleaned up regardless if we are in the destructor..
- if (wipeSG) {
- dd->cleanupNodesOnShutdown();
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup");
if (current && gl)
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 20d7c4557f..20e127c49f 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -256,12 +256,13 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
RLDEBUG("cleanup without an OpenGL context");
+ d->cleanupNodesOnShutdown();
+
#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
if (current)
QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
- d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
d->context->invalidate();
delete m_gl;
diff --git a/src/quick/scenegraph/shaders/outlinedtext.vert b/src/quick/scenegraph/shaders/outlinedtext.vert
index 9df832de3c..42fa577063 100644
--- a/src/quick/scenegraph/shaders/outlinedtext.vert
+++ b/src/quick/scenegraph/shaders/outlinedtext.vert
@@ -1,4 +1,5 @@
-uniform highp mat4 matrix;
+uniform highp mat4 modelViewMatrix;
+uniform highp mat4 projectionMatrix;
uniform highp vec2 textureScale;
uniform highp vec2 shift;
uniform highp float dpr;
@@ -19,6 +20,6 @@ void main()
sCoordDown = (tCoord - vec2(0.0, 1.0)) * textureScale;
sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * textureScale;
sCoordRight = (tCoord - vec2(1.0, 0.0)) * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
-} \ No newline at end of file
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+}
diff --git a/src/quick/scenegraph/shaders/outlinedtext_core.vert b/src/quick/scenegraph/shaders/outlinedtext_core.vert
index a854355460..50a1371e89 100644
--- a/src/quick/scenegraph/shaders/outlinedtext_core.vert
+++ b/src/quick/scenegraph/shaders/outlinedtext_core.vert
@@ -9,7 +9,8 @@ out vec2 sCoordDown;
out vec2 sCoordLeft;
out vec2 sCoordRight;
-uniform mat4 matrix;
+uniform mat4 modelViewMatrix;
+uniform mat4 projectionMatrix;
uniform vec2 textureScale;
uniform vec2 shift;
uniform float dpr;
@@ -21,6 +22,6 @@ void main()
sCoordDown = (tCoord - vec2(0.0, 1.0)) * textureScale;
sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * textureScale;
sCoordRight = (tCoord - vec2(1.0, 0.0)) * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
-} \ No newline at end of file
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+}
diff --git a/src/quick/scenegraph/shaders/styledtext.vert b/src/quick/scenegraph/shaders/styledtext.vert
index 29c9902609..dc87dadd5f 100644
--- a/src/quick/scenegraph/shaders/styledtext.vert
+++ b/src/quick/scenegraph/shaders/styledtext.vert
@@ -1,4 +1,5 @@
-uniform highp mat4 matrix;
+uniform highp mat4 modelViewMatrix;
+uniform highp mat4 projectionMatrix;
uniform highp vec2 textureScale;
uniform highp vec2 shift;
uniform highp float dpr;
@@ -13,6 +14,6 @@ void main()
{
sampleCoord = tCoord * textureScale;
shiftedSampleCoord = (tCoord - shift) * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders/styledtext_core.vert b/src/quick/scenegraph/shaders/styledtext_core.vert
index 04a0e88da8..d9a81bf06f 100644
--- a/src/quick/scenegraph/shaders/styledtext_core.vert
+++ b/src/quick/scenegraph/shaders/styledtext_core.vert
@@ -6,7 +6,8 @@ in vec2 tCoord;
out vec2 sampleCoord;
out vec2 shiftedSampleCoord;
-uniform mat4 matrix;
+uniform mat4 modelViewMatrix;
+uniform mat4 projectionMatrix;
uniform vec2 textureScale;
uniform vec2 shift;
uniform float dpr;
@@ -15,6 +16,6 @@ void main()
{
sampleCoord = tCoord * textureScale;
shiftedSampleCoord = (tCoord - shift) * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders/textmask.vert b/src/quick/scenegraph/shaders/textmask.vert
index 1692159d2c..7f418b0895 100644
--- a/src/quick/scenegraph/shaders/textmask.vert
+++ b/src/quick/scenegraph/shaders/textmask.vert
@@ -1,4 +1,5 @@
-uniform highp mat4 matrix;
+uniform highp mat4 modelViewMatrix;
+uniform highp mat4 projectionMatrix;
uniform highp vec2 textureScale;
uniform highp float dpr;
@@ -10,6 +11,6 @@ varying highp vec2 sampleCoord;
void main()
{
sampleCoord = tCoord * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders/textmask_core.vert b/src/quick/scenegraph/shaders/textmask_core.vert
index b0efc1e731..1f88974aed 100644
--- a/src/quick/scenegraph/shaders/textmask_core.vert
+++ b/src/quick/scenegraph/shaders/textmask_core.vert
@@ -5,13 +5,14 @@ in vec2 tCoord;
out vec2 sampleCoord;
-uniform mat4 matrix;
+uniform mat4 modelViewMatrix;
+uniform mat4 projectionMatrix;
uniform vec2 textureScale;
uniform float dpr;
void main()
{
sampleCoord = tCoord * textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * dpr + 0.5) / dpr;
- gl_Position = matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = modelViewMatrix * vCoord;
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
index bc3826a924..ed8da4cd30 100644
--- a/src/quick/scenegraph/shaders_ng/24bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
@@ -6,8 +6,9 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
- vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
+ vec4 color;
vec2 textureScale;
float dpr;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
index c493996375..6eee3f01d6 100644
--- a/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
index 63e445f90b..4198a4d339 100644
--- a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
@@ -6,8 +6,9 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
- vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
+ vec4 color;
vec2 textureScale;
float dpr;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
index 3f8489bfe6..d81bb2f26d 100644
--- a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
index 6304e821ff..a06743876d 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
index f721207325..6ebb3342ac 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
index 0d0fa1cd3a..f725cbc5e7 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
index 93ac0124be..e29f734c33 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
index 947d161a50..e2f82d3845 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
@@ -11,11 +11,12 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
index 1756ee9d4b..071abd2f88 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
index 023f9dfdc2..4068e42f28 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.vert
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
@@ -10,11 +10,12 @@ layout(location = 3) out vec2 sCoordLeft;
layout(location = 4) out vec2 sCoordRight;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
@@ -28,6 +29,6 @@ void main()
sCoordDown = (tCoord - vec2(0.0, 1.0)) * ubuf.textureScale;
sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * ubuf.textureScale;
sCoordRight = (tCoord - vec2(1.0, 0.0)) * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
index b8d38bdff4..dd159d008d 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
index 5b7bd9ca82..274d891a3c 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
@@ -11,11 +11,12 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
index f44b92dc28..393b1608e9 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag
index 0b16396037..2e380dfeae 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag
@@ -8,7 +8,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
index b0461a686c..5b45142201 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert b/src/quick/scenegraph/shaders_ng/styledtext.vert
index beadf07c79..271dae8d8a 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.vert
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert
@@ -7,7 +7,8 @@ layout(location = 0) out vec2 sampleCoord;
layout(location = 1) out vec2 shiftedSampleCoord;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
@@ -22,6 +23,6 @@ void main()
{
sampleCoord = tCoord * ubuf.textureScale;
shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
index 18e4685d21..e2a5859bfc 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
index b673137895..62e162c851 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
@@ -8,7 +8,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
index 3d9b5a0bdd..c3aa6c068a 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag
index 518d5c965f..ed8da4cd30 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.frag
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag.qsb b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
index cfae9575da..a4341d8915 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert b/src/quick/scenegraph/shaders_ng/textmask.vert
index 9d80d5dadb..e0b3c01bce 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.vert
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert
@@ -6,7 +6,8 @@ layout(location = 1) in vec2 tCoord;
layout(location = 0) out vec2 sampleCoord;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
@@ -17,6 +18,6 @@ out gl_PerVertex { vec4 gl_Position; };
void main()
{
sampleCoord = tCoord * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert.qsb b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
index 2ea425e1c0..4ca3b874d0 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
index f00918bb4e..7c3cf90325 100644
--- a/src/quick/scenegraph/util/qsgplaintexture.cpp
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -466,3 +466,5 @@ void QSGPlainTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch
}
QT_END_NAMESPACE
+
+#include "moc_qsgplaintexture_p.cpp"
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index e5e25d141b..4cf019c7f0 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1996,7 +1996,7 @@ void QQuickPropertyAnimationPrivate::convertVariant(QVariant &variant, int type)
}
QQuickBulkValueAnimator::QQuickBulkValueAnimator()
- : QAbstractAnimationJob(), animValue(nullptr), fromSourced(nullptr), m_duration(250)
+ : QAbstractAnimationJob(), animValue(nullptr), fromIsSourced(nullptr), m_duration(250)
{
}
@@ -2026,8 +2026,8 @@ void QQuickBulkValueAnimator::updateCurrentTime(int currentTime)
void QQuickBulkValueAnimator::topLevelAnimationLoopChanged()
{
//check for new from every top-level loop (when the top level animation is started and all subsequent loops)
- if (fromSourced)
- *fromSourced = false;
+ if (fromIsSourced)
+ *fromIsSourced = false;
QAbstractAnimationJob::topLevelAnimationLoopChanged();
}
@@ -2596,7 +2596,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
if (v == 1.) {
QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else {
- if (!fromSourced && !fromDefined) {
+ if (!fromIsSourced && !fromIsDefined) {
action.fromValue = action.property.read();
if (interpolatorType) {
QQuickPropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType);
@@ -2616,7 +2616,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
return;
}
wasDeleted = nullptr;
- fromSourced = true;
+ fromIsSourced = true;
}
void QQuickAnimationPropertyUpdater::debugUpdater(QDebug d, int indentLevel) const
@@ -2760,11 +2760,11 @@ QAbstractAnimationJob* QQuickPropertyAnimation::transition(QQuickStateActions &a
data->interpolatorType = d->interpolatorType;
data->interpolator = d->interpolator;
data->reverse = direction == Backward ? true : false;
- data->fromSourced = false;
- data->fromDefined = d->fromIsDefined;
+ data->fromIsSourced = false;
+ data->fromIsDefined = d->fromIsDefined;
data->actions = dataActions;
animator->setAnimValue(data);
- animator->setFromSourcedValue(&data->fromSourced);
+ animator->setFromIsSourcedValue(&data->fromIsSourced);
d->actions = &data->actions; //remove this?
}
diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h
index 8d23242b68..f785bf6623 100644
--- a/src/quick/util/qquickanimation_p_p.h
+++ b/src/quick/util/qquickanimation_p_p.h
@@ -135,7 +135,7 @@ public:
void setAnimValue(QQuickBulkValueUpdater *value);
QQuickBulkValueUpdater *getAnimValue() const { return animValue; }
- void setFromSourcedValue(bool *value) { fromSourced = value; }
+ void setFromIsSourcedValue(bool *value) { fromIsSourced = value; }
int duration() const override { return m_duration; }
void setDuration(int msecs) { m_duration = msecs; }
@@ -150,7 +150,7 @@ protected:
private:
QQuickBulkValueUpdater *animValue;
- bool *fromSourced;
+ bool *fromIsSourced;
int m_duration;
QEasingCurve easing;
};
@@ -311,7 +311,7 @@ public:
class Q_AUTOTEST_EXPORT QQuickAnimationPropertyUpdater : public QQuickBulkValueUpdater
{
public:
- QQuickAnimationPropertyUpdater() : interpolatorType(0), interpolator(nullptr), prevInterpolatorType(0), reverse(false), fromSourced(false), fromDefined(false), wasDeleted(nullptr) {}
+ QQuickAnimationPropertyUpdater() : interpolatorType(0), interpolator(nullptr), prevInterpolatorType(0), reverse(false), fromIsSourced(false), fromIsDefined(false), wasDeleted(nullptr) {}
~QQuickAnimationPropertyUpdater() override;
void setValue(qreal v) override;
@@ -323,8 +323,8 @@ public:
QVariantAnimation::Interpolator interpolator;
int prevInterpolatorType; //for generic
bool reverse;
- bool fromSourced;
- bool fromDefined;
+ bool fromIsSourced;
+ bool fromIsDefined;
bool *wasDeleted;
};
diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp
index d1ff78f8bc..e2a9c33adf 100644
--- a/src/quick/util/qquickanimator.cpp
+++ b/src/quick/util/qquickanimator.cpp
@@ -176,7 +176,7 @@ void QQuickAnimator::setTo(qreal to)
Q_D(QQuickAnimator);
if (to == d->to)
return;
- d->isToDefined = true;
+ d->toIsDefined = true;
d->to = to;
Q_EMIT toChanged(d->to);
}
@@ -204,7 +204,7 @@ void QQuickAnimator::setFrom(qreal from)
Q_D(QQuickAnimator);
if (from == d->from)
return;
- d->isFromDefined = true;
+ d->fromIsDefined = true;
d->from = from;
Q_EMIT fromChanged(d->from);
}
@@ -231,14 +231,14 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job,
job->setTarget(qobject_cast<QQuickItem *>(action.property.object()));
- if (isFromDefined)
+ if (fromIsDefined)
job->setFrom(from);
else if (action.fromValue.isValid())
job->setFrom(action.fromValue.toReal());
else
job->setFrom(action.property.read().toReal());
- if (isToDefined)
+ if (toIsDefined)
job->setTo(to);
else if (action.toValue.isValid())
job->setTo(action.toValue.toReal());
diff --git a/src/quick/util/qquickanimator_p_p.h b/src/quick/util/qquickanimator_p_p.h
index b176119c70..33e202c522 100644
--- a/src/quick/util/qquickanimator_p_p.h
+++ b/src/quick/util/qquickanimator_p_p.h
@@ -68,8 +68,8 @@ public:
, duration(250)
, from(0)
, to(0)
- , isFromDefined(false)
- , isToDefined(false)
+ , fromIsDefined(false)
+ , toIsDefined(false)
{
}
@@ -79,8 +79,8 @@ public:
qreal from;
qreal to;
- uint isFromDefined : 1;
- uint isToDefined : 1;
+ uint fromIsDefined : 1;
+ uint toIsDefined : 1;
void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified, QObject *defaultTarget);
};
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 2ae8a5a2aa..f027e9fafe 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -285,6 +285,17 @@ qreal QQuickAnimatorJob::progress(int time) const
return m_easing.valueForProgress((m_duration == 0) ? qreal(1) : qreal(time) / qreal(m_duration));
}
+void QQuickAnimatorJob::boundValue()
+{
+ qreal rangeMin = m_from;
+ qreal rangeMax = m_to;
+ if (m_from > m_to) {
+ rangeMax = m_from;
+ rangeMin = m_to;
+ }
+ m_value = qBound(rangeMin, m_value, rangeMax);
+}
+
qreal QQuickAnimatorJob::value() const
{
qreal value = m_to;
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index 522540bcbc..a31cd08032 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -122,10 +122,16 @@ public:
virtual void setTarget(QQuickItem *target);
QQuickItem *target() const { return m_target; }
- void setFrom(qreal from) { m_from = from; }
+ void setFrom(qreal from) {
+ m_from = from;
+ boundValue();
+ }
qreal from() const { return m_from; }
- void setTo(qreal to) { m_to = to; }
+ void setTo(qreal to) {
+ m_to = to;
+ boundValue();
+ }
qreal to() const { return m_to; }
void setDuration(int duration) { m_duration = duration; }
@@ -171,6 +177,7 @@ protected:
void debugAnimation(QDebug d) const override;
qreal progress(int time) const;
+ void boundValue();
QPointer<QQuickItem> m_target;
QQuickAnimatorController *m_controller;
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 74ee52b1d3..c39e1f5b8b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -107,12 +107,13 @@ QT_BEGIN_NAMESPACE
\li PathPolyline
\li Yes
\li Yes
+ \li No
\li Yes
- \li Yes
- \li PathMultiLine
- \li Yes
+ \row
+ \li PathMultiLine
\li Yes
\li Yes
+ \li No
\li Yes
\row
\li PathQuad
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index 1cb30f5a8d..7a47f132fe 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -239,18 +239,22 @@ public:
void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- error(compilationUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
- return;
- }
-
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
- || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ error(compilationUnit->objectAt(binding->value.objectIndex),
+ QQuickPropertyChanges::tr(
+ "PropertyChanges does not support creating state-specific objects."));
+ break;
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ case QV4::CompiledData::Binding::Type_AttachedProperty: {
const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
- for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
+ for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
verifyList(compilationUnit, subBinding);
- }
+ break;
+ }
+ default:
+ break;
}
}
@@ -273,8 +277,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QString propertyName = propertyPrefix + compilationUnit->stringAt(binding->propertyNameIndex);
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
- || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ case QV4::CompiledData::Binding::Type_AttachedProperty: {
QString pre = propertyName + QLatin1Char('.');
const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
@@ -283,6 +288,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
return;
}
+ default:
+ break;
+ }
if (propertyName.count() >= 3 &&
propertyName.at(0) == QLatin1Char('o') &&
@@ -299,7 +307,8 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
}
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Script
+ || binding->isTranslationBinding()) {
QUrl url = QUrl();
int line = -1;
int column = -1;
@@ -323,7 +332,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
QVariant var;
- switch (binding->type) {
+ switch (binding->type()) {
case QV4::CompiledData::Binding::Type_Script:
case QV4::CompiledData::Binding::Type_Translation:
case QV4::CompiledData::Binding::Type_TranslationById:
diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp
index 8e135b4624..fee8382766 100644
--- a/src/quick/util/qquickshortcut.cpp
+++ b/src/quick/util/qquickshortcut.cpp
@@ -44,6 +44,7 @@
#include <QtQuick/qquickrendercontrol.h>
#include <QtQuick/private/qtquickglobal_p.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtQml/qqmlinfo.h>
/*!
\qmltype Shortcut
@@ -124,13 +125,35 @@ Q_QUICK_PRIVATE_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher
QT_BEGIN_NAMESPACE
-static QKeySequence valueToKeySequence(const QVariant &value)
+static QKeySequence valueToKeySequence(const QVariant &value, const QQuickShortcut *const shortcut)
{
- if (value.userType() == QMetaType::Int)
- return QKeySequence(static_cast<QKeySequence::StandardKey>(value.toInt()));
+ if (value.userType() == QMetaType::Int) {
+ const QList<QKeySequence> s =
+ QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
+ if (s.size() > 1) {
+ const QString templateString = QString::fromUtf16(
+ u"Shortcut: Only binding to one of multiple key bindings associated with %1. "
+ u"Use 'sequences: [ <key> ]' to bind to all of them.");
+ qmlWarning(shortcut)
+ << templateString.arg(static_cast<QKeySequence::StandardKey>(value.toInt()));
+ }
+ return s.size() > 0 ? s[0] : QKeySequence {};
+ }
+
return QKeySequence::fromString(value.toString());
}
+static QList<QKeySequence> valueToKeySequences(const QVariant &value)
+{
+ if (value.userType() == QMetaType::Int) {
+ return QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
+ } else {
+ QList<QKeySequence> result;
+ result.push_back(QKeySequence::fromString(value.toString()));
+ return result;
+ }
+}
+
QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent),
m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut)
{
@@ -172,7 +195,7 @@ void QQuickShortcut::setSequence(const QVariant &value)
if (value == m_shortcut.userValue)
return;
- QKeySequence keySequence = valueToKeySequence(value);
+ QKeySequence keySequence = valueToKeySequence(value, this);
ungrabShortcut(m_shortcut);
m_shortcut.userValue = value;
@@ -207,28 +230,42 @@ QVariantList QQuickShortcut::sequences() const
void QQuickShortcut::setSequences(const QVariantList &values)
{
- QVector<Shortcut> remainder = m_shortcuts.mid(values.count());
- m_shortcuts.resize(values.count());
-
- bool changed = !remainder.isEmpty();
- for (int i = 0; i < values.count(); ++i) {
- const QVariant &value = values.at(i);
- Shortcut& shortcut = m_shortcuts[i];
- if (value == shortcut.userValue)
- continue;
-
- QKeySequence keySequence = valueToKeySequence(value);
-
- ungrabShortcut(shortcut);
- shortcut.userValue = value;
- shortcut.keySequence = keySequence;
- grabShortcut(shortcut, m_context);
+ // convert QVariantList to QVector<Shortcut>
+ QVector<Shortcut> requestedShortcuts;
+ for (const QVariant &v : values) {
+ const QList<QKeySequence> list = valueToKeySequences(v);
+ for (const QKeySequence &s : list) {
+ Shortcut sc;
+ sc.userValue = v;
+ sc.keySequence = s;
+ requestedShortcuts.push_back(sc);
+ }
+ }
- changed = true;
+ // if nothing has changed, just return:
+ if (m_shortcuts.size() == requestedShortcuts.size()) {
+ bool changed = false;
+ for (int i = 0; i < requestedShortcuts.count(); ++i) {
+ const Shortcut &requestedShortcut = requestedShortcuts[i];
+ const Shortcut &shortcut = m_shortcuts[i];
+ if (!(requestedShortcut.userValue == shortcut.userValue
+ && requestedShortcut.keySequence == shortcut.keySequence)) {
+ changed = true;
+ break;
+ }
+ }
+ if (!changed) {
+ return;
+ }
}
- if (changed)
- emit sequencesChanged();
+ for (Shortcut &s : m_shortcuts)
+ ungrabShortcut(s);
+ m_shortcuts = requestedShortcuts;
+ for (Shortcut &s : m_shortcuts)
+ grabShortcut(s, m_context);
+
+ emit sequencesChanged();
}
/*!
@@ -342,8 +379,15 @@ void QQuickShortcut::setContext(Qt::ShortcutContext context)
return;
ungrabShortcut(m_shortcut);
+ for (auto &s : m_shortcuts)
+ ungrabShortcut(s);
+
m_context = context;
+
grabShortcut(m_shortcut, context);
+ for (auto &s : m_shortcuts)
+ grabShortcut(s, context);
+
emit contextChanged();
}
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index 254b1af0a2..d511dc0562 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -569,4 +569,6 @@ void QQuickSmoothedAnimation::setMaximumEasingTime(int v)
QT_END_NAMESPACE
+#include "moc_qquicksmoothedanimation_p_p.cpp"
+
#include "moc_qquicksmoothedanimation_p.cpp"
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 71ab1f4d62..6a72754bde 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -635,6 +635,11 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
}
}
if (!found) {
+ // If revert list contains bindings assigned to deleted objects, we need to
+ // prevent reverting properties of those objects.
+ if (d->revertList.at(ii).binding() && !d->revertList.at(ii).property().object()) {
+ continue;
+ }
QVariant cur = d->revertList.at(ii).property().read();
QQmlPropertyPrivate::removeBinding(d->revertList.at(ii).property());
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index 2109aafc10..7cb3138618 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -376,7 +376,14 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when()) {
+ bool whenValue = state->when();
+ const QQmlProperty whenProp(state, QLatin1String("when"));
+ const auto potentialWhenBinding = QQmlPropertyPrivate::binding(whenProp);
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually reevaluate the binding
+ if (auto abstractBinding = dynamic_cast<QQmlBinding *>(potentialWhenBinding))
+ whenValue = abstractBinding->evaluate().toBool();
+ if (whenValue) {
if (stateChangeDebug())
qWarning() << "Setting auto state due to expression";
if (currentState != state->name()) {
diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp
index 660852ba83..a25af90414 100644
--- a/src/quick/util/qquickstyledtext.cpp
+++ b/src/quick/util/qquickstyledtext.cpp
@@ -46,6 +46,8 @@
#include "qquickstyledtext_p.h"
#include <QQmlContext>
+Q_LOGGING_CATEGORY(lcStyledText, "qt.quick.styledtext")
+
/*
QQuickStyledText supports few tags:
@@ -562,10 +564,14 @@ void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textI
textOut += QChar(60);
else if (entity == QLatin1String("amp"))
textOut += QChar(38);
+ else if (entity == QLatin1String("apos"))
+ textOut += QChar(39);
else if (entity == QLatin1String("quot"))
textOut += QChar(34);
else if (entity == QLatin1String("nbsp"))
textOut += QChar(QChar::Nbsp);
+ else
+ qCWarning(lcStyledText) << "StyledText doesn't support entity" << entity;
return;
} else if (*ch == QLatin1Char(' ')) {
QStringRef entity(&textIn, entityStart - 1, entityLength + 1);
diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp
index 7ec7c827eb..949724e87c 100644
--- a/src/quick/util/qquicktimeline.cpp
+++ b/src/quick/util/qquicktimeline.cpp
@@ -53,6 +53,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTl, "qt.quick.timeline")
+
struct Update {
Update(QQuickTimeLineValue *_g, qreal _v)
: g(_g), v(_v) {}
@@ -513,6 +515,7 @@ void QQuickTimeLine::reset(QQuickTimeLineValue &timeLineValue)
qWarning() << "QQuickTimeLine: Cannot reset a QQuickTimeLineValue owned by another timeline.";
return;
}
+ qCDebug(lcTl) << static_cast<QObject*>(this) << timeLineValue.value();
remove(&timeLineValue);
timeLineValue._t = nullptr;
}
@@ -954,3 +957,5 @@ QQuickTimeLineObject *QQuickTimeLineCallback::callbackObject() const
}
QT_END_NAMESPACE
+
+#include "moc_qquicktimeline_p_p.cpp"
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 8f2ef3cfc1..f9c0968c7e 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -763,6 +763,13 @@ void QQuickShape::setAsynchronous(bool async)
performance. Setting the value to \c true is safe in any case since
rendering falls back to the default method when the vendor-specific
approach, such as \c GL_NV_path_rendering, is not supported at run time.
+
+ \deprecated
+
+ Changing the default value (false) is not recommended. In particular,
+ support for Shape.NvprRenderer will not be available in future Qt versions.
+ Applications experiencing rendering problems with the property set to true
+ are advised to set it to false.
*/
bool QQuickShape::vendorExtensionsEnabled() const
@@ -1515,6 +1522,7 @@ static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradie
{
int pos = 0;
const QGradientStops &s = gradient.stops;
+ Q_ASSERT(!s.isEmpty());
const bool colorInterpolation = true;
uint alpha = qRound(opacity * 256);
@@ -1552,8 +1560,6 @@ static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradie
current_color = next_color;
}
- Q_ASSERT(s.size() > 0);
-
uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
for ( ; pos < size; ++pos)
colorTable[pos] = last_color;
@@ -1588,7 +1594,10 @@ QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &gra
if (!tx) {
static const int W = 1024; // texture size is 1024x1
QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
- generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
+ if (!grad.stops.isEmpty())
+ generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
+ else
+ gradTab.fill(Qt::black);
tx = new QSGPlainTexture;
tx->setImage(gradTab);
switch (grad.spread) {
diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index e9bc715952..98170be6d9 100644
--- a/src/quickshapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -188,8 +188,12 @@ void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path)
void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color)
{
ShapePathData &d(m_sp[index]);
+ const bool wasTransparent = d.strokeColor.a == 0;
d.strokeColor = colorToColor4ub(color);
+ const bool isTransparent = d.strokeColor.a == 0;
d.syncDirty |= DirtyColor;
+ if (wasTransparent && !isTransparent)
+ d.syncDirty |= DirtyStrokeGeom;
}
void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w)
@@ -204,8 +208,12 @@ void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w)
void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color)
{
ShapePathData &d(m_sp[index]);
+ const bool wasTransparent = d.fillColor.a == 0;
d.fillColor = colorToColor4ub(color);
+ const bool isTransparent = d.fillColor.a == 0;
d.syncDirty |= DirtyColor;
+ if (wasTransparent && !isTransparent)
+ d.syncDirty |= DirtyFillGeom;
}
void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
@@ -1276,3 +1284,5 @@ QSGMaterialShader *QQuickShapeConicalGradientMaterial::createShader() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickshapegenericrenderer_p.cpp"
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 39780f8de3..cf021d9a7c 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -978,11 +978,14 @@ void QQuickWidget::createFramebufferObject()
}
QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext();
+ bool nativeContextGotRecreated = false;
if (shareWindowContext && context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
+ d->invalidateRenderControl();
context->setShareContext(shareWindowContext);
context->setScreen(shareWindowContext->screen());
if (!context->create())
qWarning("QQuickWidget: Failed to recreate context");
+ nativeContextGotRecreated = true;
// The screen may be different so we must recreate the offscreen surface too.
// Unlike QOpenGLContext, QOffscreenSurface's create() does not recreate so have to destroy() first.
d->offscreenSurface->destroy();
@@ -1042,6 +1045,10 @@ void QQuickWidget::createFramebufferObject()
// Having one would mean create() was called and platforms that only support
// a single native window were in trouble.
Q_ASSERT(!d->offscreenWindow->handle());
+
+ // do this at the end it may trigger a recursive call
+ if (nativeContextGotRecreated)
+ d->renderControl->initialize(context);
#endif
}
diff --git a/tests/auto/cmake/qtquickcompiler/main.cpp b/tests/auto/cmake/qtquickcompiler/main.cpp
index c357ef60e6..680f81755d 100644
--- a/tests/auto/cmake/qtquickcompiler/main.cpp
+++ b/tests/auto/cmake/qtquickcompiler/main.cpp
@@ -13,7 +13,7 @@ private slots:
void tst_QQC::packaging()
{
QVERIFY(QFile::exists(":/main.qml"));
- QCOMPARE(QFileInfo(":/main.qml").size(), 0);
+ QVERIFY(QFileInfo(":/main.qml").size() > 0);
QVERIFY(QFile::exists(":/main.cpp"));
QVERIFY(QFileInfo(":/main.cpp").size() > 0);
diff --git a/tests/auto/particles/qquickitemparticle/data/takeGive.qml b/tests/auto/particles/qquickitemparticle/data/takeGive.qml
index e95ae738bd..926d83bfc1 100644
--- a/tests/auto/particles/qquickitemparticle/data/takeGive.qml
+++ b/tests/auto/particles/qquickitemparticle/data/takeGive.qml
@@ -65,9 +65,10 @@ Rectangle {
}
Emitter{
+ objectName: "emitter"
//0,0 position
size: 32
- emitRate: 1000
+ emitRate: 0
lifeSpan: Emitter.InfiniteLife
}
}
diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
index 28ebbb3c05..538c429d1a 100644
--- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
+++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
#include <private/qquickparticlesystem_p.h>
+#include <private/qquickparticleemitter_p.h>
#include <private/qquickimage_p.h>
#include <private/qabstractanimation_p.h>
@@ -114,11 +115,14 @@ void tst_qquickitemparticle::test_takeGive()
{
QQuickView* view = createView(testFileUrl("takeGive.qml"), 500);
QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system");
+ QQuickParticleEmitter* emitter = view->rootObject()->findChild<QQuickParticleEmitter*>("emitter");
QMetaObject::invokeMethod(view->rootObject(), "takeItems");
+ emitter->burst(100);
ensureAnimTime(1000, system->m_animation);
QVERIFY(system->property("acc").toInt() == 100);
QMetaObject::invokeMethod(view->rootObject(), "giveItems");
QTRY_VERIFY(system->property("acc").toInt() == 0);
+ QTRY_VERIFY(system->isEmpty() == true);
delete view;
}
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST
new file mode 100644
index 0000000000..3793debebb
--- /dev/null
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST
@@ -0,0 +1,3 @@
+# See qtbase/src/testlib/qtestblacklist.cpp for format
+[deleteChildrenWithRunningGroup]
+ci macos # QTBUG-106356
diff --git a/tests/auto/qml/animation/qpauseanimationjob/BLACKLIST b/tests/auto/qml/animation/qpauseanimationjob/BLACKLIST
new file mode 100644
index 0000000000..33799b6528
--- /dev/null
+++ b/tests/auto/qml/animation/qpauseanimationjob/BLACKLIST
@@ -0,0 +1,3 @@
+[multipleSequentialGroups]
+macos ci
+
diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
index 57b0905a8a..150734496e 100644
--- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
@@ -68,6 +68,7 @@ private slots:
void insertAnimation();
void clear();
void pauseResume();
+ void deleteFromListener();
};
void tst_QSequentialAnimationGroupJob::initTestCase()
@@ -126,15 +127,23 @@ protected:
class StateChangeListener: public QAnimationJobChangeListener
{
public:
- virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
+ virtual void animationStateChanged(
+ QAbstractAnimationJob *job, QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State)
{
states << newState;
+ if (beEvil) {
+ delete job->group();
+ groupDeleted = true;
+ }
}
void clear() { states.clear(); }
int count() const { return states.count(); }
QList<QAbstractAnimationJob::State> states;
+ bool beEvil = false;
+ bool groupDeleted = false;
};
class FinishedListener: public QAnimationJobChangeListener
@@ -1644,6 +1653,37 @@ void tst_QSequentialAnimationGroupJob::uncontrolledWithLoops()
QTRY_COMPARE(group.state(), QAbstractAnimationJob::Stopped);
}
+void tst_QSequentialAnimationGroupJob::deleteFromListener()
+{
+ QSequentialAnimationGroupJob *group = new QSequentialAnimationGroupJob;
+
+ UncontrolledAnimation *uncontrolled = new UncontrolledAnimation();
+ TestAnimation *shortLoop = new TestAnimation(100);
+ UncontrolledAnimation *more = new UncontrolledAnimation();
+
+ shortLoop->setLoopCount(-1);
+
+ group->appendAnimation(uncontrolled);
+ group->appendAnimation(shortLoop);
+ group->appendAnimation(more);
+
+ StateChangeListener listener;
+ listener.beEvil = true;
+ shortLoop->addAnimationChangeListener(&listener, QAbstractAnimationJob::StateChange);
+ group->setLoopCount(2);
+
+ group->start();
+
+ QCOMPARE(group->currentLoop(), 0);
+ QCOMPARE(group->state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(uncontrolled->state(), QAbstractAnimationJob::Running);
+
+ QVERIFY(!listener.groupDeleted);
+ uncontrolled->stop();
+
+ QTRY_VERIFY(listener.groupDeleted);
+ // It's dead, Jim.
+}
QTEST_MAIN(tst_QSequentialAnimationGroupJob)
#include "tst_qsequentialanimationgroupjob.moc"
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml
new file mode 100644
index 0000000000..c3e7687831
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml
@@ -0,0 +1,15 @@
+import QtQml 2.15
+Timer {
+ Component.onCompleted: {
+ console.log('0')
+ console.log('1')
+ console.log('2')
+ console.log('3')
+ console.log('4')
+ console.log('5')
+ running = true
+ }
+
+ interval: 0
+ onTriggered: Qt.quit()
+}
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml
new file mode 100644
index 0000000000..1715992490
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml
@@ -0,0 +1,16 @@
+import QtQml 2.15
+
+Timer {
+ Component.onCompleted: {
+ var a = 97
+ var b = 98
+ var c = 99
+ let d = 100
+ const e = 101
+ console.log("onClicked") // Set breakpoint
+ running = true
+ }
+
+ interval: 0
+ onTriggered: Qt.quit()
+}
diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
index 91470e0651..43c81ee515 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -65,6 +65,8 @@ const char *STEPACTION_QMLFILE = "stepAction.qml";
const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml";
const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml";
const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml";
+const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml";
+const char *LETCONSTLOCALS_QMLFILE = "letConstLocals.qml";
#undef QVERIFY
#define QVERIFY(statement) \
@@ -156,6 +158,9 @@ private slots:
void encodeQmlScope();
void breakOnAnchor();
+ void breakPointIds();
+ void letConstLocals();
+
private:
ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE),
bool blockMode = true, bool restrictServices = false);
@@ -165,10 +170,11 @@ private:
void targetData();
bool waitForClientSignal(const char *signal, int timeout = 30000);
void checkVersionParameters();
+ int setBreakPoint(const QString &file, int sourceLine, bool enabled);
+ void clearBreakPoint(int id);
};
-
void tst_QQmlDebugJS::initTestCase()
{
QQmlDebugTest::initTestCase();
@@ -566,7 +572,8 @@ void tst_QQmlDebugJS::changeBreakpoint()
int sourceLine2 = 37;
int sourceLine1 = 38;
- QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess);
+ const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE);
+ QCOMPARE(init(qmlscene, file), ConnectSuccess);
bool isStopped = false;
QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; });
@@ -589,27 +596,13 @@ void tst_QQmlDebugJS::changeBreakpoint()
return breakpointsHit[0].toInt();
};
- auto setBreakPoint = [&](int sourceLine, bool enabled) {
- int id = -1;
- auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() {
- id = extractBody().value("breakpoint").toInt();
- });
-
- m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine, -1, enabled);
- bool success = QTest::qWaitFor([&]() { return id >= 0; });
- Q_UNUSED(success);
-
- QObject::disconnect(connection);
- return id;
- };
-
//The breakpoints are in a timer loop so we can set them after connect().
//Furthermore the breakpoints should be hit in the right order because setting of breakpoints
//can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below)
- const int breakpoint1 = setBreakPoint(sourceLine1, false);
+ const int breakpoint1 = setBreakPoint(file, sourceLine1, false);
QVERIFY(breakpoint1 >= 0);
- const int breakpoint2 = setBreakPoint(sourceLine2, true);
+ const int breakpoint2 = setBreakPoint(file, sourceLine2, true);
QVERIFY(breakpoint2 >= 0);
auto verifyBreakpoint = [&](int sourceLine, int breakpointId) {
@@ -1026,6 +1019,96 @@ void tst_QQmlDebugJS::breakOnAnchor()
QCOMPARE(breaks, 2);
}
+void tst_QQmlDebugJS::breakPointIds()
+{
+ QString file(BREAKPOINTIDS_QMLFILE);
+ QCOMPARE(init(true, file), ConnectSuccess);
+
+ int breaks = 0;
+ int breakPointIds[] = { -1, -1, -1, -1, -1, -1};
+
+ QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() {
+ const QJsonObject body = m_client->response().body.toObject();
+ QCOMPARE(body.value("sourceLine").toInt(), breaks + 4);
+ const QJsonArray breakpointsHit = body.value("breakpoints").toArray();
+ QVERIFY(breakpointsHit.size() > 0);
+ QCOMPARE(breakpointsHit[0].toInt(), breakPointIds[breaks]);
+ ++breaks;
+ m_client->continueDebugging(QV4DebugClient::Continue);
+ });
+
+ for (int i = 0; i < 6; ++i)
+ breakPointIds[i] = setBreakPoint(file, i + 4, true);
+
+ clearBreakPoint(breakPointIds[2]);
+ breakPointIds[2] = setBreakPoint(file, 6, true);
+
+ QTRY_COMPARE(m_process->state(), QProcess::Running);
+ m_client->connect();
+
+ QTRY_COMPARE(m_process->state(), QProcess::NotRunning);
+ QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
+
+ QCOMPARE(breaks, 6);
+}
+
+void tst_QQmlDebugJS::letConstLocals()
+{
+ QString file(LETCONSTLOCALS_QMLFILE);
+ QCOMPARE(init(true, file), ConnectSuccess);
+
+ QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() {
+ m_client->frame();
+ });
+
+ int numScopes = 0;
+ QString expectedMembers = QStringLiteral("abcde");
+ QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() {
+ const auto value = m_client->response();
+ if (value.command == QStringLiteral("frame")) {
+ const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray();
+ for (const auto &scope : scopes) {
+ const auto scopeObject = scope.toObject();
+ const int type = scopeObject.value("type").toInt();
+ if (type == 1 || type == 4) {
+ m_client->scope(scopeObject.value("index").toInt());
+ ++numScopes;
+ }
+ }
+ QVERIFY(numScopes > 0);
+ } else if (value.command == QStringLiteral("scope")) {
+ const auto props = value.body.toObject().value(QStringLiteral("object")).toObject()
+ .value(QStringLiteral("properties")).toArray();
+ for (const auto &prop : props) {
+ const auto propObj = prop.toObject();
+ const QString name = propObj.value(QStringLiteral("name")).toString();
+ if (name == QStringLiteral("onCompleted"))
+ continue;
+ QVERIFY(name.length() == 1);
+ auto i = expectedMembers.indexOf(name.at(0));
+ QVERIFY(i != -1);
+ expectedMembers.remove(i, 1);
+ QCOMPARE(propObj.value(QStringLiteral("type")).toString(),
+ QStringLiteral("number"));
+ QCOMPARE(propObj.value(QStringLiteral("value")).toInt(),
+ int(name.at(0).toLatin1()));
+ }
+ if (--numScopes == 0) {
+ QVERIFY(expectedMembers.isEmpty());
+ m_client->continueDebugging(QV4DebugClient::Continue);
+ }
+ }
+ });
+
+ setBreakPoint(file, 10, true);
+
+ QTRY_COMPARE(m_process->state(), QProcess::Running);
+ m_client->connect();
+
+ QTRY_COMPARE(m_process->state(), QProcess::NotRunning);
+ QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
+}
+
QList<QQmlDebugClient *> tst_QQmlDebugJS::createClients()
{
m_client = new QV4DebugClient(m_connection);
@@ -1054,6 +1137,35 @@ void tst_QQmlDebugJS::checkVersionParameters()
QCOMPARE(body.value("ChangeBreakpoint").toBool(), true);
}
+int tst_QQmlDebugJS::setBreakPoint(const QString &file, int sourceLine, bool enabled)
+{
+ int id = -1;
+ auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() {
+ id = m_client->response().body.toObject().value("breakpoint").toInt();
+ });
+
+ m_client->setBreakpoint(file, sourceLine, -1, enabled);
+ bool success = QTest::qWaitFor([&]() { return id >= 0; });
+ Q_UNUSED(success);
+
+ QObject::disconnect(connection);
+ return id;
+}
+
+void tst_QQmlDebugJS::clearBreakPoint(int id)
+{
+ bool ok = false;
+ auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() {
+ ok = true;
+ });
+
+ m_client->clearBreakpoint(id);
+ bool success = QTest::qWaitFor([&]() { return ok; });
+ Q_UNUSED(success);
+
+ QObject::disconnect(connection);
+}
+
QTEST_MAIN(tst_QQmlDebugJS)
#include "tst_qqmldebugjs.moc"
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index e3cbeb9891..e4e7728508 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -575,21 +575,29 @@ void tst_qv4debugger::readLocals()
QString script =
"var f = function(a, b) {\n"
" var c = a + b\n"
+ " let e = 'jaja'\n"
+ " const ff = 'nenene'\n"
" var d = a - b\n" // breakpoint, c should be set, d should be undefined
" return c === d\n"
"}\n"
"f(1, 2, 3);\n";
- debugger()->addBreakPoint("readLocals", 3);
+ debugger()->addBreakPoint("readLocals", 5);
evaluateJavaScript(script, "readLocals");
QVERIFY(m_debuggerAgent->m_wasPaused);
QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1);
const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0);
- QCOMPARE(frame0.size(), 5); // locals and parameters
+ QCOMPARE(frame0.size(), 7); // locals and parameters
QVERIFY(frame0.contains("c"));
QCOMPARE(frame0.type("c"), QStringLiteral("number"));
QCOMPARE(frame0.value("c").toDouble(), 3.0);
QVERIFY(frame0.contains("d"));
QCOMPARE(frame0.type("d"), QStringLiteral("undefined"));
+ QVERIFY(frame0.contains("e"));
+ QCOMPARE(frame0.type("e"), QStringLiteral("string"));
+ QCOMPARE(frame0.value("e").toString(), QStringLiteral("jaja"));
+ QVERIFY(frame0.contains("ff"));
+ QCOMPARE(frame0.type("ff"), QStringLiteral("string"));
+ QCOMPARE(frame0.value("ff").toString(), QStringLiteral("nenene"));
}
void tst_qv4debugger::readObject()
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
index 9fe2de5368..2b6145030b 100644
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
@@ -393,9 +393,7 @@ void Test262Runner::loadTestExpectations()
return;
}
- int line = 0;
while (!file.atEnd()) {
- ++line;
QByteArray line = file.readLine().trimmed();
if (line.startsWith('#') || line.isEmpty())
continue;
@@ -440,9 +438,7 @@ void Test262Runner::updateTestExpectations()
QTemporaryFile updatedExpectations;
updatedExpectations.open();
- int line = 0;
while (!file.atEnd()) {
- ++line;
QByteArray originalLine = file.readLine();
QByteArray line = originalLine.trimmed();
if (line.startsWith('#') || line.isEmpty()) {
diff --git a/tests/auto/qml/ecmascripttests/testcase.pro b/tests/auto/qml/ecmascripttests/testcase.pro
index 5bf7ecd696..3af1adf9c9 100644
--- a/tests/auto/qml/ecmascripttests/testcase.pro
+++ b/tests/auto/qml/ecmascripttests/testcase.pro
@@ -11,5 +11,5 @@ DEFINES += SRCDIR=\\\"$$PWD\\\"
# test some of the time. Fix by raising time-out to 400s when
# invoking tst_ecmascripttests:
checkenv.name = QTEST_FUNCTION_TIMEOUT
-checkenv.value = 500000
+checkenv.value = 1000000
QT_TOOL_ENV += checkenv
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 3b7d74df63..f1c34e6142 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -102,6 +102,7 @@ private slots:
void valueConversion_RegularExpression();
void castWithMultipleInheritance();
void collectGarbage();
+ void collectGarbageNestedWrappersTwoEngines();
void gcWithNestedDataStructure();
void stacktrace();
void numberParsing_data();
@@ -259,6 +260,7 @@ private slots:
void sortNonStringArray();
void iterateInvalidProxy();
void applyOnHugeArray();
+ void reflectApplyOnHugeArray();
void tostringRecursionCheck();
void arrayIncludesWithLargeArray();
@@ -267,6 +269,9 @@ private slots:
void dataViewCtor();
void uiLanguage();
+ void forOfAndGc();
+
+ void spreadNoOverflow();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
@@ -1809,6 +1814,44 @@ void tst_QJSEngine::collectGarbage()
QVERIFY(ptr.isNull());
}
+class TestObjectContainer : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject *dummy MEMBER m_dummy CONSTANT)
+
+public:
+ TestObjectContainer() : m_dummy(new QObject(this)) {}
+
+private:
+ QObject *m_dummy;
+};
+
+void tst_QJSEngine::collectGarbageNestedWrappersTwoEngines()
+{
+ QJSEngine engine1;
+ QJSEngine engine2;
+
+ TestObjectContainer container;
+ QQmlEngine::setObjectOwnership(&container, QQmlEngine::CppOwnership);
+
+ engine1.globalObject().setProperty("foobar", engine1.newQObject(&container));
+ engine2.globalObject().setProperty("foobar", engine2.newQObject(&container));
+
+ engine1.evaluate("foobar.dummy.baz = 42");
+ engine2.evaluate("foobar.dummy.baz = 43");
+
+ QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
+ QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
+
+ engine1.collectGarbage();
+ engine2.collectGarbage();
+
+ // The GC should not collect dummy object wrappers neither in engine1 nor engine2, we
+ // verify that by checking whether the baz property still has its previous value.
+ QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
+ QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
+}
+
void tst_QJSEngine::gcWithNestedDataStructure()
{
// The GC must be able to traverse deeply nested objects, otherwise this
@@ -5134,6 +5177,22 @@ void tst_QJSEngine::applyOnHugeArray()
QCOMPARE(value.toString(), "RangeError: Array too large for apply().");
}
+
+void tst_QJSEngine::reflectApplyOnHugeArray()
+{
+ QQmlEngine engine;
+ const QJSValue value = engine.evaluate(R"(
+(function(){
+const v1 = [];
+const v3 = [];
+v3.length = 3900000000;
+Reflect.apply(v1.reverse,v1,v3);
+})()
+ )");
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), QLatin1String("RangeError: Invalid array length."));
+}
+
void tst_QJSEngine::typedArraySet()
{
QJSEngine engine;
@@ -5211,6 +5270,87 @@ void tst_QJSEngine::uiLanguage()
}
}
+void tst_QJSEngine::forOfAndGc()
+{
+ // We want to guard against the iterator of a for..of loop leaving the result unprotected from
+ // garbage collection. It should be possible to construct a pure JS test case, but due to the
+ // vaguaries of garbage collection it's hard to reliably trigger the crash. This test is the
+ // best I could come up with.
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(R"(
+ import QtQml 2.15
+ import QtQml.Models 2.15
+
+ QtObject {
+ id: counter
+ property int count: 0
+
+ property DelegateModel model: DelegateModel {
+ id: filesModel
+
+ model: ListModel {
+ Component.onCompleted: {
+ for (let idx = 0; idx < 50; idx++)
+ append({"i" : idx})
+ }
+ }
+
+ groups: [
+ DelegateModelGroup {
+ name: "selected"
+ }
+ ]
+
+ function getSelected() {
+ for (let i = 0; i < items.count; ++i) {
+ var item = items.get(i)
+ for (let el of item.groups) {
+ if (el === "selected")
+ ++counter.count
+ }
+ }
+ }
+
+ property bool bSelect: true
+ function selectAll() {
+ for (let i = 0; i < items.count; ++i) {
+ if (bSelect && !items.get(i).inSelected)
+ items.addGroups(i, 1, ["selected"])
+ else
+ items.removeGroups(i, 1, ["selected"])
+ getSelected()
+ }
+ bSelect = !bSelect
+ }
+ }
+
+ property Timer timer: Timer {
+ running: true
+ interval: 1
+ repeat: true
+ onTriggered: filesModel.selectAll()
+ }
+ }
+ )", QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QTRY_VERIFY(o->property("count").toInt() > 32768);
+}
+
+void tst_QJSEngine::spreadNoOverflow()
+{
+ QJSEngine engine;
+
+ const QString program = QString::fromLatin1("var a = [] ;a.length = 555840;Math.max(...a)");
+ const QJSValue result = engine.evaluate(program);
+ QVERIFY(result.isError());
+ QCOMPARE(result.errorType(), QJSValue::RangeError);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qmlcachegen/data/posthocrequired.qml b/tests/auto/qml/qmlcachegen/data/posthocrequired.qml
new file mode 100644
index 0000000000..3b32d9fe8f
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/posthocrequired.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ required x
+}
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index c5602eda7d..65137c65a2 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -37,6 +37,7 @@
#include <QLoggingCategory>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlscriptdata_p.h>
+#include <private/qv4compileddata_p.h>
#include <qtranslator.h>
#include "../../shared/util.h"
@@ -77,6 +78,9 @@ private slots:
void parameterAdjustment();
void inlineComponent();
+ void posthocRequired();
+
+ void saveableUnitPointer();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -701,6 +705,29 @@ void tst_qmlcachegen::inlineComponent()
QVERIFY(!obj.isNull());
}
+void tst_qmlcachegen::posthocRequired()
+{
+ bool ok = generateCache(testFile("posthocrequired.qml"));
+ QVERIFY(ok);
+ QQmlEngine engine;
+ CleanlyLoadingComponent component(&engine, testFileUrl("posthocrequired.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj.isNull() && component.isError());
+ QVERIFY(component.errorString().contains(QStringLiteral("Required property x was not initialized")));
+}
+
+void tst_qmlcachegen::saveableUnitPointer()
+{
+ QV4::CompiledData::Unit unit;
+ unit.flags = QV4::CompiledData::Unit::StaticData | QV4::CompiledData::Unit::IsJavascript;
+ const auto flags = unit.flags;
+
+ QV4::CompiledData::SaveableUnitPointer pointer(&unit);
+
+ QVERIFY(pointer.saveToDisk<char>([](const char *, quint32) { return true; }));
+ QCOMPARE(unit.flags, flags);
+}
+
QTEST_GUILESS_MAIN(tst_qmlcachegen)
#include "tst_qmlcachegen.moc"
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index d0c8390a74..773b826e62 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -64,6 +64,7 @@ private slots:
void singletonDependency();
void cppRegisteredSingletonDependency();
void cacheModuleScripts();
+ void reuseStaticMappings();
private:
QDir m_qmlCacheDirectory;
@@ -127,34 +128,43 @@ struct TestCompiler
mappedFile.setFileName(cacheFilePath);
}
- bool compile(const QByteArray &contents)
+ void reset()
{
closeMapping();
engine->clearComponentCache();
-
waitForFileSystem();
+ }
- {
- QFile f(testFilePath);
- if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- lastErrorString = f.errorString();
- return false;
- }
- if (f.write(contents) != contents.size()) {
- lastErrorString = f.errorString();
- return false;
- }
+ bool writeTestFile(const QByteArray &contents)
+ {
+ QFile f(testFilePath);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ lastErrorString = f.errorString();
+ return false;
+ }
+ if (f.write(contents) != contents.size()) {
+ lastErrorString = f.errorString();
+ return false;
}
+ return true;
+ }
+ bool loadTestFile()
+ {
CleanlyLoadingComponent component(engine, testFilePath);
if (!component.isReady()) {
lastErrorString = component.errorString();
return false;
}
-
return true;
}
+ bool compile(const QByteArray &contents)
+ {
+ reset();
+ return writeTestFile(contents) && loadTestFile();
+ }
+
const QV4::CompiledData::Unit *mapUnit()
{
if (!mappedFile.open(QIODevice::ReadOnly)) {
@@ -173,29 +183,62 @@ struct TestCompiler
}
typedef void (*HeaderTweakFunction)(QV4::CompiledData::Unit *header);
- bool tweakHeader(HeaderTweakFunction function)
+ bool tweakHeader(HeaderTweakFunction function, const QString &newName)
{
closeMapping();
- QFile f(cacheFilePath);
- if (!f.open(QIODevice::ReadWrite))
+ const QString targetTestFilePath = tempDir.path() + "/" + newName;
+
+ {
+ QFile testFile(testFilePath);
+ if (!testFile.copy(targetTestFilePath))
+ return false;
+ }
+
+ const QString targetCacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(targetTestFilePath));
+
+ QFile source(cacheFilePath);
+ if (!source.copy(targetCacheFilePath))
return false;
+
+ if (!source.open(QIODevice::ReadOnly))
+ return false;
+
+ QFile target(targetCacheFilePath);
+ if (!target.open(QIODevice::WriteOnly))
+ return false;
+
QV4::CompiledData::Unit header;
- if (f.read(reinterpret_cast<char *>(&header), sizeof(header)) != sizeof(header))
+ if (source.read(reinterpret_cast<char *>(&header), sizeof(header)) != sizeof(header))
return false;
function(&header);
- f.seek(0);
- return f.write(reinterpret_cast<const char *>(&header), sizeof(header)) == sizeof(header);
+
+ return target.write(reinterpret_cast<const char *>(&header), sizeof(header))
+ == sizeof(header);
}
- bool verify()
+ bool verify(const QString &fileName = QString())
+ {
+ const QString path = fileName.isEmpty() ? testFilePath : tempDir.path() + "/" + fileName;
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
+ return unit->loadFromDisk(QUrl::fromLocalFile(path),
+ QFileInfo(path).lastModified(), &lastErrorString);
+ }
+
+ quintptr unitData()
{
QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
= QV4::ExecutableCompilationUnit::create();
return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath),
- QFileInfo(testFilePath).lastModified(), &lastErrorString);
+ QFileInfo(testFilePath).lastModified(), &lastErrorString)
+ ? quintptr(unit->unitData())
+ : 0;
}
+
void closeMapping()
{
if (currentMapping) {
@@ -205,10 +248,11 @@ struct TestCompiler
mappedFile.close();
}
- void clearCache()
+ void clearCache(const QString &fileName = QString())
{
+ const QString path = fileName.isEmpty() ? testFilePath : tempDir.path() + "/" + fileName;
closeMapping();
- QFile::remove(cacheFilePath);
+ QFile::remove(path);
}
QQmlEngine *engine;
@@ -302,7 +346,7 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(1));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Script);
QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0));
QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
@@ -330,7 +374,7 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(2));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number);
QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants())
[obj->bindingTable()->value.constantValueIndex].doubleValue(),
@@ -375,7 +419,7 @@ void tst_qmldiskcache::registerImportForImplicitComponent()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(1));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Object);
const QV4::CompiledData::Object *implicitComponent = qmlUnit->objectAt(obj->bindingTable()->value.objectIndex);
QCOMPARE(testUnit->stringAtInternal(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType.elementName());
@@ -404,25 +448,28 @@ void tst_qmldiskcache::basicVersionChecks()
testCompiler.clearCache();
QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
- testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ const QString qtVersionFile = QStringLiteral("qtversion.qml");
+ QVERIFY(testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
header->qtVersion = 0;
- });
+ }, qtVersionFile));
- QVERIFY(!testCompiler.verify());
+ QVERIFY(!testCompiler.verify(qtVersionFile));
QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Qt version mismatch. Found 0 expected %1").arg(QT_VERSION, 0, 16));
- testCompiler.clearCache();
+ testCompiler.clearCache(qtVersionFile);
}
{
testCompiler.clearCache();
QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
- testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ const QString versionFile = QStringLiteral("version.qml");
+ QVERIFY(testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
header->version = 0;
- });
+ }, versionFile));
- QVERIFY(!testCompiler.verify());
+ QVERIFY(!testCompiler.verify(versionFile));
QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16));
+ testCompiler.clearCache(versionFile);
}
}
@@ -963,6 +1010,26 @@ void tst_qmldiskcache::cacheModuleScripts()
}
}
+void tst_qmldiskcache::reuseStaticMappings()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ QVERIFY2(testCompiler.compile("import QtQml 2.15\nQtObject { objectName: 'foobar' }\n"),
+ qPrintable(testCompiler.lastErrorString));
+
+ const quintptr data1 = testCompiler.unitData();
+ QVERIFY(data1 != 0);
+ QCOMPARE(testCompiler.unitData(), data1);
+
+ testCompiler.reset();
+ QVERIFY(testCompiler.loadTestFile());
+
+ QCOMPARE(testCompiler.unitData(), data1);
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro
index 87521eac43..006439b58a 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro
+++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro
@@ -2,7 +2,6 @@ TEMPLATE = lib
QT = core
macos:CONFIG -= app_bundle
-CONFIG -= debug_and_release_target
SOURCES = foreign.cpp
HEADERS = foreign.h
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro
index fe21b122c2..3589743f0c 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro
@@ -17,4 +17,9 @@ QML_IMPORT_NAME = QmlTypeRegistrarTest
QML_IMPORT_VERSION = 1.0
INCLUDEPATH += foreign
-LIBS += -Lforeign -lforeign
+debug_and_release {
+ CONFIG(release, debug|release): LIBS += -Lforeign/release -lforeign
+ else: LIBS += -Lforeign/debug -lforeign
+} else {
+ LIBS += -Lforeign -lforeign
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/data/Required.qml b/tests/auto/qml/qqmlapplicationengine/data/Required.qml
new file mode 100644
index 0000000000..acf4a00ce6
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/data/Required.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+
+QtObject
+{
+ required property int foo
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml b/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml
new file mode 100644
index 0000000000..c5de4661a9
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/data/requiredViolation.qml
@@ -0,0 +1,3 @@
+import QtQml 2.15
+
+Required {}
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index f636e527c3..4306c7b8ca 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -56,6 +56,7 @@ private slots:
void translationChange();
void setInitialProperties();
void failureToLoadTriggersWarningSignal();
+ void errorWhileCreating();
private:
QString buildDir;
@@ -333,6 +334,23 @@ void tst_qqmlapplicationengine::failureToLoadTriggersWarningSignal()
QTRY_COMPARE(warningObserver.count(), 1);
}
+void tst_qqmlapplicationengine::errorWhileCreating()
+{
+ auto url = testFileUrl("requiredViolation.qml");
+ QQmlApplicationEngine test;
+ QSignalSpy observer(&test, &QQmlApplicationEngine::objectCreated);
+
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlApplicationEngine failed to create component");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(QStringLiteral("%1:5:5: Required property foo was not initialized").arg(testFileUrl("Required.qml").toString())));
+
+ test.load(url);
+
+ QTRY_COMPARE(observer.count(), 1);
+ QList<QVariant> args = observer.takeFirst();
+ QVERIFY(args.at(0).isNull());
+ QCOMPARE(args.at(1).toUrl(), url);
+}
+
QTEST_MAIN(tst_qqmlapplicationengine)
#include "tst_qqmlapplicationengine.moc"
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 2610402455..b3e6ffd79e 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -358,9 +358,8 @@ void tst_qqmlbinding::delayed()
// doesn't update immediately
QCOMPARE(item->property("changeCount").toInt(), 1);
- QCoreApplication::processEvents();
// only updates once (non-delayed would update twice)
- QCOMPARE(item->property("changeCount").toInt(), 2);
+ QTRY_COMPARE(item->property("changeCount").toInt(), 2);
}
void tst_qqmlbinding::bindingOverwriting()
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 43cbd93396..544569de5f 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -828,7 +828,7 @@ void tst_qqmlcomponent::testSetInitialProperties()
comp.createWithInitialProperties(QVariantMap { {"notThePropertiesYoureLookingFor", 42} })
};
QVERIFY(obj);
- QVERIFY(comp.errorString().contains("Could not set property notThePropertiesYoureLookingFor"));
+ QVERIFY(comp.errorString().contains("Setting initial properties failed: Item does not have a property called notThePropertiesYoureLookingFor"));
}
}
diff --git a/tests/auto/qml/qqmlconsole/data/assert.qml b/tests/auto/qml/qqmlconsole/data/assert.qml
index dd580e2a72..9687437c48 100644
--- a/tests/auto/qml/qqmlconsole/data/assert.qml
+++ b/tests/auto/qml/qqmlconsole/data/assert.qml
@@ -29,21 +29,22 @@
import QtQuick 2.0
QtObject {
- property int q:1
+ property int q: 1
+
function assertFail() {
- console.assert(0, "This will fail too")
+ console.assert(0, "This will fail too");
}
Component.onCompleted: {
- var x = 12;
+ const x = 12;
console.assert(x == 12, "This will pass");
try {
- console.assert(x < 12, "This will fail");
+ console.assert(x < 12, "This will fail");
} catch (e) {
console.log(e);
}
- console.assert("x < 12", "This will pass too")
+ console.assert("x < 12", "This will pass too");
assertFail();
- console.assert(1)
+ console.assert(1);
}
}
diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
index d593f0dfa1..6d471e7f80 100644
--- a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
+++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
@@ -40,7 +40,7 @@
import QtQuick 2.12
Item {
- id:root
+ id: root
LoggingCategory {
id: testCategory
@@ -69,8 +69,11 @@ Item {
console.warn(testCategoryStartingFromWarning, "console.warn");
console.error(testCategoryStartingFromWarning, "console.error");
- testCategory.name = "qt.test2";
- testCategory.defaultLogLevel = LoggingCategory.Debug;
+ testCategory.name = "qt.test"; // should be silent
+ testCategoryStartingFromWarning.name = "qt.test.other"; // should issue a warning
+
+ testCategory.defaultLogLevel = LoggingCategory.Debug; // should be silent
+ testCategoryStartingFromWarning.defaultLogLevel = LoggingCategory.Debug; // should issue a warning
console.error(emptyCategory, "console.error");
}
diff --git a/tests/auto/qml/qqmlconsole/data/exception.qml b/tests/auto/qml/qqmlconsole/data/exception.qml
index 63afd18828..b9b83525e8 100644
--- a/tests/auto/qml/qqmlconsole/data/exception.qml
+++ b/tests/auto/qml/qqmlconsole/data/exception.qml
@@ -30,12 +30,12 @@ import QtQuick 2.0
QtObject {
function exceptionFail() {
- console.exception("Exception 2")
+ console.exception("Exception 2");
}
Component.onCompleted: {
try {
- console.exception("Exception 1")
+ console.exception("Exception 1");
} catch (e) {
console.log(e);
}
diff --git a/tests/auto/qml/qqmlconsole/data/logging.qml b/tests/auto/qml/qqmlconsole/data/logging.qml
index f5eaeb442a..ac3884bc8e 100644
--- a/tests/auto/qml/qqmlconsole/data/logging.qml
+++ b/tests/auto/qml/qqmlconsole/data/logging.qml
@@ -30,7 +30,8 @@
import QtQuick 2.0
QtObject {
- id:root
+ id: root
+
required property var customObject
required property var stringListProperty
@@ -49,14 +50,14 @@ QtObject {
consoleCount();
consoleCount();
- var a = [1, 2];
- var b = {a: "hello", d: 1 };
- b.toString = function() { return JSON.stringify(b) }
- var c
- var d = 12;
- var e = function() { return 5;};
- var f = true;
- var g = {toString: function() { throw new Error('toString'); }};
+ const a = [1, 2];
+ const b = { a: "hello", d: 1 };
+ b.toString = function() { return JSON.stringify(b); }
+ let c;
+ const d = 12;
+ const e = function() { return 5; };
+ const f = true;
+ const g = { toString: function() { throw new Error('toString'); } };
console.log(a);
console.log(b);
@@ -79,6 +80,6 @@ QtObject {
return;
}
- throw ("console.log(exception) should have raised an exception");
+ throw "console.log(exception) should have raised an exception";
}
}
diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
index 48613d04f1..3a25dfb10d 100644
--- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
+++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
@@ -134,18 +134,27 @@ void tst_qqmlconsole::categorized_logging()
QVERIFY(messageHandler.messages().contains("qt.test.warning: console.error"));
QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(56).arg(5) +
- "QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !";
+ "QML LoggingCategory: Declaring the name of a LoggingCategory is mandatory and cannot be changed later";
QVERIFY(messageHandler.messages().contains(emptyCategory));
- QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
- "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created";
+
+ QString notChangedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
+ "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the component is completed";
+ QVERIFY(!messageHandler.messages().contains(notChangedCategory));
+ QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) +
+ "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the component is completed";
QVERIFY(messageHandler.messages().contains(changedCategory));
- QString changedDefaultLogLevel = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
- "QML LoggingCategory: The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created";
+
+ QString notChangedDefaultLogLevel = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
+ "QML LoggingCategory: The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed";
+ QVERIFY(!messageHandler.messages().contains(notChangedDefaultLogLevel));
+ QString changedDefaultLogLevel = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) +
+ "QML LoggingCategory: The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed";
QVERIFY(messageHandler.messages().contains(changedDefaultLogLevel));
- QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(75) +
+
+ QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(78) +
"Error: A QmlLoggingCatgory was provided without a valid name";
QVERIFY(messageHandler.messages().contains(useEmptyCategory));
@@ -188,11 +197,11 @@ void tst_qqmlconsole::testAssert()
// assert()
QString assert1 = "This will fail\n" +
- QString::fromLatin1("onCompleted (%1:%2)").arg(testUrl.toString()).arg(41);
+ QString::fromLatin1("onCompleted (%1:%2)").arg(testUrl.toString()).arg(42);
QString assert2 = "This will fail too\n" +
- QString::fromLatin1("assertFail (%1:%2)\n").arg(testUrl.toString()).arg(34) +
- QString::fromLatin1("onCompleted (%1:%2)").arg(testUrl.toString()).arg(46);
+ QString::fromLatin1("assertFail (%1:%2)\n").arg(testUrl.toString()).arg(35) +
+ QString::fromLatin1("onCompleted (%1:%2)").arg(testUrl.toString()).arg(47);
QTest::ignoreMessage(QtCriticalMsg, qPrintable(assert1));
QTest::ignoreMessage(QtCriticalMsg, qPrintable(assert2));
diff --git a/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml
new file mode 100644
index 0000000000..fa154b25f3
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/ImageToggle.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+ property var isSelected: null
+ property string source
+ implicitWidth: 16
+ implicitHeight: 16
+
+ onSourceChanged: {
+ updateImageSource()
+ }
+
+ onIsSelectedChanged: {
+ updateImageSource()
+ }
+
+ function updateImageSource() {
+ let result = isSelected ? source + "_selected_dark.png" : source + "_active_dark.png"
+ }
+
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml
new file mode 100644
index 0000000000..46d7524527
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/contextAccessedByHandler.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ width: 640
+ height: 480
+ property bool works: myView.currentItem.okay
+
+ ListView {
+ id: myView
+ model: myModel
+ anchors.fill: parent
+ delegate: Row {
+ property alias okay: image.isSelected
+ ImageToggle {
+ id: image
+ source: "glyph_16_arrow_patch"
+ isSelected: model.age < 6
+ }
+ Text {
+ text: "age:" + model.age + " selected:" + image.isSelected
+ }
+ }
+ }
+
+ ListModel {
+ id: myModel
+ ListElement { type: "Cat"; age: 3; }
+ ListElement { type: "Dog"; age: 2; }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml b/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml
new file mode 100644
index 0000000000..02d737e37f
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.8
+import QtQml.Models 2.1
+
+DelegateModel {
+ id: visualModel
+ model: ListModel {
+ id: myLM
+ ListElement {
+ name: "Apple"
+ }
+ ListElement {
+ name: "Orange"
+ }
+ }
+
+ filterOnGroup: "selected"
+
+ groups: [
+ DelegateModelGroup {
+ name: "selected"
+ includeByDefault: true
+ }
+ ]
+
+ delegate: Text {
+ Component.onCompleted: {
+ DelegateModel.inPersistedItems = true
+ DelegateModel.inSelected = false
+ }
+ text: "item " + index
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml b/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml
new file mode 100644
index 0000000000..4ae1a8aacc
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/removeFromGroup.qml
@@ -0,0 +1,45 @@
+import QtQuick 2.8
+import QtQml.Models 2.1
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ DelegateModel {
+ id: visualModel
+ model: ListModel {
+ id: myLM
+ ListElement {
+ name: "Apple"
+ }
+ ListElement {
+ name: "Banana"
+ }
+ ListElement {
+ name: "Orange"
+ }
+ }
+ filterOnGroup: "selected"
+ groups: [
+ DelegateModelGroup {
+ name: "selected"
+ includeByDefault: true
+ }
+ ]
+ delegate: Text {
+ Component.onCompleted: {
+ if (index === 1) {
+ DelegateModel.inSelected = false
+ }
+ }
+ text: index + ": " + model.name
+ }
+ }
+
+ // Needs an actual ListView in order for the DelegateModel to instantiate all items
+ ListView {
+ model: visualModel
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro
index 7fdd3ab5f1..fbd72f6a44 100644
--- a/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro
+++ b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro
@@ -2,7 +2,7 @@ CONFIG += testcase
TARGET = tst_qqmldelegatemodel
macos:CONFIG -= app_bundle
-QT += qml testlib core-private qml-private qmlmodels-private
+QT += qml quick testlib core-private qml-private qmlmodels-private
SOURCES += tst_qqmldelegatemodel.cpp
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index 87f42c0c8a..35f1e2c94d 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -29,6 +29,8 @@
#include <QtTest/qtest.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/qquickitem.h>
#include "../../shared/util.h"
@@ -42,6 +44,9 @@ public:
private slots:
void valueWithoutCallingObjectFirst_data();
void valueWithoutCallingObjectFirst();
+ void filterOnGroup_removeWhenCompleted();
+ void qtbug_86017();
+ void contextAccessedByHandler();
};
class AbstractItemModel : public QAbstractItemModel
@@ -134,6 +139,53 @@ void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst()
QCOMPARE(model->variantValue(index, role), expectedValue);
}
+void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted()
+{
+ QQuickView view(testFileUrl("removeFromGroup.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QQuickItem *root = view.rootObject();
+ QVERIFY(root);
+ QQmlDelegateModel *model = root->findChild<QQmlDelegateModel*>();
+ QVERIFY(model);
+ QTest::qWaitFor([=]{ return model->count() == 2; } );
+
+void tst_QQmlDelegateModel::qtbug_86017()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("qtbug_86017.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY2(root, qPrintable(component.errorString()));
+ QTRY_VERIFY(component.isReady());
+ QQmlDelegateModel *model = qobject_cast<QQmlDelegateModel*>(root.data());
+
+ QVERIFY(model);
+ QCOMPARE(model->count(), 2);
+ QCOMPARE(model->filterGroup(), "selected");
+}
+
+void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted()
+{
+ QQuickView view(testFileUrl("removeFromGroup.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QQuickItem *root = view.rootObject();
+ QVERIFY(root);
+ QQmlDelegateModel *model = root->findChild<QQmlDelegateModel*>();
+ QVERIFY(model);
+ QVERIFY(QTest::qWaitFor([=]{ return model->count() == 2; }));
+}
+
+void tst_QQmlDelegateModel::contextAccessedByHandler()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("contextAccessedByHandler.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY2(root, qPrintable(component.errorString()));
+ QVERIFY(root->property("works").toBool());
+}
+
QTEST_MAIN(tst_QQmlDelegateModel)
#include "tst_qqmldelegatemodel.moc"
diff --git a/tests/auto/qml/qqmlecmascript/data/functionAsDefaultArgument.qml b/tests/auto/qml/qqmlecmascript/data/functionAsDefaultArgument.qml
new file mode 100644
index 0000000000..9f576d1af8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/functionAsDefaultArgument.qml
@@ -0,0 +1,8 @@
+import QtQml 2.15
+
+QtObject {
+ id: root
+
+ function f(inner=function(){ root.objectName = "didRun" } ){ inner() }
+ Component.onCompleted: f()
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml
new file mode 100644
index 0000000000..7fe366cac8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/generatorCallsGC.qml
@@ -0,0 +1,13 @@
+import QtQml 2.15
+
+QtObject {
+ function test_generator_gc() {
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ ((function*() { gc() })()).next();
+ }
+
+ Component.onCompleted: () => test_generator_gc()
+
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml b/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml
new file mode 100644
index 0000000000..f775b4c613
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/generatorCrashNewProperty.qml
@@ -0,0 +1,20 @@
+// QTBUG-91491
+import QtQml 2.15
+
+QtObject {
+ property int a: 42
+ property int b: 0
+ property int c: 0
+
+ function f(myfunc) {
+ let gen = myfunc();
+ gen["u"] = 0 // Adding members to the generator used to cause crashes when calling next()
+ c = gen.next().value
+ }
+
+ function refreshA() {
+ f(function*() { b = 12; return a });
+ }
+
+ Component.onCompleted: refreshA();
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
new file mode 100644
index 0000000000..f51ab662ab
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
@@ -0,0 +1,29 @@
+function init() {
+ Array.prototype.doPush = Array.prototype.push
+}
+
+function nasty() {
+ var sc_Vector = Array;
+ var push = sc_Vector.prototype.doPush;
+
+ // Change the memberData to hold something nasty on the current internalClass
+ sc_Vector.prototype.doPush = 5;
+
+ // Trigger a re-allocation of memberData
+ for (var i = 0; i < 256; ++i)
+ sc_Vector.prototype[i + "string"] = function() { return 98; }
+
+ // Change the (new) memberData back, to hold our doPush function again.
+ // This should propagate a protoId change all the way up to the lookup.
+ sc_Vector.prototype.doPush = push;
+}
+
+function func() {
+ var b = [];
+
+ // This becomes a lookup internally, which stores protoId and a pointer
+ // into the memberData. It should get invalidated when memberData is re-allocated.
+ b.doPush(3);
+
+ return b;
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
new file mode 100644
index 0000000000..460c40a750
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
@@ -0,0 +1,13 @@
+import QtQml 2.15
+
+import "internalClassParentGc.js" as Foo
+
+QtObject {
+ Component.onCompleted: {
+ gc();
+ Foo.init();
+ Foo.func();
+ Foo.nasty();
+ objectName = Foo.func()[0];
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 9198d3bebf..9d5ffda180 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -239,6 +239,9 @@ private slots:
void eval();
void function();
void topLevelGeneratorFunction();
+ void generatorCrashNewProperty();
+ void generatorCallsGC();
+ void noYieldInInnerFunction();
void qtbug_10696();
void qtbug_11606();
void qtbug_11600();
@@ -385,6 +388,9 @@ private slots:
void proxyIteration();
void proxyHandlerTraps();
void gcCrashRegressionTest();
+ void functionAsDefaultArgument();
+
+ void internalClassParentGc();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -6489,6 +6495,43 @@ void tst_qqmlecmascript::topLevelGeneratorFunction()
QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1);
}
+// QTBUG-91491
+void tst_qqmlecmascript::generatorCrashNewProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("generatorCrashNewProperty.qml"));
+
+ QScopedPointer<QObject> o(component.create());
+
+ QVERIFY2(o != nullptr, qPrintable(component.errorString()));
+
+ QCOMPARE(o->property("a").toInt(), 42);
+ QCOMPARE(o->property("b").toInt(), 12);
+ QCOMPARE(o->property("c").toInt(), 42);
+}
+
+void tst_qqmlecmascript::generatorCallsGC()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("generatorCallsGC.qml"));
+
+ QScopedPointer<QObject> o(component.create()); // should not crash
+ QVERIFY2(o != nullptr, qPrintable(component.errorString()));
+}
+
+void tst_qqmlecmascript::noYieldInInnerFunction()
+{
+ QJSEngine engine;
+ const QString program = R"(
+ function *a() {
+ (function() { yield 1; })();
+ };
+ )";
+ auto result = engine.evaluate(program);
+ QVERIFY(result.isError());
+ QCOMPARE(result.errorType(), QJSValue::SyntaxError);
+}
+
// Test the "Qt.include" method
void tst_qqmlecmascript::include()
{
@@ -9345,6 +9388,24 @@ void tst_qqmlecmascript::proxyHandlerTraps()
QVERIFY(value.isString() && value.toString() == QStringLiteral("SUCCESS"));
}
+void tst_qqmlecmascript::functionAsDefaultArgument()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("functionAsDefaultArgument.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QCOMPARE(root->objectName(), "didRun");
+}
+
+void tst_qqmlecmascript::internalClassParentGc()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("internalClassParentGc.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QCOMPARE(root->objectName(), "3");
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
index 549aae8c2b..25adf4f31d 100644
--- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
+++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
@@ -71,6 +71,7 @@ private slots:
void contextDelete();
void garbageCollection();
void requiredProperties();
+ void deleteInSetInitialState();
private:
QQmlIncubationController controller;
@@ -1213,6 +1214,38 @@ void tst_qqmlincubator::requiredProperties()
}
}
+class DeletingIncubator : public QQmlIncubator
+{
+
+
+ // QQmlIncubator interface
+protected:
+ void statusChanged(Status) override
+ {
+
+ }
+ void setInitialState(QObject *obj) override
+ {
+ delete obj;
+ clear();
+ }
+};
+
+void tst_qqmlincubator::deleteInSetInitialState()
+{
+ QQmlComponent component(&engine, testFileUrl("requiredProperty.qml"));
+ QVERIFY(component.isReady());
+ // forceCompletion immediately after creating an asynchronous object completes it
+ DeletingIncubator incubator;
+ incubator.setInitialProperties({{"requiredProperty", 42}});
+ QVERIFY(incubator.isNull());
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+ incubator.forceCompletion(); // no crash
+ QVERIFY(incubator.isNull());
+ QCOMPARE(incubator.object(), nullptr); // object was deleted
+}
+
QTEST_MAIN(tst_qqmlincubator)
#include "tst_qqmlincubator.moc"
diff --git a/tests/auto/qml/qqmlinstantiator/data/activeModelChangeInteraction.qml b/tests/auto/qml/qqmlinstantiator/data/activeModelChangeInteraction.qml
new file mode 100644
index 0000000000..2797566ad2
--- /dev/null
+++ b/tests/auto/qml/qqmlinstantiator/data/activeModelChangeInteraction.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.15
+import QtQml.Models 2.15
+
+Item {
+ id: root
+ property int instanceCount: 0
+ property alias active: instantiator.active
+
+ ListModel {
+ id: listmodel
+
+ dynamicRoles: true
+ }
+
+ Component.onCompleted: {
+ listmodel.insert(listmodel.count, {name: "one"})
+ listmodel.insert(listmodel.count, {name: "two"})
+ listmodel.insert(listmodel.count, {name: "three"})
+ }
+
+ Instantiator {
+ id: instantiator
+
+ active: false
+
+ model: listmodel
+
+ delegate: Text {
+ width: 100
+ height: 20
+
+ text: name
+
+ Component.onCompleted: ++root.instanceCount
+ }
+
+ }
+}
+
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index 84e08c471a..d5587432de 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -47,6 +47,7 @@ private slots:
void createMultiple();
void stringModel();
void activeProperty();
+ void activeModelChangeInteraction();
void intModelChange();
void createAndRemove();
@@ -153,6 +154,26 @@ void tst_qqmlinstantiator::activeProperty()
QCOMPARE(object->property("idx").toInt(), 0);
}
+void tst_qqmlinstantiator::activeModelChangeInteraction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("activeModelChangeInteraction.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+
+ // If the instantiator is inactive, a model change does not lead to items being loaded
+ bool ok = false;
+ int count = root->property("instanceCount").toInt(&ok);
+ QVERIFY(ok);
+ QCOMPARE(count, 0);
+
+ // When turning the instantiator active, it will however reflect the model
+ root->setProperty("active", true);
+ count = root->property("instanceCount").toInt(&ok);
+ QVERIFY(ok);
+ QCOMPARE(count, 3);
+}
+
void tst_qqmlinstantiator::intModelChange()
{
QQmlEngine engine;
diff --git a/tests/auto/qml/qqmllanguage/data/Broken.qml b/tests/auto/qml/qqmllanguage/data/Broken.qml
new file mode 100644
index 0000000000..e1b61f31f4
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Broken.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ notThere: 5
+}
diff --git a/tests/auto/qml/qqmllanguage/data/ComponentType.qml b/tests/auto/qml/qqmllanguage/data/ComponentType.qml
new file mode 100644
index 0000000000..ad0dad455c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ComponentType.qml
@@ -0,0 +1,8 @@
+import QtQml 2
+
+Component {
+ id: componentRoot
+ QtObject {
+ objectName: "enclosed"
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/Tab1.qml b/tests/auto/qml/qqmllanguage/data/Tab1.qml
new file mode 100644
index 0000000000..e1cd6d8c34
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Tab1.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.15
+
+Item {
+ component LeftTab: Item {
+ }
+
+ component RightTab: Item {
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
new file mode 100644
index 0000000000..b5f96e15c1
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+
+ property alias symbol: symbol
+ symbol.layer.enabled: true
+
+ Item {
+ id: symbol
+ }
+
+ Rectangle {
+ id: txtElevationValue
+
+ property Rectangle background: Rectangle { }
+
+ state: "ValidatorInvalid"
+
+ states: [
+ State {
+ name: "ValidatorInvalid"
+ PropertyChanges {
+ target: txtElevationValue
+ background.border.color: "red" // this line caused the segfault in qtbug107795
+ }
+ },
+ State {
+ name: "ValidatorAcceptable"
+ }
+ ]
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/ambiguousBinding/TestCase.qml b/tests/auto/qml/qqmllanguage/data/ambiguousBinding/TestCase.qml
new file mode 100644
index 0000000000..c76d2b679e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ambiguousBinding/TestCase.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+import QtTest 1.0
+
+QtObject {
+ component Comp: QtObject {}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/ambiguousBinding/ambiguousContainingType.qml b/tests/auto/qml/qqmllanguage/data/ambiguousBinding/ambiguousContainingType.qml
new file mode 100644
index 0000000000..765dc91fe1
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ambiguousBinding/ambiguousContainingType.qml
@@ -0,0 +1,3 @@
+import QtQml 2.15
+
+TestCase {}
diff --git a/tests/auto/qml/qqmllanguage/data/asBroken.qml b/tests/auto/qml/qqmllanguage/data/asBroken.qml
new file mode 100644
index 0000000000..bd88d14c76
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/asBroken.qml
@@ -0,0 +1,6 @@
+import QtQml 2.15
+
+QtObject {
+ id: self
+ property var selfAsBroken: self as Broken
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml
new file mode 100644
index 0000000000..34dcbf96fa
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml
@@ -0,0 +1,10 @@
+import QtQml 2.15
+
+QtObject {
+ id: testItem
+ property rect rect
+ onComplete {
+ rect.x: 2
+ rect.width: 22
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/bareInline.qml b/tests/auto/qml/qqmllanguage/data/bareInline.qml
new file mode 100644
index 0000000000..cb79021250
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bareInline.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.9
+
+Item {
+ width: 800
+ height: 600
+ visible: true
+
+ Tab1.RightTab {}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml
new file mode 100644
index 0000000000..7e10553ae7
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml
@@ -0,0 +1,11 @@
+import QtQuick 2
+
+Item {
+ id: root
+ Component {
+ id: accessibleNormal
+ Item {}
+ }
+ property alias accessibleNormalUrl: accessibleNormal.url
+ property url urlClone: root.accessibleNormalUrl // crashes qml utility
+}
diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml
new file mode 100644
index 0000000000..899b7aca37
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml
@@ -0,0 +1,11 @@
+import QtQuick 2
+Item {
+ id: root
+ Component {
+ id: accessibleNormal
+ ComponentType {
+ id: inaccessibleNormal
+ }
+ }
+ property alias accessibleNormalProgress: accessibleNormal.progress
+}
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
index e399799fe9..758be7feae 100644
--- a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
@@ -1,2 +1 @@
-2:8:Cannot assign to non-existent property "_G"
-
+2:11:Non-existent attached object
diff --git a/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml b/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml
new file mode 100644
index 0000000000..51943c3839
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml
@@ -0,0 +1,3 @@
+import QtQml 2.15
+
+QtObject["foobar"] {}
diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt
new file mode 100644
index 0000000000..d69122ef8b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.errors.txt
@@ -0,0 +1 @@
+6:40:Invalid alias target
diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml
new file mode 100644
index 0000000000..17602ca4b9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/qtbug_89822.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ readonly property QtObject test: QtObject { property int subproperty: 3}
+ readonly property alias testAlias: root.test.subproperty
+}
+
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 31a4135d89..df9d1401a9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -174,7 +174,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Execut
return;
}
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script) {
error(binding, QStringLiteral("Custom parser invoked with the wrong property value. Expected script that evaluates to enum"));
return;
}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index e247a139ec..92d0069ce4 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlprivate.h>
#include <QtQml/qqmlincubator.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>
@@ -48,6 +49,7 @@
#include <private/qqmlscriptstring_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
+#include <private/qqmltype_p_p.h>
#include "testtypes.h"
#include "testhttpserver.h"
@@ -145,6 +147,7 @@ private slots:
void aliasProperties();
void aliasPropertiesAndSignals();
void aliasPropertyChangeSignals();
+ void qtbug_89822();
void componentCompositeType();
void i18n();
void i18n_data();
@@ -331,6 +334,16 @@ private slots:
void arrayToContainer();
void qualifiedScopeInCustomParser();
void accessNullPointerPropertyCache();
+ void bareInlineComponent();
+
+ void hangOnWarning();
+
+ void ambiguousContainingType();
+ void staticConstexprMembers();
+ void bindingAliasToComponentUrl();
+ void badGroupedProperty();
+
+ void objectAsBroken();
private:
QQmlEngine engine;
@@ -2139,6 +2152,22 @@ void tst_qqmllanguage::aliasProperties()
QCOMPARE(subItem->property("y").toInt(), 1);
}
+ // Nested property bindings on group properties that are actually aliases (QTBUG-94983)
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.15a.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subItem = qvariant_cast<QObject*>(object->property("symbol"));
+ QVERIFY(!subItem.isNull());
+
+ QPointer<QObject> subSubItem = qvariant_cast<QObject*>(subItem->property("layer"));
+
+ QCOMPARE(subSubItem->property("enabled").value<bool>(), true);
+ }
+
// Alias to sub-object with binding (QTBUG-57041)
{
// This is shold *not* crash.
@@ -2228,6 +2257,12 @@ void tst_qqmllanguage::aliasPropertiesAndSignals()
QCOMPARE(o->property("test").toBool(), true);
}
+void tst_qqmllanguage::qtbug_89822()
+{
+ QQmlComponent component(&engine, testFileUrl("qtbug_89822.qml"));
+ VERIFY_ERRORS("qtbug_89822.errors.txt");
+}
+
// Test that the root element in a composite type can be a Component
void tst_qqmllanguage::componentCompositeType()
{
@@ -5813,6 +5848,129 @@ void tst_qqmllanguage::accessNullPointerPropertyCache()
QVERIFY(!obj.isNull());
}
+void tst_qqmllanguage::bareInlineComponent()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, testFileUrl("bareInline.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QQmlMetaType::freeUnusedTypesAndCaches();
+
+ bool tab1Found = false;
+ const auto types = QQmlMetaType::qmlTypes();
+ for (const QQmlType &type : types) {
+ if (type.elementName() == QStringLiteral("Tab1")) {
+ QVERIFY(type.module().isEmpty());
+ tab1Found = true;
+ const auto ics = type.priv()->objectIdToICType;
+ QVERIFY(ics.size() > 0);
+ for (const QQmlType &ic : ics)
+ QVERIFY(ic.containingType() == type);
+ }
+ }
+ QVERIFY(tab1Found);
+}
+
+void tst_qqmllanguage::hangOnWarning()
+{
+ QTest::ignoreMessage(QtWarningMsg,
+ qPrintable(QStringLiteral("%1:3 : Ignored annotation")
+ .arg(testFileUrl("hangOnWarning.qml").toString())));
+ QQmlComponent component(&engine, testFileUrl("hangOnWarning.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != nullptr);
+}
+
+void tst_qqmllanguage::ambiguousContainingType()
+{
+ // Need to do it twice, so that we load from disk cache the second time.
+ for (int i = 0; i < 2; ++i) {
+ QQmlEngine engine;
+
+ // Should not crash when loading the type
+ QQmlComponent c(&engine, testFileUrl("ambiguousBinding/ambiguousContainingType.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ }
+}
+ void tst_qqmllanguage::staticConstexprMembers() {
+ // this tests if the symbols are correclty defined for c++11 (gcc 11 and 12), and should
+ // not have any linker errors
+ using T = QObject;
+ using T2 = QObject;
+
+ auto f1 = QQmlPrivate::Constructors<T, true>::createSingletonInstance;
+ auto f2 = QQmlPrivate::Constructors<T, false>::createSingletonInstance;
+ auto f3 = QQmlPrivate::Constructors<T, true>::createInto;
+
+ auto f4 = QQmlPrivate::ExtendedType<T, true>::createParent;
+ auto f5 = QQmlPrivate::ExtendedType<T, false>::createParent;
+ auto f6 = QQmlPrivate::ExtendedType<T, true>::staticMetaObject;
+
+ auto f7 = QQmlPrivate::QmlSingleton<T, T2>::Value;
+
+ Q_UNUSED(f1);
+ Q_UNUSED(f2);
+ Q_UNUSED(f3);
+ Q_UNUSED(f3);
+ Q_UNUSED(f4);
+ Q_UNUSED(f5);
+ Q_UNUSED(f6);
+ Q_UNUSED(f7);
+ }
+
+void tst_qqmllanguage::bindingAliasToComponentUrl()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QCOMPARE(object->property("accessibleNormalUrl"), object->property("urlClone"));
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl2.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QCOMPARE(object->property("accessibleNormalProgress"), QVariant(1.0));
+ }
+}
+
+void tst_qqmllanguage::badGroupedProperty()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("badGroupedProperty.qml");
+ QQmlComponent c(&engine, url);
+ QVERIFY(c.isError());
+ QCOMPARE(c.errorString(),
+ QStringLiteral("%1:6 Cannot assign to non-existent property \"onComplete\"\n")
+ .arg(url.toString()));
+}
+
+void tst_qqmllanguage::objectAsBroken()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("asBroken.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVariant selfAsBroken = o->property("selfAsBroken");
+ QVERIFY(selfAsBroken.isValid());
+
+ // 5.15 doesn't enforce type annotation. So the "as" cast succeeds even though
+ // the target type cannot be resolved.
+ QCOMPARE(selfAsBroken.value<QObject *>(), o.data());
+
+ QQmlComponent b(&engine, testFileUrl("Broken.qml"));
+ QVERIFY(b.isError());
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
index b49832499e..9315b2d24d 100644
--- a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
+++ b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
@@ -89,15 +89,13 @@ QT_END_NAMESPACE
static const C::Group Visible = C::Group(2);
static const C::Group Selection = C::Group(3);
+constexpr auto VisibleFlag = C::Flag(0x04);
+constexpr auto SelectionFlag = C::Flag(0x08);
+
class tst_qqmllistcompositor : public QObject
{
Q_OBJECT
- enum {
- VisibleFlag = 0x04,
- SelectionFlag = 0x08
- };
-
void populateChange(
C::Change &change, int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId)
{
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.mjs b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.mjs
new file mode 100644
index 0000000000..cfc1e1146f
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.mjs
@@ -0,0 +1,117 @@
+/* Copyright 2020 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+//------------------------------------------------------------------------------
+
+export const OrderAscending = 0
+export const OrderDescending = 1
+
+//------------------------------------------------------------------------------
+
+export function sort(model, sortKey, descending, useMove) {
+
+ _sort(model, sortKey, descending, useMove);
+}
+
+//------------------------------------------------------------------------------
+
+function _sort(model, sortKey, descending, useMove) {
+ var lessThan = descending ? 1 : -1;
+ var greaterThan = -lessThan;
+
+ var indices = [...new Array(model.count).keys()];
+
+ function getKeyValue(index) {
+ return model.get(index)[sortKey];
+ }
+
+ function compareKey(index1, index2) {
+ var value1 = getKeyValue(index1);
+ var value2 = getKeyValue(index2);
+
+ return (value1 < value2)
+ ? lessThan
+ : (value1 > value2)
+ ? greaterThan
+ : 0;
+ }
+
+ indices.sort(compareKey);
+
+
+ function compareFrom(i1, i2) {
+ return (i1.from < i2.from)
+ ? -1
+ : (i1.from > i2.from)
+ ? 1
+ : 0;
+ }
+
+ var swapIndices = indices.map((e, i) => { return { from: e, to: i }}).sort(compareFrom).map(e => e.to);
+
+ function clone(o) {
+ return JSON.parse(JSON.stringify(o));
+ }
+
+ function cloneSwap(a, b) {
+ var o = clone(model.get(a));
+ model.set(a, model.get(b));
+ model.set(b, o);
+ }
+
+ function moveSwap(a, b) {
+ if (a < b) {
+ model.move(a, b, 1);
+ model.move(b - 1, a, 1);
+ }
+ else if (a > b) {
+ model.move(b, a, 1);
+ model.move(a - 1, b, 1);
+ }
+ }
+
+
+ var swap = useMove ? moveSwap : cloneSwap;
+ var swapCount = 0;
+
+
+ for (var iFrom = 0; iFrom < swapIndices.length; iFrom++) {
+ var iTo = swapIndices[iFrom];
+
+ if (iFrom === iTo) {
+ continue;
+ }
+
+ do {
+ swap(iFrom, iTo);
+
+ var t = swapIndices[iFrom];
+ swapIndices[iFrom] = swapIndices[iTo]
+ swapIndices[iTo] = t;
+
+ iTo = swapIndices[iFrom];
+
+ swapCount++;
+ }
+ while (iFrom !== iTo);
+ }
+
+}
+
+//------------------------------------------------------------------------------
+
+
+
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.qml b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.qml
new file mode 100644
index 0000000000..2679925c80
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSort.qml
@@ -0,0 +1,166 @@
+/* Copyright 2020 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import QtQml 2.13
+import QtQml.Models 2.13
+
+import QtQuick 2.13
+import QtQuick.Layouts 1.12
+
+import "ListModelSort.mjs" as ListModelSort
+
+Item {
+ Component.onCompleted: {
+ fillModel(namesModel, "A, D, E, B, C, A");
+ }
+
+
+ function verify() {
+ let expected = ["A", "A", "B", "C", "D", "E"]
+ let sorted = listView.count === 6
+ for (let i = 0; i<listView.count;++i) {
+ sorted = sorted && (listView.itemAtIndex(i).name === expected[i])
+ }
+ return sorted
+ }
+
+ function doSort() {
+ sortAsync(namesModel, "name", false, false)
+ }
+
+
+ function random(n) {
+ return Math.round(Math.random(new Date().valueOf()) * n);
+ }
+
+ function fillModel(model, values) {
+ var names = values.split(",").map(name => name.trim()).filter(name => name > "");
+
+
+ model.clear();
+
+ for (const name of names) {
+ model.append({
+ name: name,
+ number: random(names.length)
+ });
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ ListModel {
+ id: namesModel
+
+ objectName: "namesModel"
+ }
+
+ //--------------------------------------------------------------------------
+ ColumnLayout {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+
+ ListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: namesModel
+ clip: true
+ spacing: 5
+
+ delegate: Text {
+ required property string name
+ required property int number
+ width: ListView.view.width
+ text: "%1 (%2)".arg(name).arg(number)
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ }
+
+ add: Transition {
+ NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 400 }
+ NumberAnimation { property: "scale"; from: 0; to: 1.0; duration: 400 }
+ }
+
+ addDisplaced: Transition {
+ NumberAnimation { properties: "x,y"; duration: 100 }
+ }
+
+ move: Transition {
+ NumberAnimation { properties: "x,y"; duration: 100 }
+ }
+
+ moveDisplaced: Transition {
+ NumberAnimation { properties: "x,y"; duration: 100 }
+ }
+
+ displaced: Transition {
+ NumberAnimation { properties: "x,y"; duration: 40; easing.type: Easing.OutBounce }
+ }
+
+ populate: Transition {
+ NumberAnimation { properties: "x,y"; duration: 100 }
+ }
+
+ remove: Transition {
+ ParallelAnimation {
+ NumberAnimation { property: "opacity"; to: 0; duration: 100 }
+ NumberAnimation { properties: "x,y"; to: 100; duration: 100 }
+ }
+ }
+
+ removeDisplaced: Transition {
+ NumberAnimation { properties: "x,y"; duration: 100 }
+ }
+ }
+ }
+
+
+ function sortSync(model, sortKey, descending, useMove) {
+
+ ListModelSort.sort(model, sortKey, descending, useMove);
+ }
+
+ //--------------------------------------------------------------------------
+
+ function sortAsync(model, sortKey, descending, useMove) {
+
+ var params = {
+ debug: true,
+ model: model,
+ sortKey: sortKey,
+ descending: descending,
+ useMove: useMove
+ };
+
+
+ sortWorker.sendMessage(params);
+ }
+
+ WorkerScript {
+ id: sortWorker
+
+ source: "ListModelSortWorker.mjs"
+ }
+
+ //--------------------------------------------------------------------------
+
+}
+
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSortWorker.mjs b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSortWorker.mjs
new file mode 100644
index 0000000000..fa3b5c27ec
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/ListModelSortWorker.mjs
@@ -0,0 +1,38 @@
+/* Copyright 2020 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import * as ListModelSort from "ListModelSort.mjs";
+
+//------------------------------------------------------------------------------
+
+WorkerScript.onMessage = function(params) {
+
+ params.model.sync();
+
+ const debug = params.debug;
+
+ ListModelSort.sort(params.model, params.sortKey, !!params.descending, !!params.useMove);
+
+ params.model.sync();
+
+
+ WorkerScript.sendMessage({
+ text: "Sorted"
+ });
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/main.qml b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/main.qml
new file mode 100644
index 0000000000..88a23511fa
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodelworkerscript/data/listmodel_async_sort/main.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.13
+import QtQuick.Window 2.13
+
+Window {
+ visible: true
+ width: 640
+ height: 640
+ title: qsTr("Hello World")
+
+ ListModelSort {
+ id: lms
+ anchors.fill: parent
+ }
+
+ function doSort() {
+ lms.doSort()
+ }
+
+ function verify(): bool {
+ let ok = lms.verify()
+ return ok
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
index b5e8800d0e..edcfc4994d 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
+++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
@@ -104,6 +104,7 @@ private slots:
void worker_remove_list();
void dynamic_role_data();
void dynamic_role();
+ void correctMoves();
};
bool tst_qqmllistmodelworkerscript::compareVariantList(const QVariantList &testList, QVariant object)
@@ -842,6 +843,22 @@ void tst_qqmllistmodelworkerscript::dynamic_role()
qApp->processEvents();
}
+void tst_qqmllistmodelworkerscript::correctMoves()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("listmodel_async_sort/main.qml"));
+ QScopedPointer<QObject> root {component.create()};
+ QVERIFY2(root, qPrintable(component.errorString()));
+ bool ok =QMetaObject::invokeMethod(root.get(), "doSort");
+ QVERIFY(ok);
+ auto check = [&](){
+ bool success = false;
+ QMetaObject::invokeMethod(root.get(), "verify", Q_RETURN_ARG(bool, success));
+ return success;
+ };
+ QTRY_VERIFY(check());
+}
+
QTEST_MAIN(tst_qqmllistmodelworkerscript)
#include "tst_qqmllistmodelworkerscript.moc"
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index 7d0c10a702..822fe33b83 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -73,7 +73,7 @@ private slots:
void dateTimeFormat();
void timeFormat_data();
void timeFormat();
-#if defined(Q_OS_UNIX)
+#if defined(Q_OS_UNIX) && QT_CONFIG(timezone)
void timeZoneUpdated();
#endif
@@ -1276,7 +1276,7 @@ QString DateFormatter::getLocalizedForm(const QString &isoTimestamp)
return locale.toString(input);
}
-#if defined(Q_OS_UNIX)
+#if defined(Q_OS_UNIX) && QT_CONFIG(timezone)
// Currently disabled on Windows as adjusting the timezone
// requires additional privileges that aren't normally
// enabled for a process. This can be achieved by calling
@@ -1327,8 +1327,10 @@ void tst_qqmllocale::timeZoneUpdated()
obj.reset(c.create());
QVERIFY(obj);
-#if !defined(Q_OS_WIN) && QT_CONFIG(timezone) && (!defined(Q_OS_LINUX) || defined(Q_OS_ANDROID))
- QEXPECT_FAIL("", "Date.timeZoneUpdated() only works on non-Android Linux with QT_CONFIG(timezone).", Continue);
+#if !defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
+ QEXPECT_FAIL("",
+ "Date.timeZoneUpdated() only works on non-Android Linux with QT_CONFIG(timezone).",
+ Continue);
#endif
QVERIFY(obj->property("success").toBool());
@@ -1337,12 +1339,14 @@ void tst_qqmllocale::timeZoneUpdated()
QMetaObject::invokeMethod(obj.data(), "check");
-#if !defined(Q_OS_WIN) && QT_CONFIG(timezone) && (!defined(Q_OS_LINUX) || defined(Q_OS_ANDROID))
- QEXPECT_FAIL("", "Date.timeZoneUpdated() only works on non-Android Linux with QT_CONFIG(timezone).", Continue);
+#if !defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
+ QEXPECT_FAIL("",
+ "Date.timeZoneUpdated() only works on non-Android Linux with QT_CONFIG(timezone).",
+ Continue);
#endif
QVERIFY(obj->property("success").toBool());
}
-#endif
+#endif // Unix && timezone
QTEST_MAIN(tst_qqmllocale)
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 8a96fc52c5..4032a4448d 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -164,6 +164,8 @@ private slots:
void nestedQQmlPropertyMap();
void underscorePropertyChangeHandler();
+
+ void signalExpressionWithoutObject();
private:
QQmlEngine engine;
};
@@ -1941,7 +1943,8 @@ void tst_qqmlproperty::copy()
{
PropertyObject object;
- QQmlProperty *property = new QQmlProperty(&object, QLatin1String("defaultProperty"));
+ QScopedPointer<QQmlProperty> property(
+ new QQmlProperty(&object, QLatin1String("defaultProperty")));
QCOMPARE(property->name(), QString("defaultProperty"));
QCOMPARE(property->read(), QVariant(10));
QCOMPARE(property->type(), QQmlProperty::Property);
@@ -1964,7 +1967,7 @@ void tst_qqmlproperty::copy()
QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p2.propertyType(), (int)QVariant::Int);
- delete property; property = nullptr;
+ property.reset();
QCOMPARE(p1.name(), QString("defaultProperty"));
QCOMPARE(p1.read(), QVariant(10));
@@ -1977,6 +1980,16 @@ void tst_qqmlproperty::copy()
QCOMPARE(p2.type(), QQmlProperty::Property);
QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p2.propertyType(), (int)QVariant::Int);
+
+ p1 = QQmlProperty();
+ QQmlPropertyPrivate *p2d = QQmlPropertyPrivate::get(p2);
+ QCOMPARE(p2d->count(), 1);
+
+ // Use a pointer to avoid compiler warning about self-assignment.
+ QQmlProperty *p2p = &p2;
+ *p2p = p2;
+
+ QCOMPARE(p2d->count(), 1);
}
void tst_qqmlproperty::noContext()
@@ -2210,6 +2223,14 @@ void tst_qqmlproperty::underscorePropertyChangeHandler()
QVERIFY(changeHandler.isSignalProperty());
}
+void tst_qqmlproperty::signalExpressionWithoutObject()
+{
+ QQmlProperty invalid;
+ QQmlPropertyPrivate::setSignalExpression(invalid, nullptr);
+ QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(invalid);
+ QVERIFY(!expr);
+}
+
QTEST_MAIN(tst_qqmlproperty)
#include "tst_qqmlproperty.moc"
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index a75a00bd01..b6e0b00cc4 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -96,13 +96,13 @@ void tst_qqmltranslation::translation()
const bool expectCompiledTranslation = compiledTranslations.contains(propertyName);
if (expectCompiledTranslation) {
- if (binding->type != QV4::CompiledData::Binding::Type_Translation)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation));
+ QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_Translation);
} else {
- if (binding->type == QV4::CompiledData::Binding::Type_Translation)
+ if (binding->type() == QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation";
- QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation);
+ QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
@@ -148,11 +148,11 @@ void tst_qqmltranslation::idTranslation()
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
if (propertyName == "idTranslation") {
- if (binding->type != QV4::CompiledData::Binding::Type_TranslationById)
+ if (binding->type() != QV4::CompiledData::Binding::Type_TranslationById)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById));
+ QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_TranslationById);
} else {
- QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation);
+ QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
diff --git a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
index aa26956922..09f1d472b7 100644
--- a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
+++ b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml
@@ -10,6 +10,59 @@ Item {
property variant v2: Qt.vector3d(1,2,3)
property real factor: 2.23
+ function testTransformation() {
+ let m = Qt.matrix4x4();
+
+ m.scale(1, 2, 4);
+ if (m !== Qt.matrix4x4(1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 4, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.scale(Qt.vector3d(-8, -4, -2));
+ if (m !== Qt.matrix4x4(-8, 0, 0, 0,
+ 0,-8, 0, 0,
+ 0, 0, -8, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.scale(-1 / 8);
+ if (m !== Qt.matrix4x4())
+ return false;
+
+ m.rotate(180, Qt.vector3d(1, 0, 0));
+ if (m !== Qt.matrix4x4(1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.rotate(180, Qt.vector3d(0, 1, 0));
+ if (m !== Qt.matrix4x4(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1))
+ return false;
+ m.rotate(180, Qt.vector3d(0, 0, 1));
+ if (m !== Qt.matrix4x4())
+ return false;
+
+ m.translate(Qt.vector3d(1, 2, 4));
+ if (m !== Qt.matrix4x4(1, 0, 0, 1,
+ 0, 1, 0, 2,
+ 0, 0, 1, 4,
+ 0, 0, 0, 1))
+ return false;
+
+ m = Qt.matrix4x4();
+ m.lookAt(Qt.vector3d(1, 2, 4), Qt.vector3d(1, 2, 0), Qt.vector3d(0, 1, 0));
+ if (m !== Qt.matrix4x4(1, 0, 0, -1,
+ 0, 1, 0, -2,
+ 0, 0, 1, -4,
+ 0, 0, 0, 1))
+ return false;
+
+ return true;
+ }
+
Component.onCompleted: {
success = true;
if (m1.times(m2) != Qt.matrix4x4(26, 26, 26, 26, 52, 52, 52, 52, 78, 78, 78, 78, 104, 104, 104, 104)) success = false;
@@ -27,5 +80,6 @@ Item {
if (m1.transposed() != Qt.matrix4x4(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)) success = false;
if (m1.fuzzyEquals(m2)) success = false;
if (!m1.fuzzyEquals(m2, 10)) success = false;
+ if (!testTransformation()) success = false;
}
}
diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
index 5dd8e9dcc0..5d9be077fe 100644
--- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
+++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
@@ -148,6 +148,9 @@ void tst_QV4Assembler::jitEnabled()
#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM)
/* JIT should be disabled Windows on ARM/ARM64 for now. */
QVERIFY(!QT_CONFIG(qml_jit));
+#elif defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM)
+ /* JIT should be disabled on macOS on ARM/ARM64 for now. */
+ QVERIFY(!QT_CONFIG(qml_jit));
#else
/* JIT should be enabled on all other architectures/OSes tested in CI. */
QVERIFY(QT_CONFIG(qml_jit));
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 5d635aa63b..34da3a7c50 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -47,7 +47,7 @@ private slots:
void gcStats();
void multiWrappedQObjects();
void accessParentOnDestruction();
- void clearICParent();
+ void cleanInternalClasses();
};
void tst_qv4mm::gcStats()
@@ -76,10 +76,10 @@ void tst_qv4mm::multiWrappedQObjects()
QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
- // Moves the additional WeakValue from m_multiplyWrappedQObjects to
- // m_pendingFreedObjectWrapperValue. It's still alive after all.
+ // The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved
+ // to m_pendingFreedObjectWrapperValue yet. It's still alive after all.
engine1.memoryManager->runGC();
- QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 2);
+ QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
// engine2 doesn't own the object as engine1 was the first to wrap it above.
// Therefore, no effect here.
@@ -110,16 +110,41 @@ void tst_qv4mm::accessParentOnDestruction()
QCOMPARE(obj->property("destructions").toInt(), 100);
}
-void tst_qv4mm::clearICParent()
+void tst_qv4mm::cleanInternalClasses()
{
QV4::ExecutionEngine engine;
QV4::Scope scope(engine.rootContext());
QV4::ScopedObject object(scope, engine.newObject());
+ QV4::ScopedObject prototype(scope, engine.newObject());
+
+ // Set a prototype so that we get a unique IC.
+ object->setPrototypeOf(prototype);
+
+ QV4::Scoped<QV4::InternalClass> prevIC(scope, object->internalClass());
+ QVERIFY(prevIC->d()->transitions.empty());
+
+ uint prevIcChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++prevIcChainLength;
+
+ const auto checkICCHainLength = [&]() {
+ uint icChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++icChainLength;
+
+ const uint redundant = object->internalClass()->numRedundantTransitions;
+ QVERIFY(redundant <= QV4::Heap::InternalClass::MaxRedundantTransitions);
+
+ // A removal makes two transitions redundant.
+ QVERIFY(icChainLength <= prevIcChainLength + 2 * redundant);
+ };
+
+ const uint numTransitions = 16 * 1024;
// Keep identifiers in a separate array so that we don't have to allocate them in the loop that
// should test the GC on InternalClass allocations.
QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject());
- for (uint i = 0; i < 16 * 1024; ++i) {
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope);
s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i));
@@ -130,22 +155,60 @@ void tst_qv4mm::clearICParent()
object->insertMember(s, v);
}
- // When allocating the InternalClass objects required for deleting properties, the GC should
- // eventually run and remove all but the last two.
- // If we ever manage to avoid allocating the InternalClasses in the first place we will need
- // to change this test.
- for (uint i = 0; i < 16 * 1024; ++i) {
+ // There is a chain of ICs originating from the original class.
+ QCOMPARE(prevIC->d()->transitions.size(), 1u);
+ QVERIFY(prevIC->d()->transitions.front().lookup != nullptr);
+
+ // When allocating the InternalClass objects required for deleting properties, eventually
+ // the IC chain gets truncated, dropping all the removed properties.
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope, identifiers->get(i));
QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass());
QVERIFY(ic->d()->parent != nullptr);
- object->deleteProperty(s->toPropertyKey());
+ QV4::ScopedValue val(scope, object->get(s->toPropertyKey()));
+ QCOMPARE(val->toNumber(), double(i));
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
QVERIFY(object->internalClass() != ic->d());
- QCOMPARE(object->internalClass()->parent, ic->d());
- if (ic->d()->parent == nullptr)
- return;
}
- QFAIL("Garbage collector was not triggered by large amount of InternalClasses");
+
+ // None of the properties we've added are left
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
+ }
+
+ // Also no other properties have appeared
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator(object->ownPropertyKeys(object));
+ QVERIFY(!iterator->next(object).isValid());
+
+ checkICCHainLength();
+
+ // Add and remove properties until it clears all remaining redundant ones
+ uint i = 0;
+ while (object->internalClass()->numRedundantTransitions > 0) {
+ i = (i + 1) % numTransitions;
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QV4::ScopedValue v(scope);
+ v->setDouble(i);
+ object->insertMember(s, v);
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ }
+
+ // Make sure that all dangling ICs are actually gone.
+ scope.engine->memoryManager->runGC();
+
+ // Now the GC has removed the ICs we originally added by adding properties.
+ QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr);
+
+ // Same thing with redundant prototypes
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedObject prototype(scope, engine.newObject());
+ object->setPrototypeOf(prototype); // Makes previous prototype redundant
+ }
+
+ checkICCHainLength();
}
QTEST_MAIN(tst_qv4mm)
diff --git a/tests/auto/qmltest/events/tst_wheel.qml b/tests/auto/qmltest/events/tst_wheel.qml
index 96338a51ec..fc38141971 100644
--- a/tests/auto/qmltest/events/tst_wheel.qml
+++ b/tests/auto/qmltest/events/tst_wheel.qml
@@ -64,7 +64,7 @@ Rectangle {
verify(flick.contentX == 0);
flick.contentY = 0;
verify(flick.contentY == 0);
- mouseWheel(flick, 200, 200, -120, 0, Qt.NoButton, Qt.NoModifier, -1);
+ mouseWheel(flick, 200, 200, -120, 0, Qt.NoButton, Qt.NoModifier, 100);
wait(1000);
verify(flick.contentX > 0);
verify(flick.contentY == 0);
diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro
index 8ad1541cbc..0b793269e9 100644
--- a/tests/auto/qmltest/qmltest.pro
+++ b/tests/auto/qmltest/qmltest.pro
@@ -15,7 +15,6 @@ SUBDIRS += \
listview \
objectmodel \
pathview \
- pixel \
positioners \
qqmlbinding \
qtbug46798 \
diff --git a/tests/auto/qmltest/selftests/tst_grabImage.qml b/tests/auto/qmltest/selftests/tst_grabImage.qml
index 7ce7e93a07..714bc07f27 100644
--- a/tests/auto/qmltest/selftests/tst_grabImage.qml
+++ b/tests/auto/qmltest/selftests/tst_grabImage.qml
@@ -34,6 +34,7 @@ TestCase {
id: testCase
name: "tst_grabImage"
when: windowShown
+ visible: true
function test_equals() {
var rect = createTemporaryQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 10; }", testCase);
@@ -47,6 +48,8 @@ TestCase {
// Don't change anything...
newImage = grabImage(rect);
try {
+ // Check that we actually grabbed something
+ compare(newImage.pixel(0, 0), "#ff0000")
compare(newImage.size, oldImage.size);
verify(newImage.equals(oldImage));
} catch (ex) {
@@ -86,6 +89,8 @@ TestCase {
try {
verify(grabbedImage2.equals(grabbedImage))
+ // Check that we actually grabbed, saved, and loaded something
+ compare(grabbedImage2.pixel(0, 0), "#ff0000")
} catch (ex) {
grabbedImage2.save("tst_grabImage_test_save2.png")
throw ex;
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 249ecd5aa5..a4f138c8df 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -125,12 +125,12 @@ public:
setRootNode(root);
}
- void render() {
+ void render() override {
++renderCount;
renderingOrder = ++globalRendereringOrder;
}
- void nodeChanged(QSGNode *node, QSGNode::DirtyState state) {
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override {
changedNode = node;
changedState = state;
QSGBatchRenderer::Renderer::nodeChanged(node, state);
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml
new file mode 100644
index 0000000000..b24812c914
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.15
+
+import Test 1.0
+
+Item {
+ width: 640
+ height: 480
+
+ Rectangle {
+ anchors.fill: parent
+ color: "grey"
+
+ Rectangle {
+ x: 200
+ y: 200
+ width: 100
+ height: 100
+ color: "orange"
+ DragHandler {
+ grabPermissions: DragHandler.CanTakeOverFromAnything // but not anything with keepMouseGrab!
+ }
+ }
+ }
+
+ ModalLayer {
+ anchors.fill: parent
+
+ Rectangle {
+ anchors.fill: parent
+ color: "red"
+ opacity: 0.4
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml
index e5ca681bd5..60ea740105 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.12
+import QtQuick 2.15
Rectangle {
color: "#333"
@@ -13,6 +13,7 @@ Rectangle {
DragHandler {
id: dragHandler
margin: 20
+ cursorShape: Qt.ClosedHandCursor
}
Rectangle {
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index 4d6866041e..40b138bc70 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -68,6 +68,7 @@ private slots:
void touchPassiveGrabbers_data();
void touchPassiveGrabbers();
void touchPinchAndMouseMove();
+ void underModalLayer();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -410,6 +411,9 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966
QVERIFY(!dragHandler->active());
QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+#if QT_CONFIG(cursor)
+ QCOMPARE(window->cursor().shape(), Qt::ArrowCursor);
+#endif
p1 += QPoint(dragThreshold * 2, 0);
QTest::mouseMove(window, p1);
QTRY_VERIFY(dragHandler->active());
@@ -418,9 +422,18 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966
QCOMPARE(dragHandler->translation().x(), 0.0); // hmm that's odd
QCOMPARE(dragHandler->translation().y(), 0.0);
QCOMPARE(draggableItem->position(), originalPos + QPointF(dragThreshold * 2, 0));
+#if QT_CONFIG(cursor)
+ // The cursor doesn't change until the next event after the handler becomes active.
+ p1 += QPoint(1, 0);
+ QTest::mouseMove(window, p1);
+ QTRY_COMPARE(window->cursor().shape(), Qt::ClosedHandCursor);
+#endif
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!dragHandler->active());
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
+#if QT_CONFIG(cursor)
+ QTRY_COMPARE(window->cursor().shape(), Qt::ArrowCursor);
+#endif
}
void tst_DragHandler::snapMode_data()
@@ -811,6 +824,59 @@ void tst_DragHandler::touchPinchAndMouseMove()
}
}
+class ModalLayer : public QQuickItem {
+public:
+ explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) {
+ this->setAcceptedMouseButtons(Qt::AllButtons);
+ this->setAcceptTouchEvents(true);
+ this->setKeepMouseGrab(true);
+ this->setKeepTouchGrab(true);
+ }
+
+ bool event(QEvent* event) override {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseTrackingChange:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::Wheel:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchCancel:
+ case QEvent::TouchEnd: {
+ qCDebug(lcPointerTests) << "BLOCK!" << event->type();
+ return true;
+ }
+ default: break;
+ }
+ return QQuickItem::event(event);
+ }
+};
+
+void tst_DragHandler::underModalLayer() // QTBUG-78258
+{
+ qmlRegisterType<ModalLayer>("Test", 1, 0, "ModalLayer");
+
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragHandlerUnderModalLayer.qml");
+ QQuickView * window = windowPtr.data();
+ QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+
+ QPoint p1(250, 250);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ p1 += QPoint(dragThreshold, dragThreshold);
+ QTest::mouseMove(window, p1);
+ QVERIFY(!dragHandler->active());
+ p1 += QPoint(dragThreshold, dragThreshold);
+ QTest::mouseMove(window, p1);
+ QVERIFY(!dragHandler->active());
+ QTest::mouseRelease(window, Qt::LeftButton);
+}
+
QTEST_MAIN(tst_DragHandler)
#include "tst_qquickdraghandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml
new file mode 100644
index 0000000000..b5c1f3d2ef
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ width: 320
+ height: 240
+ visible: true
+ color: hh.hovered ? "lightsteelblue" : "beige"
+ HoverHandler {
+ id: hh
+ cursorShape: Qt.OpenHandCursor
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index 11a5393390..c3429456ae 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -62,6 +62,7 @@ private slots:
void hoverHandlerAndUnderlyingMouseArea();
void movingItemWithHoverHandler();
void margin();
+ void window();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -363,6 +364,28 @@ void tst_HoverHandler::margin() // QTBUG-85303
#endif
}
+void tst_HoverHandler::window() // QTBUG-98717
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("windowCursorShape.qml"));
+ QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create()));
+ QVERIFY(!window.isNull());
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+#if QT_CONFIG(cursor)
+ if (isPlatformWayland())
+ QSKIP("Wayland: QCursor::setPos() doesn't work.");
+#ifdef Q_OS_MACOS
+ QSKIP("macOS: QCursor::setPos() doesn't work (QTBUG-76312).");
+#endif
+ auto cursorPos = window->mapToGlobal(QPoint(100, 100));
+ qCDebug(lcPointerTests) << "in window @" << window->position() << "setting cursor pos" << cursorPos;
+ QCursor::setPos(cursorPos);
+ QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor);
+#endif
+}
+
QTEST_MAIN(tst_HoverHandler)
#include "tst_qquickhoverhandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
index 19fdae3b44..2b8ddd197d 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
@@ -217,6 +217,7 @@ void tst_QQuickPinchHandler::scale()
QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler");
QVERIFY(pinchHandler != nullptr);
+ QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QQuickEventPoint::GrabTransition, QQuickEventPoint*)));
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root != nullptr);
@@ -238,6 +239,7 @@ void tst_QQuickPinchHandler::scale()
// it is outside its bounds.
pinchSequence.stationary(0).press(1, p1, window).commit();
QQuickTouchUtils::flush(window);
+ QTRY_COMPARE(grabChangedSpy.count(), 1); // passive grab
QPoint pd(10, 10);
// move one point until PinchHandler activates
@@ -247,6 +249,8 @@ void tst_QQuickPinchHandler::scale()
QQuickTouchUtils::flush(window);
}
QCOMPARE(pinchHandler->active(), true);
+ // first point got a passive grab; both points got exclusive grabs
+ QCOMPARE(grabChangedSpy.count(), 3);
QLineF line(p0, p1);
const qreal startLength = line.length();
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml
new file mode 100644
index 0000000000..1c18133f92
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ id: root
+ width: 100
+ height: 100
+ property int tapCount: 0
+ TapHandler {
+ onTapped: { ++root.tapCount }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index 419afed3ac..52cef6248f 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -71,15 +71,25 @@ private slots:
void buttonsMultiTouch();
void componentUserBehavioralOverride();
void rightLongPressIgnoreWheel();
+ void nonTopLevelParentWindow();
private:
- void createView(QScopedPointer<QQuickView> &window, const char *fileName);
+ void createView(QScopedPointer<QQuickView> &window, const char *fileName,
+ QWindow *parent = nullptr);
QTouchDevice *touchDevice;
+ void mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point,
+ QWindow *targetWindow, QWindow *mapToWindow);
};
-void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName)
+void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName,
+ QWindow *parent)
{
- window.reset(new QQuickView);
+ window.reset(new QQuickView(parent));
+ if (parent) {
+ parent->show();
+ QVERIFY(QTest::qWaitForWindowActive(parent));
+ }
+
window->setSource(testFileUrl(fileName));
QTRY_COMPARE(window->status(), QQuickView::Ready);
QQuickViewTestUtil::centerOnScreen(window.data());
@@ -90,6 +100,20 @@ void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *
QVERIFY(window->rootObject() != nullptr);
}
+void tst_TapHandler::mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point,
+ QWindow *targetWindow, QWindow *mapToWindow)
+{
+ QVERIFY(targetWindow);
+ QVERIFY(mapToWindow);
+ auto buttons = button;
+ if (type == QEvent::MouseButtonRelease) {
+ buttons = Qt::NoButton;
+ }
+ QMouseEvent me(type, point, mapToWindow->mapToGlobal(point), button, buttons,
+ Qt::KeyboardModifiers());
+ QVERIFY(qApp->notify(targetWindow, &me));
+}
+
void tst_TapHandler::initTestCase()
{
// This test assumes that we don't get synthesized mouse events from QGuiApplication
@@ -745,6 +769,31 @@ void tst_TapHandler::rightLongPressIgnoreWheel()
QCOMPARE(tappedSpy.count(), 0);
}
+void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716
+{
+ QScopedPointer<QQuickWindow> parentWindowPtr(new QQuickWindow);
+ auto parentWindow = parentWindowPtr.get();
+ parentWindow->setGeometry(400, 400, 250, 250);
+
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "simpleTapHandler.qml", parentWindow);
+ auto window = windowPtr.get();
+ window->setGeometry(10, 10, 100, 100);
+
+ QQuickItem *root = window->rootObject();
+
+ auto p1 = QPoint(20, 20);
+ mouseEvent(QEvent::MouseButtonPress, Qt::LeftButton, p1, window, parentWindow);
+ mouseEvent(QEvent::MouseButtonRelease, Qt::LeftButton, p1, window, parentWindow);
+
+ QCOMPARE(root->property("tapCount").toInt(), 1);
+
+ QTest::touchEvent(window, touchDevice).press(0, p1, parentWindow).commit();
+ QTest::touchEvent(window, touchDevice).release(0, p1, parentWindow).commit();
+
+ QCOMPARE(root->property("tapCount").toInt(), 2);
+}
+
QTEST_MAIN(tst_TapHandler)
#include "tst_qquicktaphandler.moc"
diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
index 0e6c3b3ad9..30d57b9c9a 100644
--- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
+++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
@@ -383,6 +383,7 @@ void tst_QQuickAccessible::basicPropertiesTest()
QCOMPARE(item->indexOfChild(text2), 1);
QCOMPARE(text2->state().editable, 0u);
QCOMPARE(text2->state().readOnly, 1);
+ QCOMPARE(text2->state().focusable, 1);
QCOMPARE(iface->indexOfChild(text2), -1);
QCOMPARE(text2->indexOfChild(item), -1);
diff --git a/tests/auto/quick/qquickanimations/BLACKLIST b/tests/auto/quick/qquickanimations/BLACKLIST
new file mode 100644
index 0000000000..6c1bec6520
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/BLACKLIST
@@ -0,0 +1,3 @@
+# See qtbase/src/testlib/qtestblacklist.cpp for format
+[reparent]
+ci macos # QTBUG-108880
diff --git a/tests/auto/quick/qquickanimations/data/cleanupWhenRenderThreadStops.qml b/tests/auto/quick/qquickanimations/data/cleanupWhenRenderThreadStops.qml
new file mode 100644
index 0000000000..4cfb59aeab
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/cleanupWhenRenderThreadStops.qml
@@ -0,0 +1,32 @@
+//main.qml
+
+import QtQuick 2.12
+import QtQuick.Window 2.12
+
+Item {
+ id: root
+ width: 640
+ height: 480
+ visible: true
+ property bool running : false
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ color: "red"
+
+ Component.onCompleted: {
+ anim.start()
+ running = true
+ }
+ }
+
+ OpacityAnimator {
+ id: anim
+
+ target: rect
+ from: 1
+ to: 0
+ duration: 20000
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 9b5710bad4..95af4a0560 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -116,6 +116,7 @@ private slots:
void fastFlickingBug();
void opacityAnimationFromZero();
void alwaysRunToEndInSequentialAnimationBug();
+ void cleanupWhenRenderThreadStops();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -1998,6 +1999,19 @@ void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug()
QCOMPARE(whiteRect->property("opacity").value<qreal>(),1.0);
}
+void tst_qquickanimations::cleanupWhenRenderThreadStops()
+{
+ QQuickView view(QUrl::fromLocalFile("data/cleanupWhenRenderThreadStops.qml"));
+ view.show();
+ view.setPersistentOpenGLContext(false);
+ view.setPersistentSceneGraph(false);
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QTest::qWait(50);
+ view.hide();
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquickbehaviors/BLACKLIST b/tests/auto/quick/qquickbehaviors/BLACKLIST
new file mode 100644
index 0000000000..d2a11a6a32
--- /dev/null
+++ b/tests/auto/quick/qquickbehaviors/BLACKLIST
@@ -0,0 +1,3 @@
+# See qtbase/src/testlib/qtestblacklist.cpp for format
+[currentValue]
+ci macos # QTBUG-108258
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
index dc960a24d0..fb4db5ae54 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
@@ -894,6 +894,37 @@ CanvasTestCase {
comparePixel(ctx, 39,0, 0,0,0,0);
}
+ function test_lineDashReset(row) {
+ var canvas = createCanvasObject(row);
+ var ctx = canvas.getContext('2d');
+ ctx.reset();
+ ctx.strokeStyle = "#ff0000";
+ ctx.lineWidth = 2;
+ var pattern = [2, 3, 5, 1, 6, 3]
+ ctx.setLineDash(pattern)
+
+ compare(ctx.getLineDash(), pattern);
+
+ pattern = []
+ ctx.setLineDash(pattern)
+ compare(ctx.getLineDash(), pattern);
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(40, 0);
+ ctx.stroke();
+
+ comparePixel(ctx, 0,0, 255,0,0,255);
+ comparePixel(ctx, 4,0, 255,0,0,255);
+ comparePixel(ctx, 5,0, 255,0,0,255);
+ comparePixel(ctx, 14,0, 255,0,0,255);
+ comparePixel(ctx, 20,0, 255,0,0,255);
+ comparePixel(ctx, 21,0, 255,0,0,255);
+ comparePixel(ctx, 22,0, 255,0,0,255);
+ comparePixel(ctx, 34,0, 255,0,0,255);
+ comparePixel(ctx, 35,0, 255,0,0,255);
+ }
+
function test_lineDashOffset(row) {
var canvas = createCanvasObject(row);
var ctx = canvas.getContext('2d');
diff --git a/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml
new file mode 100644
index 0000000000..ec419f8935
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/RecursiveProperty.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Item {
+ id: myObject
+ readonly property int testProperty: 0
+ readonly property QtObject myproperty: myObject
+}
+
diff --git a/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml
new file mode 100644
index 0000000000..88fd8509cc
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/propertyNameTest.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.11
+
+Rectangle {
+ objectName: "rootItem"
+ color: "white"
+ width: 800
+ height: 600
+
+ RecursiveProperty {
+ objectName: "recursiveProperty"
+
+ }
+}
diff --git a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
index b44977bd5a..0471619049 100644
--- a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
+++ b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
@@ -62,6 +62,7 @@ private slots:
void testNotifyPropertyChangeCallBack();
void testFixResourcePathsForObjectCallBack();
void testComponentOnCompleteSignal();
+ void testPropertyNames();
};
void tst_qquickdesignersupport::customData()
@@ -586,6 +587,50 @@ void tst_qquickdesignersupport::testComponentOnCompleteSignal()
}
}
+void tst_qquickdesignersupport::testPropertyNames()
+{
+#ifdef Q_CC_MINGW
+ QSKIP("QQuickDesignerSupportProperties::registerCustomData segfaults on mingw. QTBUG-90869");
+#endif
+
+ QScopedPointer<QQuickView> view(new QQuickView);
+ view->engine()->setOutputWarningsToStandardError(false);
+ view->setSource(testFileUrl("propertyNameTest.qml"));
+
+ QVERIFY(view->errors().isEmpty());
+ QQuickItem *rootItem = view->rootObject();
+ QVERIFY(rootItem);
+
+ QQuickDesignerSupport::PropertyNameList names = QQuickDesignerSupportProperties::allPropertyNames(rootItem);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("width"));
+ QVERIFY(names.contains("height"));
+ QVERIFY(names.contains("clip"));
+ QVERIFY(names.contains("opacity"));
+ QVERIFY(names.contains("childrenRect"));
+ QVERIFY(names.contains("activeFocus"));
+ QVERIFY(names.contains("border.width"));
+ names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(rootItem);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("width"));
+ QVERIFY(names.contains("height"));
+ QVERIFY(names.contains("opacity"));
+ QVERIFY(names.contains("clip"));
+ QVERIFY(!names.contains("childrenRect"));
+ QVERIFY(!names.contains("activeFocus"));
+ QVERIFY(names.contains("border.width"));
+
+ QQuickItem *recursiveProperty = findItem<QQuickItem>(rootItem, QLatin1String("recursiveProperty"));
+ QVERIFY(recursiveProperty);
+ names = QQuickDesignerSupportProperties::allPropertyNames(recursiveProperty);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(names.contains("testProperty"));
+ QVERIFY(names.contains("myproperty.testProperty"));
+
+ names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(recursiveProperty);
+ QVERIFY(!names.isEmpty());
+ QVERIFY(!names.contains("testProperty"));
+}
QTEST_MAIN(tst_qquickdesignersupport)
diff --git a/tests/auto/quick/qquickdroparea/BLACKLIST b/tests/auto/quick/qquickdroparea/BLACKLIST
new file mode 100644
index 0000000000..0967969b04
--- /dev/null
+++ b/tests/auto/quick/qquickdroparea/BLACKLIST
@@ -0,0 +1,4 @@
+# Blacklist for testing
+# QTBUG-99765
+[containsDrag_internal]
+ubuntu-20.04
diff --git a/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml
new file mode 100644
index 0000000000..57a4273257
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.14
+
+Item {
+ id: root
+ width: 500
+ height: 500
+ Flickable {
+ anchors.centerIn: parent
+ width: 100
+ height: 100
+ clip: true
+ contentWidth: content.width
+ contentHeight: content.height
+ Rectangle {
+ id: content
+ width: 320
+ height: width
+ color: "#41cd52"
+ radius: width/2
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index f3659290eb..62f7c67dd4 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -27,10 +27,10 @@
****************************************************************************/
#include <qtest.h>
#include <QtTest/QSignalSpy>
+#include <QtQuick/qquickview.h>
#include <QtGui/QStyleHints>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
-#include <QtQuick/qquickview.h>
#include <private/qquickflickable_p.h>
#include <private/qquickflickable_p_p.h>
#include <private/qquickmousearea_p.h>
@@ -206,6 +206,8 @@ private slots:
void synchronousDrag_data();
void synchronousDrag();
void visibleAreaBinding();
+ void setContentPositionWhileDragging_data();
+ void setContentPositionWhileDragging();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
@@ -870,6 +872,7 @@ void tst_qquickflickable::wheel()
QVERIFY(flick != nullptr);
QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flick);
QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
+ quint64 timestamp = 10;
// test a vertical flick
{
@@ -877,6 +880,7 @@ void tst_qquickflickable::wheel()
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120),
Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
event.setAccepted(false);
+ event.setTimestamp(timestamp);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -887,6 +891,7 @@ void tst_qquickflickable::wheel()
QCOMPARE(fp->velocityTimeline.isActive(), false);
QCOMPARE(fp->timeline.isActive(), false);
QTest::qWait(50); // make sure that onContentYChanged won't sneak in again
+ timestamp += 50;
QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
// get ready to test horizontal flick
@@ -900,8 +905,8 @@ void tst_qquickflickable::wheel()
QPoint pos(200, 200);
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0),
Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
-
event.setAccepted(false);
+ event.setTimestamp(timestamp);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -926,11 +931,13 @@ void tst_qquickflickable::trackpad()
QVERIFY(flick != nullptr);
QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
QPoint pos(200, 200);
+ quint64 timestamp = 10;
{
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120),
Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false);
event.setAccepted(false);
+ event.setTimestamp(timestamp++);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -944,6 +951,7 @@ void tst_qquickflickable::trackpad()
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0),
Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate, false);
event.setAccepted(false);
+ event.setTimestamp(timestamp++);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -954,6 +962,7 @@ void tst_qquickflickable::trackpad()
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0),
Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false);
event.setAccepted(false);
+ event.setTimestamp(timestamp++);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -2551,6 +2560,115 @@ void tst_qquickflickable::visibleAreaBinding()
// Shouldn't crash.
}
+void tst_qquickflickable::setContentPositionWhileDragging_data()
+{
+ QTest::addColumn<bool>("isHorizontal");
+ QTest::addColumn<int>("newPos");
+ QTest::addColumn<int>("newExtent");
+ QTest::newRow("horizontal, setContentX") << true << 0 << -1;
+ QTest::newRow("vertical, setContentY") << false << 0 << -1;
+ QTest::newRow("horizontal, setContentWidth") << true << -1 << 200;
+ QTest::newRow("vertical, setContentHeight") << false << -1 << 200;
+}
+
+void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966
+{
+ QFETCH(bool, isHorizontal);
+ QFETCH(int, newPos);
+ QFETCH(int, newExtent);
+
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("contentPosWhileDragging.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtil::centerOnScreen(window.data());
+ QQuickViewTestUtil::moveMouseAway(window.data());
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QQuickItem *rootItem = window->rootObject();
+ QVERIFY(rootItem);
+
+ QVERIFY(window->isVisible());
+ QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable *>();
+ QVERIFY(flickable);
+
+ const auto contentPos = [flickable]() -> QPoint {
+ return QPoint(flickable->contentX(), flickable->contentY());
+ };
+ const qreal threshold =
+ qApp->styleHints()->startDragDistance() * flickable->parentItem()->scale();
+ const QPoint thresholdPnt(qRound(threshold), qRound(threshold));
+ const auto flickableCenterPos = flickable->mapToScene({flickable->width() / 2, flickable->height() / 2}).toPoint();
+
+ // Drag the mouse until we have surpassed the mouse drag threshold and a drag is initiated
+ // by checking for flickable->isDragging()
+ QPoint pos = flickableCenterPos;
+ moveAndPress(window.data(), pos);
+ int j = 1;
+ QVERIFY(!flickable->isDragging());
+ while (!flickable->isDragging()) {
+ pos = flickableCenterPos - QPoint(j, j);
+ QTest::mouseMove(window.data(), pos);
+ j++;
+ }
+
+ // Now we have entered the drag state
+ QVERIFY(flickable->isDragging());
+ QCOMPARE(flickable->contentX(), 0);
+ QCOMPARE(flickable->contentY(), 0);
+ QVERIFY(flickable->width() > 0);
+ QVERIFY(flickable->height() > 0);
+
+
+ const int moveLength = 50;
+ const QPoint unitDelta(isHorizontal ? 1 : 0, isHorizontal ? 0 : 1);
+ const QPoint moveDelta = unitDelta * moveLength;
+
+ pos -= 3*moveDelta;
+ QTest::mouseMove(window.data(), pos);
+ // Should be positive because we drag in the opposite direction
+ QCOMPARE(contentPos(), 3 * moveDelta);
+ QPoint expectedContentPos;
+
+ // Set the content item position back to zero *while dragging* (!!)
+ if (newPos >= 0) {
+ if (isHorizontal) {
+ flickable->setContentX(newPos);
+ } else {
+ flickable->setContentY(newPos);
+ }
+ // Continue dragging
+ pos -= moveDelta;
+ expectedContentPos = moveDelta;
+ } else if (newExtent >= 0) {
+ // ...or reduce the content size be be less than current (contentX, contentY) position
+ // This forces the content item to move.
+ // contentY: 150
+ // 320 - 150 = 170 pixels down to bottom
+ // Now reduce contentHeight to 200
+ // since we are at the bottom, and the flickable is 100 pixels tall, contentY must land
+ // at newExtent - 100.
+
+ if (isHorizontal) {
+ flickable->setContentWidth(newExtent);
+ } else {
+ flickable->setContentHeight(newExtent);
+ }
+ // Assumption is that the contentItem is aligned to the bottom of the flickable
+ // We therefore cannot scroll/flick it further down. Drag it up towards the top instead
+ // (by moving mouse down).
+ pos += moveDelta;
+ expectedContentPos = unitDelta * (newExtent - (isHorizontal ? flickable->width() : flickable->height()));
+ }
+
+ QTest::mouseMove(window.data(), pos);
+
+ // Make sure that the contentItem was only dragged the delta in mouse movement since the last
+ // setContentX/Y() call.
+ QCOMPARE(contentPos(), expectedContentPos);
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, pos);
+ QVERIFY(!flickable->isDragging());
+}
+
QTEST_MAIN(tst_qquickflickable)
#include "tst_qquickflickable.moc"
diff --git a/tests/auto/quick/qquickframebufferobject/BLACKLIST b/tests/auto/quick/qquickframebufferobject/BLACKLIST
index 93dd7e9d1c..cf30c293dd 100644
--- a/tests/auto/quick/qquickframebufferobject/BLACKLIST
+++ b/tests/auto/quick/qquickframebufferobject/BLACKLIST
@@ -1,3 +1,4 @@
+[testInvalidate)
# QTBUG-65614
b2qt
[testThatStuffWorks]
diff --git a/tests/auto/quick/qquickgridview/data/qtbug92998.qml b/tests/auto/quick/qquickgridview/data/qtbug92998.qml
new file mode 100644
index 0000000000..204e6d0b87
--- /dev/null
+++ b/tests/auto/quick/qquickgridview/data/qtbug92998.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.0
+
+Item {
+ width: 450
+ height: 650
+ GridView {
+ objectName: "gridview"
+ id: gridView
+ width: 450
+ height: 650
+ layoutDirection: Qt.RightToLeft
+ property int cells: calcCells(width)
+ cellWidth: width / cells
+ cellHeight: cellWidth
+
+ delegate: Component {
+ Item {
+ width: gridView.cellWidth
+ height: gridView.cellHeight
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ color: "green"
+ }
+ }
+ }
+ model: [
+ { number: "1" },
+ { number: "2" },
+ { number: "3" },
+ { number: "4" },
+ { number: "5" },
+ { number: "6" },
+ { number: "7" },
+ { number: "8" },
+ { number: "9" },
+ { number: "10" },
+ { number: "11" },
+ { number: "12" },
+ { number: "13" },
+ { number: "14" },
+ { number: "15" },
+ { number: "16" }];
+ function calcCells(w) {
+ var rw = 120;
+ var c = Math.max(1, Math.round(w / rw));
+ return c;
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 46e3457d6e..94ec4f44d5 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -215,6 +215,7 @@ private slots:
void QTBUG_48870_fastModelUpdates();
void keyNavigationEnabled();
+ void resizeDynamicCellWidthRtL();
void releaseItems();
private:
@@ -6795,6 +6796,24 @@ void tst_QQuickGridView::QTBUG_48870_fastModelUpdates()
}
}
+void tst_QQuickGridView::resizeDynamicCellWidthRtL()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("qtbug92998.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickGridView *gridview = findItem<QQuickGridView>(window->rootObject(), "gridview");
+ QTRY_VERIFY(gridview != nullptr);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ gridview->setWidth(460);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ QTRY_COMPARE(gridview->contentX(), 0.f);
+ gridview->setWidth(360);
+ QVERIFY(QQuickTest::qWaitForItemPolished(gridview));
+ QTRY_COMPARE(gridview->contentX(), 0.f);
+}
+
void tst_QQuickGridView::releaseItems()
{
QScopedPointer<QQuickView> view(createView());
diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
index 9dc9ad53ea..cb9e8d1ce2 100644
--- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
+++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
@@ -72,7 +72,7 @@ private slots:
private:
QString newImageFileName() const;
- void fillRequestTestsData(const QString &id);
+ void fillRequestTestsData(const char *id);
void runTest(bool async, QQuickImageProvider *provider);
};
@@ -161,7 +161,7 @@ QString tst_qquickimageprovider::newImageFileName() const
return QString("image://test/image-%1.png").arg(count++);
}
-void tst_qquickimageprovider::fillRequestTestsData(const QString &id)
+void tst_qquickimageprovider::fillRequestTestsData(const char *id)
{
QTest::addColumn<QString>("source");
QTest::addColumn<QString>("imageId");
@@ -170,39 +170,39 @@ void tst_qquickimageprovider::fillRequestTestsData(const QString &id)
QTest::addColumn<QString>("error");
QString fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " simple test"))
+ QTest::addRow("%s simple test", id)
<< "image://test/" + fileName << fileName << "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " simple test with capitalization"))//As it's a URL, should make no difference
+ QTest::addRow("%s simple test with capitalization", id)//As it's a URL, should make no difference
<< "image://Test/" + fileName << fileName << "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " url with no id"))
+ QTest::addRow("%s url with no id", id)
<< "image://test/" + fileName << "" + fileName << "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " url with path"))
+ QTest::addRow("%s url with path", id)
<< "image://test/test/path" + fileName << "test/path" + fileName << "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " url with fragment"))
+ QTest::addRow("%s url with fragment", id)
<< "image://test/faq.html?#question13" + fileName << "faq.html?#question13" + fileName << "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " url with query"))
+ QTest::addRow("%s url with query", id)
<< "image://test/cgi-bin/drawgraph.cgi?type=pie&color=green" + fileName << "cgi-bin/drawgraph.cgi?type=pie&color=green" + fileName
<< "" << QSize(100,100) << "";
fileName = newImageFileName();
- QTest::newRow(QTest::toString(id + " scaled image"))
+ QTest::addRow("%s scaled image", id)
<< "image://test/" + fileName << fileName << "sourceSize: \"80x30\"" << QSize(80,30) << "";
- QTest::newRow(QTest::toString(id + " missing"))
+ QTest::addRow("%s missing", id)
<< "image://test/no-such-file.png" << "no-such-file.png" << "" << QSize(100,100)
<< "<Unknown File>:2:1: QML Image: Failed to get image from provider: image://test/no-such-file.png";
- QTest::newRow(QTest::toString(id + " unknown provider"))
+ QTest::addRow("%s unknown provider", id)
<< "image://bogus/exists.png" << "" << "" << QSize()
<< "<Unknown File>:2:1: QML Image: Invalid image provider: image://bogus/exists.png";
}
diff --git a/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml
new file mode 100644
index 0000000000..a690c4243b
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/focusableItemReparentedToLoadedComponent.qml
@@ -0,0 +1,51 @@
+import QtQuick 2.12
+
+Item {
+ width: 240; height: 240
+ Loader {
+ id: loader
+ sourceComponent: surfaceParent
+ anchors.fill: parent
+
+ onStatusChanged: {
+ if (status === Loader.Ready) {
+ holder.create()
+ holder.item.parent = item
+ } else if (status === Loader.Null){
+ holder.item.parent = null
+ }
+ }
+ }
+
+ property var holder: QtObject {
+ property bool created: false
+ function create()
+ {
+ if (!created)
+ surfaceComponent.createObject(item)
+ created = true
+ }
+
+ property Item item: Item {
+ anchors.fill: parent
+ Component {
+ id: surfaceComponent
+ Item {
+ anchors.fill: parent
+ TextInput {
+ width: parent.width
+ font.pixelSize: 40
+ text: "focus me"
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: surfaceParent
+ Rectangle {
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index f65650cf9c..c8f251dbe1 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -33,6 +33,7 @@
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/qquickview.h>
#include <QtGui/private/qinputmethod_p.h>
+#include <QtQuick/private/qquickloader_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
@@ -75,6 +76,7 @@ private slots:
void qtbug_50516();
void qtbug_50516_2_data();
void qtbug_50516_2();
+ void focusableItemReparentedToLoadedComponent();
void keys();
#if QT_CONFIG(shortcut)
@@ -1312,6 +1314,35 @@ void tst_QQuickItem::qtbug_50516_2()
delete window;
}
+void tst_QQuickItem::focusableItemReparentedToLoadedComponent() // QTBUG-89736
+{
+ QQuickView window;
+ window.setSource(testFileUrl("focusableItemReparentedToLoadedComponent.qml"));
+ window.show();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
+ QCOMPARE(QGuiApplication::focusWindow(), &window);
+ QQuickLoader *loader = window.rootObject()->findChild<QQuickLoader *>();
+ QVERIFY(loader);
+ QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
+ QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput *>();
+ QVERIFY(textInput);
+
+ // click to focus
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+ QTRY_VERIFY(textInput->hasActiveFocus());
+
+ // unload and reload
+ auto component = loader->sourceComponent();
+ loader->resetSourceComponent();
+ QTRY_VERIFY(loader->status() == QQuickLoader::Null);
+ loader->setSourceComponent(component);
+ QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
+
+ // click to focus again
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+ QTRY_VERIFY(textInput->hasActiveFocus());
+}
+
void tst_QQuickItem::keys()
{
QQuickView *window = new QQuickView(nullptr);
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
index 85fe54eca6..87e0a01df2 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -1233,5 +1233,42 @@ Item {
// DO NOT CRASH due to stack overflow (or loop endlessly due to updatePolish()/polish() loop)
}
+
+ Component {
+ id: layout_dependentWidth_QTBUG_87253_Component
+
+ RowLayout {
+ anchors.fill: parent;
+
+ RowLayout {
+ spacing: 10
+
+ Text {
+ id: btnOPE
+ text: qsTr("Ok")
+ Layout.fillWidth: true
+ Layout.preferredWidth: (parent.width - 20) / 2
+ }
+
+ Text {
+ id: btnSeeChanged
+ text: qsTr("Not Ok");
+ Layout.fillWidth: true
+ Layout.preferredWidth: (parent.width - 20) / 2
+ }
+ }
+ }
+ }
+
+ function test_dependentWidth_QTBUG_87253()
+ {
+ var warningMsg = new RegExp("Qt Quick Layouts: Detected recursive rearrange. "
+ + "Aborting after two iterations.")
+ for (var i = 0; i < 10; ++i) {
+ ignoreWarning(warningMsg)
+ }
+ var layout = createTemporaryObject(layout_dependentWidth_QTBUG_87253_Component, container)
+ waitForRendering(layout)
+ }
}
}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
index 3a41bdb3e0..2536b9789f 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
@@ -51,6 +51,7 @@
import QtQuick 2.2
import QtTest 1.0
import QtQuick.Layouts 1.3
+import QtQuick.Window 2.1
Item {
id: container
@@ -103,5 +104,467 @@ Item {
layout.destroy()
}
+
+ Component {
+ id: layout_setCurrentIndex_Component
+
+ StackLayout {
+ width: 200
+ height: 200
+
+ property alias firstItem : rect
+ property alias secondItem: rowLayout
+
+ Rectangle {
+ id: rect
+ color: "red"
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ RowLayout {
+ id: rowLayout
+ spacing: 0
+ Rectangle {
+ color: "green"
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ color: "blue"
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_setCurrentIndex()
+ {
+ var layout = layout_setCurrentIndex_Component.createObject(container)
+ compare(layout.firstItem.width, 200)
+
+ // Invalidate the StackLayout (and its cached size hints)
+ layout.firstItem.implicitWidth = 42
+
+ layout.currentIndex = 1
+ compare(layout.secondItem.width, 200) // width should not be -1
+ layout.destroy()
+ }
+
+ function geometry(item) {
+ return [item.x, item.y, item.width, item.height]
+ }
+
+ Component {
+ id: countGeometryChanges_Component
+ StackLayout {
+ id: stack
+ property alias col: _col
+ property alias row: _row
+ width: 100
+ ColumnLayout {
+ id: _col
+ property alias r1: _r1
+ property alias r2: _r2
+ property alias r3: _r3
+ spacing: 0
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ Rectangle {
+ id: _r1
+ implicitWidth: 20
+ implicitHeight: 20
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ Rectangle {
+ id: _r2
+ implicitWidth: 50
+ implicitHeight: 50
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ Rectangle {
+ id: _r3
+ implicitWidth: 40
+ implicitHeight: 40
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ }
+ RowLayout {
+ id: _row
+ property alias r5: _r5
+ spacing: 0
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ Rectangle {
+ id: _r5
+ implicitWidth: 100
+ implicitHeight: 100
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ }
+ }
+ }
+
+ function test_countGeometryChanges() {
+
+ var stack = countGeometryChanges_Component.createObject(container)
+ compare(stack.currentIndex, 0)
+ compare(stack.col.width, 100)
+ compare(stack.col.height, 110)
+ compare(stack.row.width, 100)
+ compare(stack.row.height, 100)
+ verify(stack.col.r1.counter <= 2)
+ compare(stack.col.r2.counter, 1)
+ verify(stack.col.r3.counter <= 2)
+ verify(stack.col.counter <= 2)
+ compare(stack.row.counter, 1) // not visible, will only receive the initial geometry change
+ compare(stack.row.r5.counter, 0)
+ stack.destroy()
+ }
+
+
+ Component {
+ id: layoutItem_Component
+ Rectangle {
+ implicitWidth: 20
+ implicitHeight: 20
+ }
+ }
+
+ Component {
+ id: emtpy_StackLayout_Component
+ StackLayout {
+ property int num_onCountChanged: 0
+ property int num_onCurrentIndexChanged: 0
+ onCountChanged: { ++num_onCountChanged; }
+ onCurrentIndexChanged: { ++num_onCurrentIndexChanged; }
+ }
+ }
+
+ function test_addAndRemoveItems()
+ {
+ var stack = emtpy_StackLayout_Component.createObject(container)
+ stack.currentIndex = 2
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+
+ var rect0 = layoutItem_Component.createObject(stack)
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 20)
+ compare(stack.implicitHeight, 20)
+ compare(rect0.visible, false)
+
+ var rect1 = layoutItem_Component.createObject(stack)
+ rect1.Layout.preferredWidth = 30
+ rect1.Layout.preferredHeight = 10
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 20)
+ compare(rect0.visible, false)
+ compare(rect1.visible, false)
+
+ var rect2 = layoutItem_Component.createObject(stack)
+ rect2.x = 42 // ### items in a stacklayout will have their x and y positions discarded.
+ rect2.y = 42
+ rect2.Layout.preferredWidth = 80
+ rect2.Layout.preferredHeight = 30
+ rect2.Layout.fillWidth = true
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 80)
+ compare(stack.implicitHeight, 30)
+ compare(rect0.visible, false)
+ compare(rect1.visible, false)
+ compare(rect2.visible, true)
+ compare(geometry(rect2), geometry(stack))
+
+ rect2.destroy()
+ wait(0)
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 20)
+
+ rect0.destroy()
+ wait(0)
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 10)
+
+ rect1.destroy()
+ wait(0)
+ verify(waitForItemPolished(stack))
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+
+ stack.destroy()
+ }
+
+ function test_sizeHint_data() {
+ return [
+ { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, Number.POSITIVE_INFINITY]},
+ { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, Number.POSITIVE_INFINITY]},
+ { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, Number.POSITIVE_INFINITY]},
+ { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, Number.POSITIVE_INFINITY]},
+ { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, Number.POSITIVE_INFINITY]},
+ { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, Number.POSITIVE_INFINITY]},
+ { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, Number.POSITIVE_INFINITY]},
+ ];
+ }
+
+ function itemSizeHints(item) {
+ return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth]
+ }
+ Component {
+ id: stacklayout_sizeHint_Component
+ StackLayout {
+ property int implicitWidthChangedCount : 0
+ onImplicitWidthChanged: { ++implicitWidthChangedCount }
+ ColumnLayout {
+ Rectangle {
+ id: r1
+ color: "red"
+ Layout.minimumWidth: 1
+ Layout.preferredWidth: 2
+ Layout.maximumWidth: 3
+
+ Layout.minimumHeight: 20
+ Layout.preferredHeight: 20
+ Layout.maximumHeight: 20
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+ function test_sizeHint(data) {
+ var layout = stacklayout_sizeHint_Component.createObject(container)
+
+ var col = layout.children[0]
+ col.Layout.minimumWidth = data.layoutHints[0]
+ col.Layout.preferredWidth = data.layoutHints[1]
+ col.Layout.maximumWidth = data.layoutHints[2]
+
+ var child = col.children[0]
+ if (data.implicitWidth !== undefined) {
+ child.implicitWidth = data.implicitWidth
+ }
+ child.Layout.minimumWidth = data.childHints[0]
+ child.Layout.preferredWidth = data.childHints[1]
+ child.Layout.maximumWidth = data.childHints[2]
+
+ verify(waitForItemPolished(layout))
+ var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth]
+ compare(effectiveSizeHintResult, data.expected)
+ layout.destroy()
+ }
+
+ Component {
+ id: stacklayout_addIgnoredItem_Component
+ StackLayout {
+ Repeater {
+ id: rep
+ model: 1
+ Rectangle {
+ id: r
+ }
+ }
+ }
+ }
+
+ // Items with no size information is ignored.
+ function test_addIgnoredItem()
+ {
+ var stack = stacklayout_addIgnoredItem_Component.createObject(container)
+ compare(stack.count, 1)
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+ var r = stack.children[0]
+ r.Layout.preferredWidth = 20
+ r.Layout.preferredHeight = 30
+ verify(waitForItemPolished(stack))
+ compare(stack.count, 1)
+ compare(stack.implicitWidth, 20)
+ compare(stack.implicitHeight, 30)
+ stack.destroy();
+ }
+
+ function test_dontCrashWhenAnchoredToAWindow() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Window 2.1; \
+ import QtQuick.Layouts 1.3; \
+ Window { \
+ visible: true; \
+ width: stack.implicitWidth; \
+ height: stack.implicitHeight; \
+ StackLayout { \
+ id: stack; \
+ currentIndex: 0; \
+ anchors.fill: parent; \
+ Rectangle { \
+ color: "red"; \
+ implicitWidth: 300; \
+ implicitHeight: 200; \
+ } \
+ } \
+ } '
+
+ var win = Qt.createQmlObject(test_layoutStr, container, '');
+ if (win.visibility === Window.Windowed) {
+ // on single-window systems (such as Android), the window geometry will be
+ // fullscreen, and most likely it will be set to screen size. Avoid this test for
+ // those systems, as the size of the window will not be determined by the layout
+ tryCompare(win, 'width', 300);
+ }
+ win.destroy()
+ }
+
+ Component {
+ id: test_dontCrashWhenChildIsResizedToNull_Component
+ StackLayout {
+ property alias rect : _rect
+ Rectangle {
+ id: _rect;
+ color: "red"
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+ }
+
+ function test_dontCrashWhenChildIsResizedToNull() {
+ var layout = test_dontCrashWhenChildIsResizedToNull_Component.createObject(container)
+ layout.rect.width = 0
+ layout.width = 222 // trigger a rearrange with a valid size
+ layout.height = 222
+ }
+
+ Component {
+ id: test_currentIndex_Component
+ StackLayout {
+ currentIndex: 1
+ Text {
+ text: "0"
+ }
+ Text {
+ text: "1"
+ }
+ }
+ }
+
+ function test_currentIndex() {
+ var layout = test_currentIndex_Component.createObject(container)
+ var c0 = layout.children[0]
+ var c1 = layout.children[1]
+ compare(layout.currentIndex, 1)
+ tryCompare(layout, 'visible', true)
+ compare(c0.visible, false)
+ compare(c1.visible, true)
+ layout.currentIndex = 0
+ compare(c0.visible, true)
+ compare(c1.visible, false)
+ var c2 = layoutItem_Component.createObject(layout)
+ compare(c2.visible, false)
+
+ /*
+ * destroy the current item and check if visibility advances to next
+ */
+ c0.destroy()
+ tryCompare(c1, 'visible', true)
+ compare(c2.visible, false)
+ c1.destroy()
+ tryCompare(c2, 'visible', true)
+ c2.destroy()
+ tryCompare(layout, 'currentIndex', 0)
+
+ layout.destroy()
+
+ /*
+ * Test the default/implicit value of currentIndex, either -1 (if empty) or 0:
+ */
+ layout = emtpy_StackLayout_Component.createObject(container)
+ tryCompare(layout, 'visible', true)
+ compare(layout.currentIndex, -1)
+ compare(layout.num_onCurrentIndexChanged, 0)
+ // make it non-empty
+ c0 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 0)
+ compare(layout.num_onCurrentIndexChanged, 1)
+ compare(c0.visible, true)
+ // make it empty again
+ c0.destroy()
+ wait(0)
+ compare(layout.currentIndex, -1)
+ //tryCompare(layout, 'currentIndex', -1)
+ compare(layout.num_onCurrentIndexChanged, 2)
+
+ /*
+ * Check that explicit value doesn't change,
+ * and that no items are visible if the index is invalid/out of range
+ */
+ layout.currentIndex = 2
+ compare(layout.currentIndex, 2)
+ compare(layout.num_onCurrentIndexChanged, 3)
+ c0 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+
+ c1 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+
+ c2 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+ compare(c2.visible, true)
+
+ c2.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+ c1.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ c0.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(layout.num_onCurrentIndexChanged, 3)
+ }
+
+ function test_count() {
+ var layout = emtpy_StackLayout_Component.createObject(container)
+ tryCompare(layout, 'visible', true)
+ compare(layout.count, 0)
+ compare(layout.currentIndex, -1)
+ compare(layout.num_onCountChanged, 0)
+ compare(layout.num_onCurrentIndexChanged, 0)
+ var c0 = layoutItem_Component.createObject(layout)
+ compare(layout.count, 1)
+ compare(layout.currentIndex, 0)
+ compare(layout.num_onCurrentIndexChanged, 1)
+ compare(layout.num_onCountChanged, 1)
+ }
+
+
}
}
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index 6ef69550a4..cba2e8d752 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -9,6 +9,8 @@ macos
#QTBUG-75960
#QTBUG-76652
[currentIndex]
+# QTBUG-101498
+ubuntu-20
macos
opensuse-leap
ubuntu-18.04
diff --git a/tests/auto/quick/qquicklistview/data/AnimatedButton.qml b/tests/auto/quick/qquicklistview/data/AnimatedButton.qml
new file mode 100644
index 0000000000..19cd580cf5
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/AnimatedButton.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.12
+
+/*
+ \qmltype AnimatedButton
+*/
+Rectangle {
+ width: 100
+ height: 20
+ Behavior on color { ColorAnimation { duration : 300 } }
+}
diff --git a/tests/auto/quick/qquicklistview/data/animatedDelegate.qml b/tests/auto/quick/qquicklistview/data/animatedDelegate.qml
new file mode 100644
index 0000000000..46913d87d7
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/animatedDelegate.qml
@@ -0,0 +1,35 @@
+import QtQuick 2.12
+
+Item {
+ width: 640
+ height: 480
+
+ property var moreModel: [myDataModelContainer.createObject(null)]
+ property Component myDataModelContainer: Component {
+ QtObject {}
+ }
+
+ function refreshModel() {
+ //copy contents of moreModel
+ var list = moreModel.slice()
+
+ moreModel = [myDataModelContainer.createObject(null), myDataModelContainer.createObject(null)]
+
+ for (var i = 0; i < list.length; i++) {
+ //console.log("trying to destroy ="+list[i])
+ list[i].destroy()
+ }
+ }
+
+ ListView {
+ id: listView
+ objectName: "listView"
+ anchors.fill: parent
+ model: moreModel
+ focus: true
+
+ delegate: AnimatedButton {
+ color: ListView.isCurrentItem ? "red" : "black"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/data/delegateWithMouseArea2.qml b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea2.qml
new file mode 100644
index 0000000000..dee89ae4cd
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea2.qml
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.15
+
+Rectangle {
+
+ width: 240
+ height: 320
+ color: "#ffffff"
+
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ width: list.orientation == ListView.Vertical ? 240 : 20
+ height: list.orientation == ListView.Vertical ? 20 : 240
+ border.width: 1
+ border.color: "black"
+ MouseArea {
+ anchors.fill: parent
+ }
+ Text {
+ text: index + ":" + (list.orientation == ListView.Vertical ? parent.y : parent.x).toFixed(0)
+ }
+ color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+
+ ListView {
+ id: list
+ objectName: "list"
+ focus: true
+ width: 240
+ height: 200
+ clip: true
+ model: 30
+ headerPositioning: ListView.OverlayHeader
+ delegate: myDelegate
+
+ header: Rectangle {
+ width: list.orientation == Qt.Vertical ? 240 : 30
+ height: list.orientation == Qt.Vertical ? 30 : 240
+ color: "green"
+ z: 11
+ Text {
+ anchors.centerIn: parent
+ text: "header " + (list.orientation == ListView.Vertical ? parent.y : parent.x).toFixed(1)
+ }
+ }
+ }
+
+ // debug
+ Rectangle {
+ color: "#40ff0000"
+ border.width: txt.x
+ border.color: "black"
+ radius: 5
+ width: txt.implicitWidth + 50
+ height: txt.implicitHeight + 2 * txt.x
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.left: parent.left
+
+ Text {
+ id: txt
+ x: 3
+ y: x
+ text: "header position: " + (list.orientation == ListView.Vertical ? list.headerItem.y : list.headerItem.x).toFixed(1)
+ + "\ncontent position: " + (list.orientation == ListView.Vertical ? list.contentY : list.contentX).toFixed(1)
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/data/qtbug_92809.qml b/tests/auto/quick/qquicklistview/data/qtbug_92809.qml
new file mode 100644
index 0000000000..c0b7ac546b
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/qtbug_92809.qml
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.15
+import QtQml.Models 2.12
+
+Rectangle {
+ id: root
+ width: 800
+ height: 480
+
+ property list<QtObject> myModel: [
+ QtObject { property string name: "Item 0"; property bool selected: true },
+ QtObject { property string name: "Item 1"; property bool selected: true },
+ QtObject { property string name: "Item 2"; property bool selected: true },
+ QtObject { property string name: "Item 3"; property bool selected: true },
+ QtObject { property string name: "Item 4"; property bool selected: true },
+ QtObject { property string name: "Item 5"; property bool selected: true },
+ QtObject { property string name: "Item 6"; property bool selected: true },
+ QtObject { property string name: "Item 7"; property bool selected: true },
+ QtObject { property string name: "Item 8"; property bool selected: true },
+ QtObject { property string name: "Item 9"; property bool selected: true },
+ QtObject { property string name: "Press Enter here"; property bool selected: true }
+ ]
+
+ DelegateModel {
+ objectName: "model"
+ id: visualModel
+ model: myModel
+ filterOnGroup: "selected"
+
+ groups: [
+ DelegateModelGroup {
+ name: "selected"
+ includeByDefault: true
+ }
+ ]
+
+ delegate: Rectangle {
+ width: 180
+ height: 180
+ visible: DelegateModel.inSelected
+ color: ListView.isCurrentItem ? "orange" : "yellow"
+ Component.onCompleted: {
+ DelegateModel.inPersistedItems = true
+ DelegateModel.inSelected = Qt.binding(function() { return model.selected })
+ }
+ }
+ }
+
+ ListView {
+ objectName: "list"
+ anchors.fill: parent
+ spacing: 180/15
+ orientation: ListView.Horizontal
+ model: visualModel
+ focus: true
+ currentIndex: 0
+ preferredHighlightBegin: (width-180)/2
+ preferredHighlightEnd: (width+180)/2
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ highlightMoveDuration: 300
+ highlightMoveVelocity: -1
+ cacheBuffer: 0
+
+ onCurrentIndexChanged: {
+ if (currentIndex === 10) {
+ myModel[6].selected = !myModel[6].selected
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml b/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml
new file mode 100644
index 0000000000..f230786723
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+import test 1.0
+
+Window {
+ id: root
+ visible: true
+ width: 800
+ height: 680
+ property bool alive: false
+
+ Component {
+ id: view
+ ListView {
+ model: SingletonModel
+ }
+ }
+ function compare(a,b) {
+ root.alive = (a === b)
+ }
+
+ function test_singletonModelCrash() {
+ SingletonModel.objectName = "model"
+ var o = view.createObject(root)
+ o.destroy()
+ Qt.callLater(function() {
+ compare(SingletonModel.objectName, "model")
+ })
+ }
+
+ Component.onCompleted: root.test_singletonModelCrash()
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 2af6b084fb..57d7dddf20 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -33,6 +33,8 @@
#include <QtGui/QStyleHints>
#include <QtQuick/qquickview.h>
#include <QtQuickTest/QtQuickTest>
+#include <QStringListModel>
+#include <QQmlApplicationEngine>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
@@ -298,6 +300,13 @@ private slots:
void requiredObjectListModel();
void clickHeaderAndFooterWhenClip();
+ void animatedDelegate();
+ void dragDelegateWithMouseArea();
+ void dragDelegateWithMouseArea_data();
+
+
+ void singletonModelLifetime();
+ void QTBUG_92809();
private:
template <class T> void items(const QUrl &source);
@@ -10094,6 +10103,153 @@ void tst_QQuickListView::clickHeaderAndFooterWhenClip() // QTBUG-85302
QVERIFY(root->property("footerPressed").toBool());
}
+void tst_QQuickListView::animatedDelegate()
+{
+ // QTBUG-86567: Should not crash
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("animatedDelegate.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ for (int i = 0; i < 100; ++i) {
+ QMetaObject::invokeMethod(window->rootObject(), "refreshModel");
+ QTest::qWait(10);
+ }
+}
+
+static void dragListView(QWindow *window, QPoint *startPos, const QPoint &delta)
+{
+ auto drag_helper = [&](QWindow *window, QPoint *startPos, const QPoint &d) {
+ QPoint pos = *startPos;
+ const int dragDistance = d.manhattanLength();
+ const QPoint unitVector(qBound(-1, d.x(), 1), qBound(-1, d.y(), 1));
+ for (int i = 0; i < dragDistance; ++i) {
+ QTest::mouseMove(window, pos);
+ pos += unitVector;
+ }
+ // Move to the final position
+ pos = *startPos + d;
+ QTest::mouseMove(window, pos);
+ *startPos = pos;
+ };
+
+ if (delta.manhattanLength() == 0)
+ return;
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ const QPoint unitVector(qBound(-1, delta.x(), 1), qBound(-1, delta.y(), 1));
+ // go just beyond the drag theshold
+ drag_helper(window, startPos, unitVector * (dragThreshold + 1));
+ drag_helper(window, startPos, unitVector);
+
+ // next drag will actually scroll the listview
+ drag_helper(window, startPos, delta);
+}
+
+void tst_QQuickListView::dragDelegateWithMouseArea()
+{
+ QFETCH(QQuickItemView::LayoutDirection, layoutDirection);
+
+ QScopedPointer<QQuickView> window(createView());
+ QVERIFY(window);
+ window->setSource(testFileUrl("delegateWithMouseArea2.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+ QVERIFY(listview != nullptr);
+
+ const bool horizontal = layoutDirection < QQuickItemView::VerticalTopToBottom;
+ listview->setOrientation(horizontal ? QQuickListView::Horizontal : QQuickListView::Vertical);
+
+ if (horizontal)
+ listview->setLayoutDirection(static_cast<Qt::LayoutDirection>(layoutDirection));
+ else
+ listview->setVerticalLayoutDirection(static_cast<QQuickItemView::VerticalLayoutDirection>(layoutDirection));
+
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+
+ auto contentPosition = [&](QQuickListView *listview) {
+ return (listview->orientation() == QQuickListView::Horizontal ? listview->contentX(): listview->contentY());
+ };
+
+ qreal expectedContentPosition = contentPosition(listview);
+ QPoint startPos = (QPointF(listview->width(), listview->height())/2).toPoint();
+ QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, startPos, 200);
+
+ QPoint dragDelta(0, -10);
+
+ if (layoutDirection == QQuickItemView::RightToLeft || layoutDirection == QQuickItemView::VerticalBottomToTop)
+ dragDelta = -dragDelta;
+ expectedContentPosition -= dragDelta.y();
+ if (horizontal)
+ dragDelta = dragDelta.transposed();
+
+ dragListView(window.data(), &startPos, dragDelta);
+
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, startPos, 200); // Wait 200 ms before we release to avoid trigger a flick
+
+ // wait for the "fixup" animation to finish
+ QVERIFY(QTest::qWaitFor([&]()
+ { return !listview->isMoving();}
+ ));
+
+ QCOMPARE(contentPosition(listview), expectedContentPosition);
+}
+
+void tst_QQuickListView::dragDelegateWithMouseArea_data()
+{
+ QTest::addColumn<QQuickItemView::LayoutDirection>("layoutDirection");
+
+ for (int layDir = QQuickItemView::LeftToRight; layDir <= (int)QQuickItemView::VerticalBottomToTop; layDir++) {
+ const char *enumValueName = QMetaEnum::fromType<QQuickItemView::LayoutDirection>().valueToKey(layDir);
+ QTest::newRow(enumValueName) << static_cast<QQuickItemView::LayoutDirection>(layDir);
+ }
+}
+
+class SingletonModel : public QStringListModel
+{
+ Q_OBJECT
+public:
+ SingletonModel(QObject* parent = nullptr) : QStringListModel(parent) { }
+};
+
+void tst_QQuickListView::singletonModelLifetime()
+{
+ // this does not really test any functionality of listview, but we do not have a good way
+ // to unit test QQmlAdaptorModel in isolation.
+ qmlRegisterSingletonType<SingletonModel>("test", 1, 0, "SingletonModel",
+ [](QQmlEngine* , QJSEngine*) -> QObject* { return new SingletonModel; });
+
+ QQmlApplicationEngine engine(testFile("singletonModelLifetime.qml"));
+ // needs event loop iteration for callLater to execute
+ QTRY_VERIFY(engine.rootObjects().first()->property("alive").toBool());
+}
+
+void tst_QQuickListView::QTBUG_92809()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QTRY_VERIFY(window);
+ window->setSource(testFileUrl("qtbug_92809.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+ QTRY_VERIFY(listview != nullptr);
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+ listview->setCurrentIndex(1);
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+ listview->setCurrentIndex(2);
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+ listview->setCurrentIndex(3);
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+ QTest::qWait(500);
+ listview->setCurrentIndex(10);
+ QVERIFY(QQuickTest::qWaitForItemPolished(listview));
+ QTest::qWait(500);
+ int currentIndex = listview->currentIndex();
+ QTRY_COMPARE(currentIndex, 9);
+}
+
QTEST_MAIN(tst_QQuickListView)
#include "tst_qquicklistview.moc"
diff --git a/tests/auto/quick/qquickloader/BLACKLIST b/tests/auto/quick/qquickloader/BLACKLIST
index a45a300607..aeb3674482 100644
--- a/tests/auto/quick/qquickloader/BLACKLIST
+++ b/tests/auto/quick/qquickloader/BLACKLIST
@@ -1,4 +1,5 @@
# Test fails on qemu when bound to one core, passes on real ARM
# QTBUG-63049
[asyncToSync1]
+ci ubuntu-20.04 # QTBUG-106424
b2qt
diff --git a/tests/auto/quick/qquickloader/data/initialPropertyValues.12.qml b/tests/auto/quick/qquickloader/data/initialPropertyValues.12.qml
new file mode 100644
index 0000000000..940730b307
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/initialPropertyValues.12.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int i: 0
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ active: false
+ }
+
+ Component.onCompleted: {
+ loader.setSource("CacheClearTest.qml", {i: 12})
+ loader.active = true
+ loader.active = false
+ loader.active = true
+ root.i = loader.item.i // should be 12
+ }
+}
diff --git a/tests/auto/quick/qquickloader/data/loader-async-race-rect.qml b/tests/auto/quick/qquickloader/data/loader-async-race-rect.qml
new file mode 100644
index 0000000000..a56dcea5ad
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/loader-async-race-rect.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.15
+
+Rectangle {
+ anchors.fill: parent
+ color: "blue"
+ Item {
+ Item {
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickloader/data/loader-async-race.qml b/tests/auto/quick/qquickloader/data/loader-async-race.qml
new file mode 100644
index 0000000000..8ba625c5c1
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/loader-async-race.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ Component.onCompleted: {
+ myloader.active = false
+ }
+ Loader {
+ id: myloader
+ anchors.fill: parent
+ asynchronous: true
+ source: "loader-async-race-rect.qml"
+ }
+}
diff --git a/tests/auto/quick/qquickloader/data/noEngine.qml b/tests/auto/quick/qquickloader/data/noEngine.qml
new file mode 100644
index 0000000000..19e619f32e
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/noEngine.qml
@@ -0,0 +1,32 @@
+import QtQuick 2
+
+Item {
+ id: root
+ property bool a: false
+ property int changes: 0
+ onAChanged: {
+ m.model = 0
+ m.model = 1
+ ++changes;
+ }
+
+ Repeater {
+ id: m
+ model: 1
+
+ Item {
+ Timer {
+ onTriggered: {
+ root.a = true
+ l.source = "loaded.qml"
+ }
+ interval: 0
+ running: true
+ }
+
+ Loader {
+ id: l
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickloader/data/overflow.qml b/tests/auto/quick/qquickloader/data/overflow.qml
new file mode 100644
index 0000000000..8ca196e2ed
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/overflow.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Loader {
+ source: "overflow.qml"
+}
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index f4b682f3f4..4539a89cb3 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -132,6 +132,10 @@ private slots:
void statusChangeOnlyEmittedOnce();
void setSourceAndCheckStatus();
+ void asyncLoaderRace();
+ void noEngine();
+
+ void stackOverflow();
};
Q_DECLARE_METATYPE(QList<QQmlError>)
@@ -700,6 +704,11 @@ void tst_QQuickLoader::initialPropertyValues_data()
<< QStringList()
<< (QStringList() << "oldi" << "i")
<< (QVariantList() << 12 << 42);
+
+ QTest::newRow("ensure initial properties aren't disposed after active = true") << testFileUrl("initialPropertyValues.12.qml")
+ << QStringList()
+ << (QStringList() << "i")
+ << (QVariantList() << 12);
}
void tst_QQuickLoader::initialPropertyValues()
@@ -1491,6 +1500,49 @@ void tst_QQuickLoader::setSourceAndCheckStatus()
QCOMPARE(loader->status(), QQuickLoader::Null);
}
+void tst_QQuickLoader::asyncLoaderRace()
+{
+ QQmlApplicationEngine engine;
+ auto url = testFileUrl("loader-async-race.qml");
+ engine.load(url);
+ auto root = engine.rootObjects().at(0);
+ QVERIFY(root);
+
+ QQuickLoader *loader = root->findChild<QQuickLoader *>();
+ QCOMPARE(loader->active(), false);
+ QCOMPARE(loader->status(), QQuickLoader::Null);
+ QCOMPARE(loader->item(), nullptr);
+
+ QSignalSpy spy(loader, &QQuickLoader::itemChanged);
+ QVERIFY(!spy.wait(100));
+ QCOMPARE(loader->item(), nullptr);
+}
+
+void tst_QQuickLoader::noEngine()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("noEngine.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+
+ const QString message = url.toString()
+ + QStringLiteral(":27:13: QML Loader: createComponent: Cannot find a QML engine.");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ QTRY_COMPARE(o->property("changes").toInt(), 1);
+}
+
+void tst_QQuickLoader::stackOverflow()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("overflow.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ const QString message = url.toString() + QStringLiteral(": Maximum call stack size exceeded.");
+ QTest::ignoreMessage(QtCriticalMsg, qPrintable(message));
+ QScopedPointer<QObject> o(component.create());
+}
+
QTEST_MAIN(tst_QQuickLoader)
#include "tst_qquickloader.moc"
diff --git a/tests/auto/quick/qquickmousearea/data/containsMouse.qml b/tests/auto/quick/qquickmousearea/data/containsMouse.qml
new file mode 100644
index 0000000000..c4f1299e49
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/containsMouse.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.15
+
+Rectangle {
+ width: 200
+ height: 200
+ visible: true
+ MouseArea {
+ id: mouseArea
+ objectName: "mouseArea"
+ anchors.fill: parent
+ hoverEnabled: true
+ visible: false
+ }
+}
diff --git a/tests/auto/quick/qquickmousearea/data/doubleClickToHide.qml b/tests/auto/quick/qquickmousearea/data/doubleClickToHide.qml
new file mode 100644
index 0000000000..01f6eaabed
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/doubleClickToHide.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int clicked: 0
+ property int doubleClicked: 0
+ property int released: 0
+
+ MouseArea {
+ width: 200; height: 200
+ onClicked: { root.clicked++ }
+ onDoubleClicked: {
+ root.doubleClicked++
+ visible = false
+ }
+ onReleased: { root.released++ }
+ }
+}
+
diff --git a/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml
new file mode 100644
index 0000000000..3833c5a8a9
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.15
+
+ListView {
+ id: flick
+ width: 640
+ height: 480
+ model: 100
+
+ delegate: Rectangle {
+ border.color: "#81e889"
+ width: 640; height: 100
+ Text { text: "Row " + index }
+ }
+
+ Rectangle {
+ anchors.right: parent.right
+ anchors.margins: 2
+ color: ma.pressed ? "#81e889" : "#c2f4c6"
+ width: 50; height: 50
+ radius: 5
+ MouseArea {
+ id: ma
+ anchors.fill: parent
+ preventStealing: true
+ drag {
+ target: parent
+ axis: Drag.YAxis
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 91fcae40af..941d6dc47b 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -129,6 +129,7 @@ private slots:
void invalidClick();
void pressedOrdering();
void preventStealing();
+ void preventStealingListViewChild();
void clickThrough();
void hoverPosition();
void hoverPropagation();
@@ -160,6 +161,8 @@ private slots:
void mask();
void nestedEventDelivery();
void settingHiddenInPressUngrabs();
+ void containsMouseAndVisibility();
+ void doubleClickToHide();
private:
int startDragDistance() const {
@@ -1210,6 +1213,39 @@ void tst_QQuickMouseArea::preventStealing()
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
}
+// QTBUG-103522
+void tst_QQuickMouseArea::preventStealingListViewChild()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("preventStealingListViewChild.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject());
+ QVERIFY(flickable);
+ QQuickMouseArea *mouseArea = flickable->findChild<QQuickMouseArea*>();
+ QVERIFY(mouseArea);
+ QPoint p = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint();
+ const int threshold = qApp->styleHints()->startDragDistance();
+
+ flickable->flick(0, -10000);
+ for (int i = 0; i < 2; ++i) {
+ QVERIFY(flickable->isMovingVertically());
+ QTest::touchEvent(&window, device).press(0, p);
+ QQuickTouchUtils::flush(&window);
+ for (int j = 0; j < 4 && !mouseArea->drag()->active(); ++j) {
+ p += QPoint(0, threshold);
+ QTest::touchEvent(&window, device).move(0, p);
+ QQuickTouchUtils::flush(&window);
+ }
+ // MouseArea should be dragged because of preventStealing; ListView does not steal the grab.
+ QVERIFY(mouseArea->drag()->active());
+ QCOMPARE(flickable->isDragging(), false);
+ QTest::touchEvent(&window, device).release(0, p);
+ QCOMPARE(mouseArea->drag()->active(), false);
+ }
+}
+
void tst_QQuickMouseArea::clickThrough()
{
//With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers
@@ -2450,6 +2486,82 @@ void tst_QQuickMouseArea::settingHiddenInPressUngrabs()
QVERIFY(!mouseArea->pressed());
}
+// QTBUG-87197
+void tst_QQuickMouseArea::containsMouseAndVisibility()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("containsMouse.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ window.requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
+ QVERIFY(mouseArea != nullptr);
+ QVERIFY(!mouseArea->isVisible());
+
+ QTest::mouseMove(&window, QPoint(10, 10));
+ QTRY_VERIFY(!mouseArea->hovered());
+
+ mouseArea->setVisible(true);
+ QVERIFY(mouseArea->isVisible());
+ QTRY_VERIFY(mouseArea->hovered());
+
+ /* we (ab-)use QPointF() as the 'reset' value in QQuickWindow's leave-event handling,
+ but can't verify that this leaves an invalid interpretation of states for position
+ QPoint(0, 0) as QTest::mouseMove interprets a null-position as "center of the window".
+
+ So instead, verify the current (unexpectedly expected) behavior as far as testing is
+ concern.
+ */
+ QTest::mouseMove(&window, QPoint(0, 0));
+ QTRY_VERIFY(mouseArea->hovered());
+ QTRY_VERIFY(mouseArea->isUnderMouse());
+
+ // move to the edge (can't move outside)
+ QTest::mouseMove(&window, QPoint(window.width() - 1, window.height() / 2));
+ // then pretend we left
+ QEvent event(QEvent::Leave);
+ QGuiApplication::sendEvent(&window, &event);
+ QVERIFY(!mouseArea->hovered());
+
+ // toggle mouse area visibility - the hover state should not change
+ mouseArea->setVisible(false);
+ QVERIFY(!mouseArea->isVisible());
+ QVERIFY(!mouseArea->hovered());
+
+ mouseArea->setVisible(true);
+ QVERIFY(mouseArea->isVisible());
+ QVERIFY(!mouseArea->hovered());
+}
+
+// QTBUG-35995 and QTBUG-102158
+void tst_QQuickMouseArea::doubleClickToHide()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("doubleClickToHide.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ window.requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
+ QVERIFY(mouseArea);
+
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
+ QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
+ QCOMPARE(mouseArea->isVisible(), false);
+ QCOMPARE(mouseArea->pressed(), false);
+ QCOMPARE(mouseArea->pressedButtons(), Qt::NoButton);
+
+ mouseArea->setVisible(true);
+
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
+}
+
QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
index 6af00ab76f..db8595544b 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
+++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
@@ -1,10 +1,14 @@
[nonOverlapping]
+# QTBUG-101499
+ubuntu-20.04
ubuntu-16.04
ubuntu-18.04
opensuse-42.3
opensuse-leap
sles
[nested]
+# QTBUG-101499
+ubuntu-20.04
ubuntu-16.04
ubuntu-18.04
opensuse-42.3
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml
new file mode 100644
index 0000000000..7ca3f187d6
--- /dev/null
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.12
+
+Item {
+ id: root
+ width: 300; height: 300
+ property point mptaPoint
+ property point maPoint
+ MultiPointTouchArea {
+ anchors.fill : parent
+ onPressed: function(touchPoints) {
+ root.mptaPoint = Qt.point(touchPoints[0].x, touchPoints[0].y)
+ }
+ MouseArea {
+ id: ma
+ width: 100; height: 100
+ anchors.centerIn: parent
+ onPressed: function(mouse) {
+ root.maPoint = Qt.point(mouse.x, mouse.y)
+ }
+ }
+ Rectangle {
+ anchors.fill: ma
+ border.color: "grey"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nestedPinchArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nestedPinchArea.qml
new file mode 100644
index 0000000000..0e51804b30
--- /dev/null
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/nestedPinchArea.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.15
+
+MultiPointTouchArea {
+ width: 240
+ height: 320
+ mouseEnabled: true
+ property int pressedCount: 0
+ property int updatedCount: 0
+ property int releasedCount: 0
+
+ onPressed: (points) => { pressedCount = points.length }
+ onUpdated: (points) => { updatedCount = points.length }
+ onReleased: (points) => { releasedCount = points.length }
+
+ touchPoints: [
+ TouchPoint {
+ id: point1
+ objectName: "point1"
+ },
+ TouchPoint {
+ id: point2
+ objectName: "point2"
+ }
+ ]
+
+ PinchArea {
+ anchors.fill: parent
+ }
+
+ Rectangle {
+ width: 30; height: 30
+ color: "green"
+ x: point1.x
+ y: point1.y
+ }
+
+ Rectangle {
+ id: rectangle
+ width: 30; height: 30
+ color: "yellow"
+ x: point2.x
+ y: point2.y
+ }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index c18a220996..3aff9a293a 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -77,6 +77,8 @@ private slots:
void mouseGestureStarted();
void cancel();
void stationaryTouchWithChangingPressure();
+ void nestedPinchAreaMouse();
+ void touchFiltering();
private:
QQuickView *createAndShowView(const QString &file);
@@ -1408,6 +1410,70 @@ void tst_QQuickMultiPointTouchArea::stationaryTouchWithChangingPressure() // QTB
QCOMPARE(point1->pressure(), 0);
}
+void tst_QQuickMultiPointTouchArea::nestedPinchAreaMouse()
+{
+ QScopedPointer<QQuickView> window(createAndShowView("nestedPinchArea.qml"));
+ QQuickMultiPointTouchArea *mpta = qobject_cast<QQuickMultiPointTouchArea *>(window->rootObject());
+ QVERIFY(mpta);
+
+ QQuickTouchPoint *point1 = mpta->findChild<QQuickTouchPoint*>("point1");
+ QCOMPARE(point1->pressed(), false);
+ QQuickTouchPoint *point2 = mpta->findChild<QQuickTouchPoint*>("point2");
+ QCOMPARE(point2->pressed(), false);
+ QSignalSpy pressedSpy(mpta, &QQuickMultiPointTouchArea::pressed);
+ QSignalSpy updatedSpy(mpta, &QQuickMultiPointTouchArea::updated);
+ QSignalSpy releasedSpy(mpta, &QQuickMultiPointTouchArea::released);
+
+ QPoint p1(20, 20);
+ QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QCOMPARE(point1->pressed(), true);
+ QCOMPARE(point2->pressed(), false);
+ QCOMPARE(pressedSpy.count(), 1);
+ QCOMPARE(mpta->property("pressedCount").toInt(), 1);
+ QCOMPARE(updatedSpy.count(), 0);
+ QCOMPARE(mpta->property("updatedCount").toInt(), 0);
+ QCOMPARE(releasedSpy.count(), 0);
+ QCOMPARE(mpta->property("releasedCount").toInt(), 0);
+
+ p1 += QPoint(0, 15);
+ QTest::mouseMove(window.data(), p1);
+ QCOMPARE(point1->pressed(), true);
+ QCOMPARE(point2->pressed(), false);
+ QCOMPARE(pressedSpy.count(), 1);
+ QCOMPARE(mpta->property("pressedCount").toInt(), 1);
+ QCOMPARE(updatedSpy.count(), 1);
+ QCOMPARE(mpta->property("updatedCount").toInt(), 1);
+ QCOMPARE(releasedSpy.count(), 0);
+ QCOMPARE(mpta->property("releasedCount").toInt(), 0);
+
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QCOMPARE(point1->pressed(), false);
+ QCOMPARE(point2->pressed(), false);
+ QCOMPARE(pressedSpy.count(), 1);
+ QCOMPARE(mpta->property("pressedCount").toInt(), 1);
+ QCOMPARE(updatedSpy.count(), 1);
+ QCOMPARE(mpta->property("updatedCount").toInt(), 1);
+ QCOMPARE(releasedSpy.count(), 1);
+ QCOMPARE(mpta->property("releasedCount").toInt(), 1);
+}
+
+void tst_QQuickMultiPointTouchArea::touchFiltering() // QTBUG-74028
+{
+ QScopedPointer<QQuickView> window(createAndShowView("nestedMouseArea.qml"));
+ QVERIFY(window->rootObject() != nullptr);
+ QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta);
+ QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
+ QVERIFY(ma);
+
+ QSignalSpy mptaSpy(mpta, &QQuickMultiPointTouchArea::pressed);
+ const QPoint pt = window->rootObject()->boundingRect().center().toPoint();
+ QTest::touchEvent(window.data(), device).press(1, pt);
+ QQuickTouchUtils::flush(window.data());
+ QTRY_COMPARE(mpta->parentItem()->property("mptaPoint").toPoint(), pt);
+ QCOMPARE(mpta->parentItem()->property("maPoint").toPoint(), ma->boundingRect().center().toPoint());
+ QCOMPARE(mptaSpy.count(), 1);
+}
QTEST_MAIN(tst_QQuickMultiPointTouchArea)
diff --git a/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml b/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml
new file mode 100644
index 0000000000..3acf67b4b6
--- /dev/null
+++ b/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml
@@ -0,0 +1,70 @@
+import QtQuick 2.12
+
+Rectangle {
+ id: root
+ width: 600
+ height: 600
+
+ Rectangle {
+ objectName: "paContainer"
+ width: parent.width -100
+ height: parent.height - 100
+ border.color: "black"
+ anchors.centerIn: parent
+ transformOrigin: Item.Center
+
+ Rectangle {
+ width: 300
+ height: 300
+ color: "tomato"
+ PinchArea {
+ id: pa
+ anchors.fill: parent
+ pinch.target: parent
+ pinch.minimumScale: 0.5
+ pinch.maximumScale: 2
+ pinch.minimumRotation: -360
+ pinch.maximumRotation: 360
+ pinch.dragAxis: Pinch.XAndYAxis
+ pinch.minimumX: -100
+ pinch.maximumX: 300
+ pinch.minimumY: -100
+ pinch.maximumY: 300
+ }
+
+
+ Text { text: "this way up" }
+ }
+ }
+
+ // only for touch feedback / troubleshooting
+ Item {
+ id: glassPane
+ z: 10000
+ anchors.fill: parent
+
+ PointHandler {
+ id: ph1
+ target: Rectangle {
+ parent: glassPane
+ color: "green"
+ visible: ph1.active
+ x: ph1.point.position.x - width / 2
+ y: ph1.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+ }
+
+ PointHandler {
+ id: ph2
+ target: Rectangle {
+ parent: glassPane
+ color: "blue"
+ visible: ph2.active
+ x: ph2.point.position.x - width / 2
+ y: ph2.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
index 5b7108d96b..367df96118 100644
--- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
+++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
@@ -52,6 +52,8 @@ private slots:
void cancel();
void transformedPinchArea_data();
void transformedPinchArea();
+ void dragTransformedPinchArea_data();
+ void dragTransformedPinchArea();
private:
QQuickView *createView();
@@ -586,6 +588,70 @@ void tst_QQuickPinchArea::transformedPinchArea()
}
}
+void tst_QQuickPinchArea::dragTransformedPinchArea_data()
+{
+ QTest::addColumn<int>("rotation");
+ QTest::addColumn<QPoint>("p1");
+ QTest::addColumn<QPoint>("p2");
+ QTest::addColumn<QPoint>("delta");
+
+ QTest::newRow("unrotated")
+ << 0 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 40);
+ QTest::newRow("20 deg")
+ << 20 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 40);
+ QTest::newRow("90 deg")
+ << 90 << QPoint(100, 100) << QPoint(200, 100) << QPoint(0, 40);
+ QTest::newRow("180 deg")
+ << 180 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 0);
+ QTest::newRow("225 deg")
+ << 210 << QPoint(200, 200) << QPoint(300, 200) << QPoint(80, 80);
+}
+
+void tst_QQuickPinchArea::dragTransformedPinchArea() // QTBUG-63673
+{
+ QFETCH(int, rotation);
+ QFETCH(QPoint, p1);
+ QFETCH(QPoint, p2);
+ QFETCH(QPoint, delta);
+ const int threshold = qApp->styleHints()->startDragDistance();
+
+ QQuickView *view = createView();
+ QScopedPointer<QQuickView> scope(view);
+ view->setSource(testFileUrl("draggablePinchArea.qml"));
+ view->show();
+ QVERIFY(QTest::qWaitForWindowExposed(view));
+ QVERIFY(view->rootObject());
+ QQuickPinchArea *pinchArea = view->rootObject()->findChild<QQuickPinchArea*>();
+ QVERIFY(pinchArea);
+ QQuickItem *pinchAreaTarget = pinchArea->parentItem();
+ QVERIFY(pinchAreaTarget);
+ QQuickItem *pinchAreaContainer = pinchAreaTarget->parentItem();
+ QVERIFY(pinchAreaContainer);
+ pinchAreaContainer->setRotation(rotation);
+
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device);
+ // start pinch
+ pinchSequence.press(1, pinchArea->mapToScene(p1).toPoint(), view)
+ .press(2, pinchArea->mapToScene(p2).toPoint(), view).commit();
+ QQuickTouchUtils::flush(view);
+ pinchSequence.move(1, pinchArea->mapToScene(p1 + QPoint(threshold, threshold)).toPoint(), view)
+ .move(2, pinchArea->mapToScene(p2 + QPoint(threshold, threshold)).toPoint(), view).commit();
+ QQuickTouchUtils::flush(view);
+ pinchSequence.move(1, pinchArea->mapToScene(p1 + delta).toPoint(), view)
+ .move(2, pinchArea->mapToScene(p2 + delta).toPoint(), view).commit();
+ QQuickTouchUtils::flush(view);
+ QCOMPARE(pinchArea->pinch()->active(), true);
+ auto error = delta - QPoint(threshold, threshold) -
+ pinchAreaTarget->position().toPoint(); // expect 0, 0
+ QVERIFY(qAbs(error.x()) <= 1);
+ QVERIFY(qAbs(error.y()) <= 1);
+
+ // release pinch
+ pinchSequence.release(1, p1, view).release(2, p2, view).commit();
+ QQuickTouchUtils::flush(view);
+ QCOMPARE(pinchArea->pinch()->active(), false);
+}
+
QQuickView *tst_QQuickPinchArea::createView()
{
QQuickView *window = new QQuickView(nullptr);
diff --git a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
index 4962690796..1985f3aa31 100644
--- a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
+++ b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
@@ -42,12 +42,16 @@ class tst_QQuickShortcut : public QQmlDataTest
Q_OBJECT
private slots:
+ void standardShortcuts_data();
+ void standardShortcuts();
void shortcuts_data();
void shortcuts();
void sequence_data();
void sequence();
void context_data();
void context();
+ void contextChange_data();
+ void contextChange();
void matcher_data();
void matcher();
void multiple_data();
@@ -79,6 +83,49 @@ static QVariant shortcutMap(const QVariant &key, bool enabled = true, bool autoR
return shortcutMap(key, Qt::WindowShortcut, enabled, autoRepeat);
}
+void tst_QQuickShortcut::standardShortcuts_data()
+{
+ QTest::addColumn<QKeySequence::StandardKey>("standardKey");
+ QTest::newRow("Close") << QKeySequence::Close;
+ QTest::newRow("Cut") << QKeySequence::Cut;
+ QTest::newRow("NextChild") << QKeySequence::NextChild;
+ QTest::newRow("PreviousChild") << QKeySequence::PreviousChild;
+ QTest::newRow("FindNext") << QKeySequence::FindNext;
+ QTest::newRow("FindPrevious") << QKeySequence::FindPrevious;
+ QTest::newRow("FullScreen") << QKeySequence::FullScreen;
+}
+
+void tst_QQuickShortcut::standardShortcuts()
+{
+ QFETCH(QKeySequence::StandardKey, standardKey);
+
+ QQmlApplicationEngine engine;
+
+ engine.load(testFileUrl("multiple.qml"));
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0));
+ QVERIFY(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QObject *shortcut = window->property("shortcut").value<QObject *>();
+ QVERIFY(shortcut);
+
+ // create list of shortcuts
+ QVariantList shortcuts;
+ shortcuts.push_back(standardKey);
+ shortcut->setProperty("sequences", shortcuts);
+
+ // test all:
+ QList<QKeySequence> allsequences = QKeySequence::keyBindings(standardKey);
+ for (const QKeySequence &s : allsequences) {
+ window->setProperty("activated", false);
+ QTest::keySequence(window, s);
+ QCOMPARE(window->property("activated").toBool(), true);
+ }
+}
+
void tst_QQuickShortcut::shortcuts_data()
{
QTest::addColumn<QVariantList>("shortcuts");
@@ -186,6 +233,7 @@ void tst_QQuickShortcut::shortcuts()
window->setProperty("shortcuts", shortcuts);
QTest::keyPress(window, key, modifiers);
+ QTest::keyRelease(window, key, modifiers);
QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut);
QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut);
}
@@ -261,14 +309,22 @@ void tst_QQuickShortcut::sequence()
window->setProperty("shortcuts", shortcuts);
- if (key1 != 0)
+ if (key1 != 0) {
QTest::keyPress(window, key1, modifiers1);
- if (key2 != 0)
+ QTest::keyRelease(window, key1, modifiers1);
+ }
+ if (key2 != 0) {
QTest::keyPress(window, key2, modifiers2);
- if (key3 != 0)
+ QTest::keyRelease(window, key2, modifiers2);
+ }
+ if (key3 != 0) {
QTest::keyPress(window, key3, modifiers3);
- if (key4 != 0)
+ QTest::keyRelease(window, key3, modifiers3);
+ }
+ if (key4 != 0) {
QTest::keyPress(window, key4, modifiers4);
+ QTest::keyRelease(window, key4, modifiers4);
+ }
QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut);
QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut);
@@ -350,6 +406,7 @@ void tst_QQuickShortcut::context()
activeWindow->setProperty("shortcuts", activeWindowShortcuts);
QTest::keyPress(activeWindow, key);
+ QTest::keyRelease(activeWindow, key);
QCOMPARE(activeWindow->property("activatedShortcut").toString(), activeWindowActivatedShortcut);
QCOMPARE(inactiveWindow->property("activatedShortcut").toString(), inactiveWindowActivatedShortcut);
@@ -409,6 +466,8 @@ void tst_QQuickShortcut::matcher()
QVERIFY(window);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
window->setProperty("shortcuts", QVariantList() << shortcut);
QTest::keyClick(window, key);
@@ -450,6 +509,8 @@ void tst_QQuickShortcut::multiple()
QVERIFY(window);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
QObject *shortcut = window->property("shortcut").value<QObject *>();
QVERIFY(shortcut);
@@ -458,8 +519,75 @@ void tst_QQuickShortcut::multiple()
shortcut->setProperty("sequences", sequences);
QTest::keyPress(window, key, modifiers);
+ QTest::keyRelease(window, key, modifiers);
+
+ QCOMPARE(window->property("activated").toBool(), activated);
+
+ // check it still works after rotating the sequences
+ QStringList rotatedSequences;
+ for (int i = 1; i < sequences.size(); ++i)
+ rotatedSequences.push_back(sequences[i]);
+ if (sequences.size())
+ rotatedSequences.push_back(sequences[0]);
+
+ window->setProperty("activated", false);
+ shortcut->setProperty("sequences", rotatedSequences);
+ QTest::keyPress(window, key, modifiers);
+ QTest::keyRelease(window, key, modifiers);
QCOMPARE(window->property("activated").toBool(), activated);
+
+ // check setting to no shortcuts
+ QStringList emptySequence;
+
+ window->setProperty("activated", false);
+ shortcut->setProperty("sequences", emptySequence);
+
+ QTest::keyPress(window, key, modifiers);
+ QTest::keyRelease(window, key, modifiers);
+ QCOMPARE(window->property("activated").toBool(), false);
+}
+
+void tst_QQuickShortcut::contextChange_data()
+{
+ multiple_data();
+}
+void tst_QQuickShortcut::contextChange()
+{
+ QFETCH(QStringList, sequences);
+ QFETCH(Qt::Key, key);
+ QFETCH(Qt::KeyboardModifiers, modifiers);
+ QFETCH(bool, enabled);
+ QFETCH(bool, activated);
+
+ QQmlApplicationEngine engine;
+
+ engine.load(testFileUrl("multiple.qml"));
+ QQuickWindow *inactivewindow = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0));
+ QVERIFY(inactivewindow);
+ inactivewindow->show();
+ QVERIFY(QTest::qWaitForWindowExposed(inactivewindow));
+
+ QObject *shortcut = inactivewindow->property("shortcut").value<QObject *>();
+ QVERIFY(shortcut);
+
+ shortcut->setProperty("enabled", enabled);
+ shortcut->setProperty("sequences", sequences);
+ shortcut->setProperty("context", Qt::WindowShortcut);
+
+ engine.load(testFileUrl("multiple.qml"));
+ QQuickWindow *activewindow = qobject_cast<QQuickWindow *>(engine.rootObjects().value(1));
+ QVERIFY(activewindow);
+ activewindow->show();
+ QVERIFY(QTest::qWaitForWindowExposed(activewindow));
+
+ QTest::keyPress(activewindow, key, modifiers);
+ QCOMPARE(inactivewindow->property("activated").toBool(), false);
+
+ shortcut->setProperty("context", Qt::ApplicationShortcut);
+
+ QTest::keyPress(activewindow, key, modifiers);
+ QCOMPARE(inactivewindow->property("activated").toBool(), activated);
}
#ifdef QT_QUICKWIDGETS_LIB
@@ -572,6 +700,7 @@ void tst_QQuickShortcut::renderControlShortcuts()
QQuickItem* item = quickWidget->rootObject();
item->setProperty("shortcuts", shortcuts);
QTest::keyPress(quickWidget->quickWindow(), key, modifiers, 1500);
+ QTest::keyRelease(quickWidget->quickWindow(), key, modifiers, 1600);
QCOMPARE(item->property("activatedShortcut").toString(), activatedShortcut);
QCOMPARE(item->property("ambiguousShortcut").toString(), ambiguousShortcut);
}
diff --git a/tests/auto/quick/qquickstates/data/noStateOsciallation.qml b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
new file mode 100644
index 0000000000..f0d7aeeb6d
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ property int number: 2
+ property int stateChangeCounter: 0
+
+ Item {
+ id: item
+ onStateChanged: ++stateChangeCounter
+ states: [
+ State {
+ name: "n1"
+ when: root.number === 1
+ },
+ State {
+ name: "n2"
+ when: root.number === 2
+ }
+ ]
+ }
+}
diff --git a/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
new file mode 100644
index 0000000000..dee82f52ed
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
@@ -0,0 +1,48 @@
+import QtQuick 2.12
+import Qt.test 1.0
+
+Item {
+ id: root
+ readonly property int someProp: 1234
+
+ property bool state1Active: false
+ property bool state2Active: false
+ StateGroup {
+ states: [
+ State {
+ id: state1
+ name: "state1"
+ when: state1Active
+ changes: [
+ PropertyChanges {
+ objectName: "propertyChanges1"
+ target: ContainingObj.obj
+ prop: root.someProp
+ }
+ ]
+ }
+ ]}
+ StateGroup {
+ states: [
+ State {
+ id: state2
+ name: "state2"
+ when: state2Active
+ changes: [
+ PropertyChanges {
+ objectName: "propertyChanges2"
+ target: ContainingObj.obj
+ prop: 11111
+ }
+ ]
+ }
+ ]
+ }
+
+ Component.onCompleted: {
+ state1Active = true;
+ state2Active = true;
+
+ ContainingObj.reset()
+ }
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index d5fea3cb28..aa55b42935 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -79,6 +79,55 @@ private:
QML_DECLARE_TYPE(MyRect)
QML_DECLARE_TYPEINFO(MyRect, QML_HAS_ATTACHED_PROPERTIES)
+class RemovableObj : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
+
+public:
+ RemovableObj(QObject *parent) : QObject(parent), m_prop(4321) { }
+ int prop() const { return m_prop; }
+
+public slots:
+ void setProp(int prop)
+ {
+ if (m_prop == prop)
+ return;
+
+ m_prop = prop;
+ emit propChanged(m_prop);
+ }
+
+signals:
+ void propChanged(int prop);
+
+private:
+ int m_prop;
+};
+
+class ContainingObj : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(RemovableObj *obj READ obj NOTIFY objChanged)
+ RemovableObj *m_obj;
+
+public:
+ ContainingObj() : m_obj(new RemovableObj(this)) { }
+ RemovableObj *obj() const { return m_obj; }
+
+ Q_INVOKABLE void reset()
+ {
+ if (m_obj) {
+ m_obj->deleteLater();
+ }
+
+ m_obj = new RemovableObj(this);
+ emit objChanged();
+ }
+signals:
+ void objChanged();
+};
+
class tst_qquickstates : public QQmlDataTest
{
Q_OBJECT
@@ -139,13 +188,22 @@ private slots:
void revertListMemoryLeak();
void duplicateStateName();
void trivialWhen();
+ void noStateOsciallation();
void parentChangeCorrectReversal();
+ void revertNullObjectBinding();
};
void tst_qquickstates::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<MyRect>("Qt.test", 1, 0, "MyRectangle");
+ qmlRegisterSingletonType<ContainingObj>(
+ "Qt.test", 1, 0, "ContainingObj", [](QQmlEngine *engine, QJSEngine *) {
+ static ContainingObj instance;
+ engine->setObjectOwnership(&instance, QQmlEngine::CppOwnership);
+ return &instance;
+ });
+ qmlRegisterUncreatableType<RemovableObj>("Qt.test", 1, 0, "RemovableObj", "Uncreatable");
}
QByteArray tst_qquickstates::fullDataPath(const QString &path) const
@@ -1676,6 +1734,20 @@ void tst_qquickstates::trivialWhen()
QVERIFY(c.create());
}
+void tst_qquickstates::noStateOsciallation()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml"));
+ QScopedPointer<QObject> root {component.create()};
+ QVERIFY(root);
+ // set to 1 on initial transition from "" to "n2"
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 1);
+ root->setProperty("number", 1);
+ // setting number to 1 changes directly from "n2" to "n1"
+ // without any intermediate transition to ""
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 2);
+}
+
void tst_qquickstates::parentChangeCorrectReversal()
{
QQmlEngine engine;
@@ -1692,6 +1764,17 @@ void tst_qquickstates::parentChangeCorrectReversal()
QCOMPARE(oldX, stayingRectX.read().toDouble());
}
+void tst_qquickstates::revertNullObjectBinding()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, testFileUrl("revertNullObjectBinding.qml"));
+ QScopedPointer<QObject> root { c.create() };
+ QVERIFY(root);
+ QTest::qWait(10);
+ QQmlProperty state2Active(root.get(), "state2Active");
+ state2Active.write(false);
+}
QTEST_MAIN(tst_qquickstates)
diff --git a/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml
new file mode 100644
index 0000000000..b4a04c89cb
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Window 2.3
+
+Item {
+ width: 640
+ height: 450
+
+ property alias tableView: tableView
+
+ TableView {
+ id: tableView
+ width: 600
+ height: 400
+ anchors.margins: 1
+ delegate: tableViewDelegate
+ columnSpacing: 1
+ rowSpacing: 1
+ }
+
+ Component {
+ id: tableViewDelegate
+ Rectangle {
+ color: "lightgray"
+ implicitWidth: text.width
+ implicitHeight: text.height
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ text: modelData
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index d489a873e4..6d37fb44e3 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -121,7 +121,9 @@ private slots:
void checkForceLayoutFunction();
void checkForceLayoutEndUpDoingALayout();
void checkForceLayoutDuringModelChange();
+ void checkForceLayoutWhenAllItemsAreHidden();
void checkContentWidthAndHeight();
+ void checkContentWidthAndHeightForSmallTables();
void checkPageFlicking();
void checkExplicitContentWidthAndHeight();
void checkExtents_origin();
@@ -167,6 +169,8 @@ private slots:
void checkTableviewInsideAsyncLoader();
void hideRowsAndColumns_data();
void hideRowsAndColumns();
+ void hideAndShowFirstColumn();
+ void hideAndShowFirstRow();
void checkThatRevisionedPropertiesCannotBeUsedInOldImports();
void checkSyncView_rootView_data();
void checkSyncView_rootView();
@@ -625,6 +629,38 @@ void tst_QQuickTableView::checkForceLayoutDuringModelChange()
QCOMPARE(tableView->rows(), initialRowCount + 1);
}
+void tst_QQuickTableView::checkForceLayoutWhenAllItemsAreHidden()
+{
+ // Check that you can have a TableView where all columns are
+ // initially hidden, and then show some columns and call
+ // forceLayout(). This should make the columns become visible.
+ LOAD_TABLEVIEW("forcelayout.qml");
+
+ // Tell all columns to be hidden
+ const char *propertyName = "columnWidths";
+ view->rootObject()->setProperty(propertyName, 0);
+
+ const int rows = 3;
+ const int columns = 3;
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that the we have no items loaded
+ QCOMPARE(tableViewPrivate->loadedColumns.count(), 0);
+ QCOMPARE(tableViewPrivate->loadedRows.count(), 0);
+ QCOMPARE(tableViewPrivate->loadedItems.count(), 0);
+
+ // Tell all columns to be visible
+ view->rootObject()->setProperty(propertyName, 10);
+ tableView->forceLayout();
+
+ QCOMPARE(tableViewPrivate->loadedRows.count(), rows);
+ QCOMPARE(tableViewPrivate->loadedColumns.count(), columns);
+ QCOMPARE(tableViewPrivate->loadedItems.count(), rows * columns);
+}
+
void tst_QQuickTableView::checkContentWidthAndHeight()
{
// Check that contentWidth/Height reports the correct size of the
@@ -673,6 +709,30 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
}
+void tst_QQuickTableView::checkContentWidthAndHeightForSmallTables()
+{
+ // For tables where all the columns in the model are loaded, we know
+ // the exact table width, and can therefore update the content width
+ // if e.g new rows are added or removed. The same is true for rows.
+ // This test will check that we do so.
+ LOAD_TABLEVIEW("sizefromdelegate.qml");
+
+ TestModel model(3, 3);
+ tableView->setModel(QVariant::fromValue(&model));
+ WAIT_UNTIL_POLISHED;
+
+ const qreal initialContentWidth = tableView->contentWidth();
+ const qreal initialContentHeight = tableView->contentHeight();
+ const QString longText = QStringLiteral("Adding a row with a very long text");
+ model.insertRow(0);
+ model.setModelData(QPoint(0, 0), QSize(1, 1), longText);
+
+ WAIT_UNTIL_POLISHED;
+
+ QVERIFY(tableView->contentWidth() > initialContentWidth);
+ QVERIFY(tableView->contentHeight() > initialContentHeight);
+}
+
void tst_QQuickTableView::checkPageFlicking()
{
// Check that we rebuild the table instead of refilling edges, if the viewport moves
@@ -2355,6 +2415,82 @@ void tst_QQuickTableView::hideRowsAndColumns()
QVERIFY(!columnsToHideList.contains(column));
}
+void tst_QQuickTableView::hideAndShowFirstColumn()
+{
+ // Check that if we hide the first column, it will move
+ // the second column to the origin of the viewport. Then check
+ // that if we show the first column again, it will reappear at
+ // the origin of the viewport, and as such, pushing the second
+ // column to the right of it.
+ LOAD_TABLEVIEW("hiderowsandcolumns.qml");
+
+ const int modelSize = 5;
+ auto model = TestModelAsVariant(modelSize, modelSize);
+ tableView->setModel(model);
+
+ // Start by making the first column hidden
+ const auto columnsToHideList = QList<int>() << 0;
+ view->rootObject()->setProperty("columnsToHide", QVariant::fromValue(columnsToHideList));
+
+ WAIT_UNTIL_POLISHED;
+
+ const int expectedColumnCount = modelSize - columnsToHideList.count();
+ QCOMPARE(tableViewPrivate->loadedColumns.count(), expectedColumnCount);
+ QCOMPARE(tableViewPrivate->leftColumn(), 1);
+ QCOMPARE(tableView->contentX(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.x(), 0);
+
+ // Make the first column in the model visible again
+ const auto emptyList = QList<int>();
+ view->rootObject()->setProperty("columnsToHide", QVariant::fromValue(emptyList));
+ tableView->forceLayout();
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableViewPrivate->loadedColumns.count(), modelSize);
+ QCOMPARE(tableViewPrivate->leftColumn(), 0);
+ QCOMPARE(tableView->contentX(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.x(), 0);
+}
+
+void tst_QQuickTableView::hideAndShowFirstRow()
+{
+ // Check that if we hide the first row, it will move
+ // the second row to the origin of the viewport. Then check
+ // that if we show the first row again, it will reappear at
+ // the origin of the viewport, and as such, pushing the second
+ // row below it.
+ LOAD_TABLEVIEW("hiderowsandcolumns.qml");
+
+ const int modelSize = 5;
+ auto model = TestModelAsVariant(modelSize, modelSize);
+ tableView->setModel(model);
+
+ // Start by making the first row hidden
+ const auto rowsToHideList = QList<int>() << 0;
+ view->rootObject()->setProperty("rowsToHide", QVariant::fromValue(rowsToHideList));
+
+ WAIT_UNTIL_POLISHED;
+
+ const int expectedRowsCount = modelSize - rowsToHideList.count();
+ QCOMPARE(tableViewPrivate->loadedRows.count(), expectedRowsCount);
+ QCOMPARE(tableViewPrivate->topRow(), 1);
+ QCOMPARE(tableView->contentY(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.y(), 0);
+
+ // Make the first row in the model visible again
+ const auto emptyList = QList<int>();
+ view->rootObject()->setProperty("rowsToHide", QVariant::fromValue(emptyList));
+ tableView->forceLayout();
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableViewPrivate->loadedRows.count(), modelSize);
+ QCOMPARE(tableViewPrivate->topRow(), 0);
+ QCOMPARE(tableView->contentY(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.y(), 0);
+}
+
void tst_QQuickTableView::checkThatRevisionedPropertiesCannotBeUsedInOldImports()
{
// Check that if you use a QQmlAdaptorModel together with a Repeater, the
diff --git a/tests/auto/quick/qquicktext/data/elideZeroWidth.qml b/tests/auto/quick/qquicktext/data/elideZeroWidth.qml
new file mode 100644
index 0000000000..1193909350
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/elideZeroWidth.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ property bool ok: false
+ width: 640
+ height: 480
+
+ Text {
+ id: text
+ text: "This is a quite long text. Click me and i should remain visible!!! Sadly this doesn't happen"
+ elide: Text.ElideRight
+ }
+
+ Component.onCompleted: {
+ text.width = 300;
+ text.height = 0;
+ text.width = 0;
+ text.height = 30;
+ text.width = 300;
+ root.ok = text.paintedWidth > 0 && text.paintedHeight > 0
+ }
+}
diff --git a/tests/auto/quick/qquicktext/data/fontSizeMode.qml b/tests/auto/quick/qquicktext/data/fontSizeMode.qml
index 48e7c7b6d0..d5e794824f 100644
--- a/tests/auto/quick/qquicktext/data/fontSizeMode.qml
+++ b/tests/auto/quick/qquicktext/data/fontSizeMode.qml
@@ -16,7 +16,7 @@ Item {
height: 35
minimumPointSize: 8
minimumPixelSize: 8
- font.pixelSize: 25
+ font.pixelSize: 23
font.family: "Helvetica"
}
}
diff --git a/tests/auto/quick/qquicktext/data/padding.qml b/tests/auto/quick/qquicktext/data/padding.qml
index ab0a37d041..f830af0e40 100644
--- a/tests/auto/quick/qquicktext/data/padding.qml
+++ b/tests/auto/quick/qquicktext/data/padding.qml
@@ -9,4 +9,30 @@ Text {
leftPadding: 30
rightPadding: 40
bottomPadding: 50
+
+ Rectangle {
+ width: parent.leftPadding
+ height: parent.height
+ color: "#6600FF00"
+ }
+
+ Rectangle {
+ width: parent.width
+ height: parent.topPadding
+ color: "#66888800"
+ }
+
+ Rectangle {
+ x: parent.width - parent.rightPadding
+ width: parent.rightPadding
+ height: parent.height
+ color: "#6600FFFF"
+ }
+
+ Rectangle {
+ y: parent.height - parent.bottomPadding
+ width: parent.width
+ height: parent.bottomPadding
+ color: "#66880088"
+ }
}
diff --git a/tests/auto/quick/qquicktext/data/paddingInLoader.qml b/tests/auto/quick/qquicktext/data/paddingInLoader.qml
new file mode 100644
index 0000000000..6ef7c25a9b
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/paddingInLoader.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.12
+
+Item {
+ width: 30
+ height: 30
+ Loader {
+ anchors.fill: parent
+ sourceComponent: Text {
+ rightPadding: 30
+ text: "Some text"
+ elide: Text.ElideRight
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 308c6b5091..03570b43ce 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -55,6 +55,8 @@ QT_BEGIN_NAMESPACE
extern void qt_setQtEnableTestFont(bool value);
QT_END_NAMESPACE
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
class tst_qquicktext : public QQmlDataTest
{
Q_OBJECT
@@ -68,6 +70,7 @@ private slots:
void wrap();
void elide();
void elideParentChanged();
+ void elideRelayoutAfterZeroWidth();
void multilineElide_data();
void multilineElide();
void implicitElide_data();
@@ -152,6 +155,7 @@ private slots:
void growFromZeroWidth();
void padding();
+ void paddingInLoader();
void hintingPreference();
@@ -607,6 +611,15 @@ void tst_qquicktext::elideParentChanged()
QCOMPARE(actualItemImageGrab, expectedItemImageGrab);
}
+void tst_qquicktext::elideRelayoutAfterZeroWidth()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("elideZeroWidth.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY2(root, qPrintable(component.errorString()));
+ QVERIFY(root->property("ok").toBool());
+}
+
void tst_qquicktext::multilineElide_data()
{
QTest::addColumn<QQuickText::TextFormat>("format");
@@ -3465,6 +3478,46 @@ void tst_qquicktext::fontSizeMode()
myText->setElideMode(QQuickText::ElideNone);
QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ // Growing height needs to update the baselineOffset when AlignBottom is used
+ // and text is NOT wrapped
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::Fit);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ int baselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() * 2);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QVERIFY(myText->baselineOffset() > baselineOffset);
+
+ // Growing height needs to update the baselineOffset when AlignBottom is used
+ // and the text is wrapped
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::Fit);
+ myText->setWrapMode(QQuickText::NoWrap);
+ myText->resetMaximumLineCount();
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ baselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() * 2);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QVERIFY(myText->baselineOffset() > baselineOffset);
+
+ // Check baselineOffset for the HorizontalFit case
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::HorizontalFit);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QSignalSpy baselineOffsetSpy(myText, SIGNAL(baselineOffsetChanged(qreal)));
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ const qreal oldBaselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() + 42);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QCOMPARE(baselineOffsetSpy.count(), 1);
+ QCOMPARE(myText->baselineOffset(), oldBaselineOffset + 42);
+ myText->setHeight(myText->height() - 42);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QCOMPARE(baselineOffsetSpy.count(), 2);
+ QCOMPARE(myText->baselineOffset(), oldBaselineOffset);
}
void tst_qquicktext::fontSizeModeMultiline_data()
@@ -4332,6 +4385,20 @@ void tst_qquicktext::padding()
obj->setElideMode(QQuickText::ElideRight);
QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
+
+ obj->setLeftPadding(0);
+ QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
+
+ obj->setWidth(cw);
+ obj->setRightPadding(cw);
+ QCOMPARE(obj->contentWidth(), 0);
+
+ for (int incr = 1; incr < 50 && qFuzzyIsNull(obj->contentWidth()); ++incr)
+ obj->setWidth(cw + incr);
+ QVERIFY(obj->contentWidth() > 0);
+ qCDebug(lcTests) << "increasing Text width from" << cw << "to" << obj->width()
+ << "rendered a character: contentWidth now" << obj->contentWidth();
+
obj->setElideMode(QQuickText::ElideNone);
obj->resetWidth();
@@ -4376,6 +4443,34 @@ void tst_qquicktext::padding()
delete root;
}
+void tst_qquicktext::paddingInLoader() // QTBUG-83413
+{
+ QQuickView view(testFileUrl("paddingInLoader.qml"));
+ view.show();
+ view.requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QQuickText *qtext = view.rootObject()->findChild<QQuickText*>();
+ QVERIFY(qtext);
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(qtext);
+ QVERIFY(textPrivate);
+ QCOMPARE(qtext->contentWidth(), 0); // does not render text, because width == rightPadding
+ QCOMPARE(textPrivate->availableWidth(), 0);
+
+ qtext->setLeftPadding(qtext->width());
+ qtext->setRightPadding(0);
+ QCOMPARE(qtext->contentWidth(), 0); // does not render text, because width == leftPadding
+ QCOMPARE(textPrivate->availableWidth(), 0);
+
+ qtext->setRightPadding(qtext->width());
+ QCOMPARE(qtext->contentWidth(), 0); // does not render text: available space is negative
+ QCOMPARE(textPrivate->availableWidth(), -qtext->width());
+
+ qtext->setLeftPadding(2);
+ qtext->setRightPadding(2);
+ QVERIFY(qtext->contentWidth() > 0); // finally space is available to render text
+ QCOMPARE(textPrivate->availableWidth(), qtext->width() - 4);
+}
+
void tst_qquicktext::hintingPreference()
{
{
diff --git a/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml b/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml
new file mode 100644
index 0000000000..e6f07b4687
--- /dev/null
+++ b/tests/auto/quick/qquicktextinput/data/checkCursorDelegateWhenPaddingChanged.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.12
+
+Rectangle {
+ width: 200
+ height: 200
+ TextInput {
+ objectName: "textInput"
+ leftPadding: 10
+ focus: true
+ cursorDelegate: Rectangle {
+ objectName: "cursorDelegate"
+ width: 5
+ color: "red"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktextinput/data/focusReason.qml b/tests/auto/quick/qquicktextinput/data/focusReason.qml
new file mode 100644
index 0000000000..7ac913d363
--- /dev/null
+++ b/tests/auto/quick/qquicktextinput/data/focusReason.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.2
+
+Rectangle {
+ width: 400
+ height: 400
+
+ Column {
+ spacing: 5
+ TextInput {
+ id: first
+ objectName: "first"
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.backtab: third
+ KeyNavigation.tab: second
+ KeyNavigation.down: second
+ }
+ TextInput {
+ id: second
+ objectName: "second"
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.up: first
+ KeyNavigation.backtab: first
+ KeyNavigation.tab: third
+ }
+ TextInput {
+ objectName: "third"
+ id: third
+ width: 100
+ Rectangle { anchors.fill: parent; color: parent.activeFocus ? "red" : "blue"; opacity: 0.3 }
+ KeyNavigation.backtab: second
+ KeyNavigation.tab: first
+ }
+ Component.onCompleted: {
+ first.focus = true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index 2e64c80b85..0ef1711fd6 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -236,6 +236,9 @@ private slots:
void QTBUG_51115_readOnlyResetsSelection();
void QTBUG_77814_InsertRemoveNoSelection();
+ void checkCursorDelegateWhenPaddingChanged();
+
+ void focusReason();
private:
void simulateKey(QWindow *, int key);
@@ -5921,6 +5924,7 @@ void tst_qquicktextinput::clear()
textInput->clear();
QVERIFY(textInput->text().isEmpty());
+ QVERIFY2(textInput->preeditText().isEmpty(), "Pre-edit text must be empty after clear");
QCOMPARE(spy.count(), 3);
@@ -5929,6 +5933,7 @@ void tst_qquicktextinput::clear()
QVERIFY(!textInput->canUndo());
QCOMPARE(spy.count(), 4);
QCOMPARE(textInput->text(), QString("I am Legend"));
+ QVERIFY2(textInput->preeditText().isEmpty(), "Pre-edit text must be empty after undo");
}
void tst_qquicktextinput::backspaceSurrogatePairs()
@@ -7054,6 +7059,113 @@ void tst_qquicktextinput::QTBUG_77814_InsertRemoveNoSelection()
QCOMPARE(textInput->selectedText(), QString());
}
+void tst_qquicktextinput::checkCursorDelegateWhenPaddingChanged()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("checkCursorDelegateWhenPaddingChanged.qml"));
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QQuickTextInput *textInput = view.rootObject()->findChild<QQuickTextInput *>("textInput");
+ QVERIFY(textInput);
+
+ QQuickItem *cursorDelegate = textInput->findChild<QQuickItem *>("cursorDelegate");
+ QVERIFY(cursorDelegate);
+
+ QCOMPARE(cursorDelegate->x(), textInput->leftPadding());
+ QCOMPARE(cursorDelegate->y(), textInput->topPadding());
+
+ textInput->setPadding(5);
+ QCOMPARE(cursorDelegate->x(), textInput->leftPadding());
+ QCOMPARE(cursorDelegate->y(), textInput->topPadding());
+
+ textInput->setTopPadding(10);
+ QCOMPARE(cursorDelegate->x(), textInput->leftPadding());
+ QCOMPARE(cursorDelegate->y(), textInput->topPadding());
+
+ textInput->setLeftPadding(10);
+ QCOMPARE(cursorDelegate->x(), textInput->leftPadding());
+ QCOMPARE(cursorDelegate->y(), textInput->topPadding());
+}
+
+/*!
+ Verifies that TextInput items get focus in/out events with the
+ correct focus reason set.
+
+ Up and Down keys translates to Backtab and Tab focus reasons.
+
+ See QTBUG-75862.
+*/
+void tst_qquicktextinput::focusReason()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("focusReason.qml"));
+
+ QQuickTextInput *first = view.rootObject()->findChild<QQuickTextInput *>("first");
+ QQuickTextInput *second = view.rootObject()->findChild<QQuickTextInput *>("second");
+ QQuickTextInput *third = view.rootObject()->findChild<QQuickTextInput *>("third");
+ QVERIFY(first && second && third);
+
+ class FocusEventFilter : public QObject
+ {
+ public:
+ using QObject::QObject;
+
+ QHash<QObject*, Qt::FocusReason> lastFocusReason;
+ protected:
+ bool eventFilter(QObject *o, QEvent *e)
+ {
+ if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) {
+ QFocusEvent *fe = static_cast<QFocusEvent*>(e);
+ lastFocusReason[o] = fe->reason();
+ }
+ return QObject::eventFilter(o, e);
+ }
+ } eventFilter;
+ first->installEventFilter(&eventFilter);
+ second->installEventFilter(&eventFilter);
+ third->installEventFilter(&eventFilter);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QCOMPARE(qApp->focusObject(), first);
+ // on some platforms we don't get ActiveWindowFocusReason; tolerate this,
+ // it's not what we are testing in this test
+ if (eventFilter.lastFocusReason[first] != Qt::ActiveWindowFocusReason) {
+ QEXPECT_FAIL("", qPrintable(QString("No window activation event on the %1 platform")
+ .arg(QGuiApplication::platformName())),
+ Continue);
+ }
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::ActiveWindowFocusReason);
+
+ QTest::mouseClick(&view, Qt::LeftButton, {},
+ (second->boundingRect().center() + second->position()).toPoint());
+ QTRY_COMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::MouseFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::MouseFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Tab);
+ QCOMPARE(qApp->focusObject(), third);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[third], Qt::TabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Backtab);
+ QCOMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[third], Qt::BacktabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Up);
+ QCOMPARE(qApp->focusObject(), first);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::BacktabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::BacktabFocusReason);
+
+ QTest::keyClick(&view, Qt::Key_Down);
+ QCOMPARE(qApp->focusObject(), second);
+ QCOMPARE(eventFilter.lastFocusReason[second], Qt::TabFocusReason);
+ QCOMPARE(eventFilter.lastFocusReason[first], Qt::TabFocusReason);
+}
+
QTEST_MAIN(tst_qquicktextinput)
#include "tst_qquicktextinput.moc"
diff --git a/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml b/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml
new file mode 100644
index 0000000000..05623fbc5b
--- /dev/null
+++ b/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml
@@ -0,0 +1,43 @@
+import QtQuick 2.0
+
+Item {
+ width: 200
+ height: 200
+ id: root
+ property int creationCount: 0
+ property bool testStarted: false
+
+ ListModel {
+ id: mymodel
+ ListElement {message: "This is my alarm"}
+ }
+
+ Component {
+ id: aDelegate
+ Rectangle {
+ color: "blue"
+ width: 100
+ height: 100
+ }
+ }
+
+ Component {
+ id: bDelegate
+ Rectangle {
+ color: "red"
+ width: 100
+ height: 100
+ Text {text: model.message }
+ Component.onCompleted: root.creationCount++
+ }
+ }
+
+
+ ListView {
+ width: 200
+ height: 200
+ id: myListView
+ model: mymodel
+ delegate: testStarted ? bDelegate : aDelegate
+ }
+}
diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
index 27bd8aae49..83bdb1a5f8 100644
--- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
+++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
@@ -434,6 +434,7 @@ private slots:
void invalidContext();
void externalManagedModel();
void delegateModelChangeDelegate();
+ void noDoubleDelegateUpdate();
void checkFilterGroupForDelegate();
void readFromProxyObject();
@@ -4345,6 +4346,20 @@ void tst_qquickvisualdatamodel::delegateModelChangeDelegate()
QCOMPARE(visualModel->count(), 3);
}
+void tst_qquickvisualdatamodel::noDoubleDelegateUpdate()
+{
+ // changing a delegate only refreshes its instances once
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("setDelegateNoDoubleChange.qml"));
+
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+
+ bool ok = root->setProperty("testStarted", true);
+ QVERIFY(ok);
+ QCOMPARE(root->property("creationCount").toInt(), 1);
+}
+
void tst_qquickvisualdatamodel::checkFilterGroupForDelegate()
{
QQuickView view;
diff --git a/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml
new file mode 100644
index 0000000000..f06be8f553
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.15
+
+/*
+ QTBUG-92984.
+
+ Have three Image (with semi-transparency) and two semi-transparent
+ Rectangle elements, and so all in the alpha render list, with images that
+ are big enough to not get atlased. (meaning the underlying nodes never get
+ merged, but the nodes for the Rectangle elements might)
+
+ Lay them out vertically below each other, with the two Rectangles on top of
+ the second and third Images, respectively. Then change (swap) the source
+ property of the Images. This triggers a rebuild in the batch renderer.
+
+ Verify that the results are still correct, i.e. that the two Rectangle
+ elements do not get merged. An incorrect result would be having the third
+ Image rendered on top of the corresponding Rectangle due the two Rectangles
+ (incorrectly) being in one merged batch. The Image should always be below,
+ regardless of which nodes get changed, invalidated, and how batches get
+ rebuilt.
+
+ The base-final sample set 1 just verifies that the Image changes from the
+ blueish to greenish. The important part is the second set of samples: this
+ checks that the red(ish) rectangle is still on top of the third Image. With
+ incorrect merging behavior the second final result would be the same as the
+ first final one (i.e. the "background" Image rendered, incorrectly, on top
+ of the Rectangle).
+
+ #samples: 4
+ PixelPos R G B Error-tolerance
+ #base: 30 115 0.24313 0.30588 0.99607 0.05
+ #base: 30 124 0.847059 0.062745 0.2 0.05
+ #final: 30 115 0.36078 0.99607 0.42745 0.05
+ #final: 30 124 0.870588 0.2 0.0862745 0.05
+*/
+
+RenderTestBase {
+ id: root
+
+ property string selectedItem: "item2"
+
+ Item {
+ width: 150; height: 50
+ Image {
+ width: parent.width
+ objectName: "item1"
+ source: "widebtn1.png"
+ }
+ }
+
+ Item {
+ y: 50; width: 150; height: 50
+ Image {
+ width: parent.width
+ objectName: "item2"
+ source: selectedItem == objectName ? "widebtn2.png" : "widebtn1.png"
+ }
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 20
+ color: "red"
+ opacity: 0.8
+ }
+ }
+
+ Item {
+ y: 100; width: 150; height: 50
+ Image {
+ id: img3
+ width: parent.width
+ objectName: "item3"
+ source: selectedItem == objectName ? "widebtn2.png" : "widebtn1.png"
+ }
+ Rectangle {
+ width: parent.width + 50
+ anchors.centerIn: parent
+ height: img3.height - 40
+ color: "red"
+ opacity: 0.8
+ }
+ }
+
+ onEnterFinalStage: {
+ selectedItem = "item3";
+ finalStageComplete = true;
+ }
+}
diff --git a/tests/auto/quick/scenegraph/data/widebtn1.png b/tests/auto/quick/scenegraph/data/widebtn1.png
new file mode 100644
index 0000000000..1150b67a7a
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/widebtn1.png
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/widebtn2.png b/tests/auto/quick/scenegraph/data/widebtn2.png
new file mode 100644
index 0000000000..40afe08363
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/widebtn2.png
Binary files differ
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 12f7efb7d5..cf85e262c0 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -402,7 +402,9 @@ void tst_SceneGraph::render_data()
<< "render_StackingOrder.qml"
<< "render_ImageFiltering.qml"
<< "render_bug37422.qml"
- << "render_OpacityThroughBatchRoot.qml";
+ << "render_OpacityThroughBatchRoot.qml"
+ << "render_AlphaOverlapRebuild.qml";
+
if (!m_brokenMipmapSupport)
files << "render_Mipmap.qml";
diff --git a/tests/auto/quicktest/quicktest.pro b/tests/auto/quicktest/quicktest.pro
index 6d09f76c1d..2116e4d3ac 100644
--- a/tests/auto/quicktest/quicktest.pro
+++ b/tests/auto/quicktest/quicktest.pro
@@ -3,4 +3,5 @@ SUBDIRS = \
polish \
signalspy \
quicktestmainwithsetup \
- testfiltering
+ testfiltering \
+ testwithcomponents
diff --git a/tests/auto/quicktest/testwithcomponents/data/Sample.qml b/tests/auto/quicktest/testwithcomponents/data/Sample.qml
new file mode 100644
index 0000000000..78e3008b01
--- /dev/null
+++ b/tests/auto/quicktest/testwithcomponents/data/Sample.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+
+ component InlineComponent: Rectangle {}
+ InlineComponent{}
+}
diff --git a/tests/auto/qmltest/pixel/tst_pixel.qml b/tests/auto/quicktest/testwithcomponents/data/tst_setup.qml
index e628fed1d8..533027147e 100644
--- a/tests/auto/qmltest/pixel/tst_pixel.qml
+++ b/tests/auto/quicktest/testwithcomponents/data/tst_setup.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -27,39 +27,27 @@
****************************************************************************/
import QtQuick 2.0
-import QtTest 1.1
+import QtTest 1.2
-Rectangle {
- id:rect
- width: 40
- height: 40
- color:"red"
- TestCase {
- name: "Pixels"
- when: windowShown
-
- function test_pixel() {
- skip("test_pixel() is unstable, QTBUG-27671")
- var img = grabImage(rect);
- compare(img.pixel(20, 20), Qt.rgba(255, 0, 0, 255));
- compare(img.red(1,1), 255);
- compare(img.green(1,1), 0);
- compare(img.blue(1,1), 0);
- compare(img.alpha(1,1), 255);
- fuzzyCompare(img.red(1,1), 254, 2);
- fuzzyCompare(img.pixel(1,1), Qt.rgba(254, 0, 0, 254), 2);
- fuzzyCompare(img.pixel(1,1), "#FF0201", 2);
+Item {
- rect.color = "blue";
- waitForRendering(rect);
- img = grabImage(rect);
- compare(img.pixel(20, 20), Qt.rgba(0, 0, 255, 255));
- compare(img.red(1,1), 0);
- compare(img.green(1,1), 0);
- compare(img.blue(1,1), 255);
- compare(img.alpha(1,1), 255);
+ Component {
+ id: mock
+ Sample {
}
+ }
+
+ component Mock : Sample {}
+ TestCase {
+ id: root
+ name: "ComponentTest"
+
+ function test_create()
+ {
+ let dialog = createTemporaryObject(mock, root);
+ verify(dialog);
+ }
}
-} \ No newline at end of file
+}
diff --git a/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro b/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro
new file mode 100644
index 0000000000..8f64dc2ebb
--- /dev/null
+++ b/tests/auto/quicktest/testwithcomponents/testwithcomponents.pro
@@ -0,0 +1,10 @@
+CONFIG += qmltestcase
+macos:CONFIG -= app_bundle
+TARGET = tst_quicktestwithcomponents
+
+QT += testlib quick
+
+SOURCES += tst_quicktestwithcomponents.cpp
+
+TESTDATA += \
+ $$PWD/data/*.qml
diff --git a/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp b/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp
new file mode 100644
index 0000000000..9692347cb8
--- /dev/null
+++ b/tests/auto/quicktest/testwithcomponents/tst_quicktestwithcomponents.cpp
@@ -0,0 +1,32 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/qtest.h>
+#include <QtQuickTest/quicktest.h>
+
+QUICK_TEST_MAIN(data)
diff --git a/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml
new file mode 100644
index 0000000000..92598eb490
--- /dev/null
+++ b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+//vary font style, native rendering without antialiasing
+
+Item {
+ id: topLevel
+ width: 320
+ height: 580
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 20 * index
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ antialiasing: false
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+}
diff --git a/tests/manual/pointer/pinchHandler.qml b/tests/manual/pointer/pinchHandler.qml
index 46ab91c2ed..93169da60a 100644
--- a/tests/manual/pointer/pinchHandler.qml
+++ b/tests/manual/pointer/pinchHandler.qml
@@ -154,6 +154,14 @@ Rectangle {
if (!active)
anim.restart(centroid.velocity)
}
+ onGrabChanged: function (transition, point) {
+ if (transition === 0x10) { // GrabExclusive
+ console.log(point.id, "grabbed @", point.position)
+ Qt.createQmlObject("import QtQuick 2.0; Rectangle { opacity: 0.5; border.color: 'red'; radius: 8; width: radius * 2; height: radius * 2; " +
+ "x: " + (point.position.x - 8) + "; y: " + (point.position.y - 8) + "}",
+ rect3, "touchpoint" + point.id);
+ }
+ }
}
TapHandler { gesturePolicy: TapHandler.DragThreshold; onTapped: rect3.z = rect2.z + 1 }
MomentumAnimation { id: anim; target: rect3 }
diff --git a/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_multi.qml b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_multi.qml
new file mode 100644
index 0000000000..277ac4f2db
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_multi.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 320
+ height: 480
+ property int standardWidth: 60
+ property int standardHeight: 60
+ property int standardSpacing: 20
+ property bool smoothing: true
+ Rectangle {
+ width: 150
+ height: 150
+ anchors.centerIn: parent
+ color: "black"
+ clip: true
+ rotation: 45
+ Rectangle {
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+ color: "lightGray"
+ clip: true
+ Grid {
+ anchors.centerIn: parent
+ columns: 4
+ spacing: standardSpacing
+ Rectangle{ color: "red"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardHeight/2 ; axis{x: 0; y: 0; z:1} angle: 5; } smooth: smoothing}
+ Rectangle{ color: "orange"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 10; } smooth: smoothing }
+ Rectangle{ color: "yellow"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing }
+ Rectangle{ color: "blue"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 20; } smooth: smoothing }
+ Rectangle{ color: "green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardWidth/2 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing}
+ Rectangle{ color: "indigo"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 30; } smooth: smoothing}
+ Rectangle{ color: "violet"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 35; } smooth: smoothing }
+ Rectangle{ color: "light green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 40; } smooth: smoothing }
+ }
+ }
+ }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_scissor.qml b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_scissor.qml
new file mode 100644
index 0000000000..71379ff1bb
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_scissor.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 320
+ height: 480
+ property int standardWidth: 60
+ property int standardHeight: 60
+ property int standardSpacing: 20
+ property bool smoothing: true
+ Rectangle {
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+ color: "lightGray"
+ clip: true
+ Grid {
+ anchors.centerIn: parent
+ columns: 4
+ spacing: standardSpacing
+ Rectangle{ color: "red"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardHeight/2 ; axis{x: 0; y: 0; z:1} angle: 5; } smooth: smoothing}
+ Rectangle{ color: "orange"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 10; } smooth: smoothing }
+ Rectangle{ color: "yellow"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing }
+ Rectangle{ color: "blue"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 20; } smooth: smoothing }
+ Rectangle{ color: "green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardWidth/2 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing}
+ Rectangle{ color: "indigo"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 30; } smooth: smoothing}
+ Rectangle{ color: "violet"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 35; } smooth: smoothing }
+ Rectangle{ color: "light green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 40; } smooth: smoothing }
+ }
+ }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_stencil.qml b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_stencil.qml
new file mode 100644
index 0000000000..f6167e70e0
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/rotation/rotation_clip_stencil.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 320
+ height: 480
+ property int standardWidth: 60
+ property int standardHeight: 60
+ property int standardSpacing: 20
+ property bool smoothing: true
+ Rectangle {
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+ color: "lightGray"
+ rotation: 45
+ clip: true
+ Grid {
+ anchors.centerIn: parent
+ columns: 4
+ spacing: standardSpacing
+ Rectangle{ color: "red"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardHeight/2 ; axis{x: 0; y: 0; z:1} angle: 5; } smooth: smoothing}
+ Rectangle{ color: "orange"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 10; } smooth: smoothing }
+ Rectangle{ color: "yellow"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing }
+ Rectangle{ color: "blue"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 20; } smooth: smoothing }
+ Rectangle{ color: "green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: standardWidth/2; origin.y: standardWidth/2 ; axis{x: 0; y: 0; z:1} angle: 15; } smooth: smoothing}
+ Rectangle{ color: "indigo"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 30; } smooth: smoothing}
+ Rectangle{ color: "violet"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 35; } smooth: smoothing }
+ Rectangle{ color: "light green"; width: standardWidth; height: standardHeight; transform: Rotation { origin.x: 0; origin.y: 0 ; axis{x: 0; y: 0; z:1} angle: 40; } smooth: smoothing }
+ }
+ }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/text/text_nativerendering_subpixelpositions.qml b/tests/manual/scenegraph_lancelot/data/text/text_nativerendering_subpixelpositions.qml
new file mode 100644
index 0000000000..c60fc4d8b0
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/text/text_nativerendering_subpixelpositions.qml
@@ -0,0 +1,91 @@
+import QtQuick 2.0
+
+//vary font style, native rendering at non-integer offsets
+
+Item {
+ id: topLevel
+ width: 320
+ height: 580
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 20 * index
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 100.5 + 20 * index
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 200.5 + 20 * index
+ x: 0.5
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 300.5 + 20 * index
+ x: 0.5
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Rectangle {
+ y: 400.5 + 20 * index
+ x: 0.5
+ width: topLevel.width
+ height: topLevel.height
+ clip: true
+ Text {
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/storagemodel/storagemodel.cpp b/tests/manual/tableview/storagemodel/storagemodel.cpp
index b43454b249..f0394db6b8 100644
--- a/tests/manual/tableview/storagemodel/storagemodel.cpp
+++ b/tests/manual/tableview/storagemodel/storagemodel.cpp
@@ -64,17 +64,20 @@ StorageModel::StorageModel(QObject *parent) :
}
QHash<int, QByteArray> StorageModel::roleNames() const {
- static auto roles = QHash<int, QByteArray> {
- { int(Role::Type), "type" },
- { int(Role::Heading), "heading" },
- { int(Role::Value), "value" },
- { int(Role::ValueMax), "valueMax" },
- { int(Role::ValueDisplay), "valueDisplay" },
- { int(Role::ValueMaxDisplay), "valueMaxDisplay" },
- { Qt::CheckStateRole, "checkState" },
- };
- static auto ret = roles.unite(QAbstractTableModel::roleNames());;
- return ret;
+ static auto roles = [this]() {
+ auto roles = QHash<int, QByteArray> {
+ { int(Role::Type), "type" },
+ { int(Role::Heading), "heading" },
+ { int(Role::Value), "value" },
+ { int(Role::ValueMax), "valueMax" },
+ { int(Role::ValueDisplay), "valueDisplay" },
+ { int(Role::ValueMaxDisplay), "valueMaxDisplay" },
+ { Qt::CheckStateRole, "checkState" }
+ };
+ roles.insert(QAbstractTableModel::roleNames());
+ return roles;
+ }();
+ return roles;
}
void StorageModel::refresh()
diff --git a/tests/manual/touch/flicktext.qml b/tests/manual/touch/flicktext.qml
index 9e84261687..e69d6207a9 100644
--- a/tests/manual/touch/flicktext.qml
+++ b/tests/manual/touch/flicktext.qml
@@ -380,6 +380,36 @@ Rectangle {
text: "content X " + flick.contentX.toFixed(2) + " Y " + flick.contentY.toFixed(2)
}
}
+
+ Column {
+ Row {
+ spacing: 2
+ Examples.Button {
+ id: decrButton
+ text: "-"
+ onClicked: flick.flickDeceleration -= 100
+ Timer {
+ running: decrButton.pressed
+ interval: 100; repeat: true
+ onTriggered: flick.flickDeceleration -= 100
+ }
+ }
+ Text {
+ horizontalAlignment: Text.AlignHCenter
+ text: "decel:\n" + flick.flickDeceleration.toFixed(4)
+ }
+ Examples.Button {
+ id: incrButton
+ text: "+"
+ onClicked: flick.flickDeceleration += 100
+ }
+ Timer {
+ running: incrButton.pressed
+ interval: 100; repeat: true
+ onTriggered: flick.flickDeceleration += 100
+ }
+ }
+ }
}
Component.onCompleted: {
diff --git a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
index 26d42c02a9..8a91fb8b87 100644
--- a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
+++ b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
@@ -40,6 +40,7 @@ but not all the files it references.
set(rcc_files ${_RCC_UNPARSED_ARGUMENTS})
set(rcc_options ${_RCC_OPTIONS})
set(filtered_rcc_files)
+ set(output_resources)
set(compiler_output)
set(rcc_files_with_compilation_units)
set(loader_flags)
@@ -52,13 +53,15 @@ but not all the files it references.
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${input_resource}")
execute_process(COMMAND ${compiler_path} --filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files)
- list(APPEND filtered_rcc_files ${new_resource_file})
- list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\")
set(rcc_file_with_compilation_units)
execute_process(COMMAND ${rcc_path} -list \"${input_resource}\" OUTPUT_VARIABLE rcc_contents)
if (NOT rcc_contents STREQUAL \"\")
+
+ list(APPEND filtered_rcc_files ${new_resource_file})
+ list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\")
+
string(REGEX REPLACE \"[\r\n]+\" \";\" rcc_contents ${rcc_contents})
foreach(it ${rcc_contents})
get_filename_component(extension ${it} EXT)
@@ -85,6 +88,8 @@ but not all the files it references.
list(APPEND compiler_output ${loader_source})
endif()
- qt5_add_resources(output_resources ${filtered_rcc_files} OPTIONS ${rcc_options})
+ if(filtered_rcc_files)
+ qt5_add_resources(output_resources ${filtered_rcc_files} OPTIONS ${rcc_options})
+ endif()
set(${outfiles} ${output_resources} ${compiler_output} PARENT_SCOPE)
endfunction()
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index bc74e7b08e..050dd95e45 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -135,7 +135,7 @@ static void annotateListElements(QmlIR::Document *document)
if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex)))
continue;
for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex));
}
@@ -146,7 +146,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
{
for (QmlIR::Object *object: qAsConst(doc.objects)) {
for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
const QString propName = doc.stringAt(binding->propertyNameIndex);
if (!propName.startsWith(QLatin1String("on"))
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 5e3f41e8c6..8209236916 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -74,18 +74,18 @@ inline QString directoryLiteral() { return QStringLiteral("directory"); }
void printUsage(const QString &appNameIn)
{
- const std::wstring appName = appNameIn.toStdWString();
+ const std::string appName = appNameIn.toStdString();
#ifndef QT_BOOTSTRAPPED
const QString qmlPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
#else
const QString qmlPath = QStringLiteral("/home/user/dev/qt-install/qml");
#endif
- std::wcerr
+ std::cerr
<< "Usage: " << appName << " -rootPath path/to/app/qml/directory -importPath path/to/qt/qml/directory\n"
" " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n"
" " << appName << " -qrcFiles file1.qrc file2.qrc -importPath path/to/qt/qml/directory\n\n"
"Example: " << appName << " -rootPath . -importPath "
- << QDir::toNativeSeparators(qmlPath).toStdWString()
+ << QDir::toNativeSeparators(qmlPath).toStdString()
<< '\n';
}
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 1556718471..21d04df853 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -109,14 +109,16 @@ struct QmlVersionInfo
};
static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) {
+ const QString &module = ty.module();
if (versionInfo.strict) {
- return (versionInfo.pluginImportUri == ty.module()
- && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1))
- || ty.module().isEmpty();
+ return (versionInfo.pluginImportUri == module
+ && (ty.majorVersion() == versionInfo.majorVersion
+ || ty.majorVersion() == -1))
+ || module.isEmpty();
}
- return ty.module().isEmpty()
- || versionInfo.pluginImportUri == ty.module()
- || ty.module().startsWith(versionInfo.pluginImportUri + QLatin1Char('.'));
+ return module.isEmpty()
+ || versionInfo.pluginImportUri == module
+ || module.startsWith(versionInfo.pluginImportUri + QLatin1Char('.'));
}
void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false)
@@ -989,19 +991,9 @@ bool compactDependencies(QStringList *dependencies)
return false;
}
-inline std::wostream &operator<<(std::wostream &str, const QString &s)
-{
-#ifdef Q_OS_WIN
- str << reinterpret_cast<const wchar_t *>(s.utf16());
-#else
- str << s.toStdWString();
-#endif
- return str;
-}
-
void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg)
{
- std::wcerr << msg << std::endl;
+ std::cerr << msg.toStdString() << std::endl;
// In case of QtFatalMsg the calling code will abort() when appropriate.
}