aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2026-05-25 13:55:19 +0300
committerTarja Sundqvist <tarja.sundqvist@qt.io>2026-05-25 13:55:19 +0300
commit3fd4f41cd306ee97aaf47400fba75cc874048215 (patch)
tree6c60d274e67e90b3a44ac1c8c476a91274beb962
parent22032227d16c39211e2ebceef97d21f4d89c7c87 (diff)
parent94cf5162f8817fb7883ad56ac704e752e5dab5a4 (diff)
Merge tag 'v6.5.9-lts' into tqtc/lts-6.5-opensourcev6.5.9-lts-lgpl
Qt 6.5.9-lts release Conflicts solved: dependencies.yaml Change-Id: I6f892bad653449a1dd0e0c6e66cc74eee8657cda
-rw-r--r--.cmake.conf2
-rw-r--r--cmake/QtDeclarativeSetup.cmake8
-rw-r--r--dependencies.yaml10
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.cpp8
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp48
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h8
-rw-r--r--src/qml/Qt6QmlMacros.cmake11
-rw-r--r--src/qml/common/qqmltranslation.cpp4
-rw-r--r--src/qml/common/qv4compileddata_p.h2
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h17
-rw-r--r--src/qml/doc/src/cmake/policy/qtp0001.qdoc4
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc4
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc37
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc6
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc74
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc7
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/enumerations.qdoc74
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc58
-rw-r--r--src/qml/doc/src/qt6-changes.qdoc4
-rw-r--r--src/qml/jsapi/qjsvalue.cpp13
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp36
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp9
-rw-r--r--src/qml/jsruntime/qv4value_p.h8
-rw-r--r--src/qml/qml/qqmlanybinding_p.h32
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp16
-rw-r--r--src/qml/qml/qqmlcomponent.cpp5
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp6
-rw-r--r--src/qml/qml/qqmlproperty.cpp8
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp2
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h9
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h31
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding.cpp2
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp3
-rw-r--r--src/qml/qml/qqmltypedata.cpp5
-rw-r--r--src/qml/qml/qqmltypeloader.cpp5
-rw-r--r--src/qml/qml/qqmltypenamecache_p.h3
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp5
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp29
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h2
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h2
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp304
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h50
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h14
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp25
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h12
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp5
-rw-r--r--src/qmldom/qqmldomelements.cpp5
-rw-r--r--src/qmldom/qqmldomelements_p.h2
-rw-r--r--src/qmldom/qqmldomtop.cpp16
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp5
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp15
-rw-r--r--src/quick/doc/snippets/pointerHandlers/wheelHandlerAcceptedModifiers.qml20
-rw-r--r--src/quick/doc/snippets/pointerHandlers/wheelHandlerMargin.qml15
-rw-r--r--src/quick/doc/src/concepts/input/focus.qdoc8
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc6
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp112
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp2
-rw-r--r--src/quick/items/qquickitem.cpp2
-rw-r--r--src/quick/items/qquicklistview.cpp21
-rw-r--r--src/quick/items/qquickpalette.cpp2
-rw-r--r--src/quick/items/qquickselectable_p.h2
-rw-r--r--src/quick/items/qquicktableview.cpp6
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp10
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp27
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp15
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp4
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp6
-rw-r--r--src/quick/scenegraph/qsgrhilayer.cpp11
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp4
-rw-r--r--src/quick/util/qquickforeignutils.cpp2
-rw-r--r--src/quick/util/qquickpath.cpp2
-rw-r--r--src/quickcontrols/ios/Dial.qml55
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp2
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle.cpp8
-rw-r--r--src/quickcontrolsimpl/qquickanimatednode.cpp4
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog.cpp63
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog_p.h17
-rw-r--r--src/quicknativestyle/util/FocusFrame.qml37
-rw-r--r--src/quicknativestyle/util/qquickmacfocusframe.mm1
-rw-r--r--src/quicktemplates/CMakeLists.txt2
-rw-r--r--src/quicktemplates/qquickabstractbutton.cpp2
-rw-r--r--src/quicktemplates/qquickapplicationwindow.cpp3
-rw-r--r--src/quicktemplates/qquickcontrol.cpp13
-rw-r--r--src/quicktemplates/qquickdrawer.cpp4
-rw-r--r--src/quicktemplates/qquickpopup.cpp9
-rw-r--r--src/quicktemplates/qquickpopup_p_p.h2
-rw-r--r--src/quicktemplates/qquickslider.cpp6
-rw-r--r--src/quicktemplates/qquickswipedelegate.cpp4
-rw-r--r--src/quicktemplates/qquickswitch.cpp4
-rw-r--r--src/quicktemplates/qquickswitchdelegate.cpp4
-rw-r--r--src/quicktemplates/qquicktemplatesutils.cpp35
-rw-r--r--src/quicktemplates/qquicktemplatesutils_p.h28
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp8
-rw-r--r--src/quicktestutils/quick/visualtestutils_p.h7
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp6
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp7
-rw-r--r--tests/auto/qml/qmlcppcodegen/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/CMakeLists.txt27
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/Main.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/Main2.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/Test/broken.js3
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/Test/qmldir2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Confused/Test/test.js3
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp22
-rw-r--r--tests/auto/qml/qmllint/data/GPane.qml10
-rw-r--r--tests/auto/qml/qmllint/data/aliasGroup.qml20
-rw-r--r--tests/auto/qml/qmllint/data/aliasToRequiredPropertyIsNotRequiredItself.qml30
-rw-r--r--tests/auto/qml/qmllint/data/deceptiveLayout.qml10
-rw-r--r--tests/auto/qml/qmllint/data/evals.qml6
-rw-r--r--tests/auto/qml/qmllint/data/missingRequiredPropertyOnObjectDefinitionBinding.qml7
-rw-r--r--tests/auto/qml/qmllint/data/setRequiredPropertyThroughAlias.qml18
-rw-r--r--tests/auto/qml/qmllint/data/setRequiredPropertyThroughAliasOfAlias.qml20
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp17
-rw-r--r--tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp43
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp8
-rw-r--r--tests/auto/qml/qqmllanguage/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmllanguage/OtherModuleTest/CMakeLists.txt15
-rw-r--r--tests/auto/qml/qqmllanguage/OtherModuleTest/other.h50
-rw-r--r--tests/auto/qml/qqmllanguage/data/JsSelfImport/CustomPrinter.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/JsSelfImport/JsLib.js6
-rw-r--r--tests/auto/qml/qqmllanguage/data/JsSelfImport/Main.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/JsSelfImport/qmldir5
-rw-r--r--tests/auto/qml/qqmllanguage/data/aliasOfBindableValueTypeProperty.qml21
-rw-r--r--tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderAB.qml12
-rw-r--r--tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderBA.qml12
-rw-r--r--tests/auto/qml/qqmllanguage/data/attachedTypeResolutionImportOrder.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/singletonResolutionImportOrder.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h54
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp116
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp12
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp5
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp33
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST13
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp3
-rw-r--r--tests/auto/quick/qquickanimators/BLACKLIST1
-rw-r--r--tests/auto/quick/qquickanimators/tst_qquickanimators.cpp4
-rw-r--r--tests/auto/quick/qquickapplication/BLACKLIST5
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp3
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp4
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST3
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp1
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp2
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp76
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp4
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp10
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_button.qml40
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritPaletteForPopupWithinItemView.qml30
-rw-r--r--tests/auto/quickcontrols/palette/tst_palette.cpp31
-rw-r--r--tests/auto/quickcontrols/pointerhandlers/data/controlandmousearea.qml5
-rw-r--r--tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp4
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/data/hoverInMouseArea.qml40
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp31
-rw-r--r--tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp19
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp11
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp6
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/BLACKLIST6
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml27
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml28
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml58
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp65
-rw-r--r--tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp1
-rw-r--r--tests/manual/qmldom/CMakeLists.txt (renamed from examples/qml/qmldom/CMakeLists.txt)6
-rw-r--r--tests/manual/qmldom/qmldomloadeditwrite.cpp (renamed from examples/qml/qmldom/qmldomloadeditwrite.cpp)10
-rw-r--r--tools/qmlcachegen/CMakeLists.txt35
171 files changed, 2302 insertions, 612 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 791e84b56a..856ec9c329 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,4 +1,4 @@
-set(QT_REPO_MODULE_VERSION "6.5.8")
+set(QT_REPO_MODULE_VERSION "6.5.9")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1")
diff --git a/cmake/QtDeclarativeSetup.cmake b/cmake/QtDeclarativeSetup.cmake
index 165a71e09a..b174756343 100644
--- a/cmake/QtDeclarativeSetup.cmake
+++ b/cmake/QtDeclarativeSetup.cmake
@@ -3,9 +3,9 @@
# 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 tree hash. For unreleased versions, we'll ask git
-# rev-parse. If none of this works, we use CMake to hash all the files
-# in the src/qml/ directory.
+# archive with the tree hash. For unreleased versions, we'll do
+# "git show -s --format=format:%T" to get the tree hash. If none of this
+# works, we use CMake to hash all the files in the src/qml/ directory.
# Skip recreation of the hash when doing a developer build.
function(qt_declarative_write_tag_header target_name)
set(out_file "${CMAKE_CURRENT_BINARY_DIR}/qml_compile_hash_p.h")
@@ -27,7 +27,7 @@ function(qt_declarative_write_tag_header target_name)
set(QML_COMPILE_HASH "${tag_contents}")
elseif(git_path AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../.git")
execute_process(
- COMMAND ${git_path} rev-parse HEAD
+ COMMAND ${git_path} show -s --format=format:%T HEAD
OUTPUT_VARIABLE QML_COMPILE_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
diff --git a/dependencies.yaml b/dependencies.yaml
index 7a078aea27..fcbad32e87 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../tqtc-qtbase:
- ref: 0fe1875684a77d985277eaf58741e64ef6514bca
+ ref: ee5586e7ad1fa061b9cdb529dba9cd131678f4b7
required: true
../tqtc-qtimageformats:
- ref: a293ff4fff3101beacf74ea5ad98cde575d3cb72
+ ref: dc8311f1d12bd0e0a560493f0d3e797a4ff376fc
required: false
../tqtc-qtlanguageserver:
- ref: 82ebb8fba7417cea5cada61c9200f40fe3873c7e
+ ref: 693d3ed6424afd701ec9c004b00fa230a3d79366
required: false
../tqtc-qtshadertools:
- ref: 1045af7131573c7db4580df0d28663269b535e69
+ ref: 1f3cc2e23adf1943131465aa0ada1feb12563fb9
required: false
../tqtc-qtsvg:
- ref: 93dc5a03c1e296ac8618471bbdfedf8303442b63
+ ref: f69e56c3fffde393ac5bf889ebd933d41e991876
required: false
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp
index 010ace51e5..c1775ce8b8 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.cpp
+++ b/src/plugins/qmllint/quick/quicklintplugin.cpp
@@ -52,6 +52,14 @@ void ForbiddenChildrenPropertyValidatorPass::run(const QQmlSA::Element &element)
{
for (const auto elementPair : m_types.asKeyValueRange()) {
const QQmlSA::Element &type = elementPair.first;
+ const QQmlSA::Element parentScope = element->parentScope();
+
+ // If the parent's default property is not what we think it is, then we can't say whether
+ // the element in question is actually a visual child of the (document) parent scope.
+ const auto defaultProperty = parentScope->property(parentScope->defaultPropertyName());
+ if (defaultProperty != type->property(type->defaultPropertyName()))
+ continue;
+
if (!element->parentScope()->inherits(type))
continue;
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
index c4b42ed1be..e4020f0b95 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
@@ -19,7 +19,7 @@ void QQmlPreviewBlacklist::whitelist(const QString &path)
bool QQmlPreviewBlacklist::isBlacklisted(const QString &path) const
{
- return path.isEmpty() ? true : m_root.containedPrefixLeaf(path, 0) > 0;
+ return path.isEmpty() ? true : m_root.findPrefix(path, 0) == Node::MatchedLeaf;
}
void QQmlPreviewBlacklist::clear()
@@ -138,33 +138,49 @@ void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset)
if (offset == path.size())
return;
- auto it = m_next.find(path.at(offset));
- if (it != m_next.end())
- (*it)->remove(path, ++offset);
+ Node *&node = m_next[path.at(offset++)];
+ if (node) {
+ node->remove(path, offset);
+ } else {
+ QString inserted;
+ inserted.resize(path.size() - offset);
+ std::copy(path.begin() + offset, path.end(), inserted.begin());
+ node = new Node(inserted, {}, false);
+ }
}
-int QQmlPreviewBlacklist::Node::containedPrefixLeaf(const QString &path, int offset) const
+QQmlPreviewBlacklist::Node::PrefixResult QQmlPreviewBlacklist::Node::findPrefix(
+ const QString &path, int offset) const
{
- if (offset == path.size())
- return (m_mine.isEmpty() && m_isLeaf) ? offset : -1;
+ if (offset == path.size()) {
+ if (!m_mine.isEmpty())
+ return Unmatched;
+ return m_isLeaf ? MatchedLeaf : MatchedBranch;
+ }
for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
if (path.at(offset) != *it)
- return -1;
+ return Unmatched;
- if (++offset == path.size())
- return (++it == end && m_isLeaf) ? offset : -1;
+ if (++offset == path.size()) {
+ if (++it != end)
+ return Unmatched;
+ return m_isLeaf ? MatchedLeaf : MatchedBranch;
+ }
}
const QChar c = path.at(offset);
- if (m_isLeaf && c == '/')
- return offset;
+ const auto it = m_next.find(c);
+ if (it != m_next.end()) {
+ const PrefixResult result = (*it)->findPrefix(path, offset + 1);
+ if (result != Unmatched)
+ return result;
+ }
- auto it = m_next.find(c);
- if (it == m_next.end())
- return -1;
+ if (c == '/')
+ return m_isLeaf ? MatchedLeaf : MatchedBranch;
- return (*it)->containedPrefixLeaf(path, ++offset);
+ return Unmatched;
}
QQmlPreviewBlacklist::Node::Node(const QString &mine,
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
index 715acf4ae6..4aca70b612 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
@@ -33,6 +33,12 @@ public:
private:
class Node {
public:
+ enum PrefixResult {
+ MatchedLeaf,
+ MatchedBranch,
+ Unmatched,
+ };
+
Node();
Node(const Node &other);
Node(Node &&other) noexcept;
@@ -45,7 +51,7 @@ private:
void split(QString::iterator it, QString::iterator end);
void insert(const QString &path, int offset);
void remove(const QString &path, int offset);
- int containedPrefixLeaf(const QString &path, int offset) const;
+ PrefixResult findPrefix(const QString &path, int offset) const;
private:
Node(const QString &mine, const QHash<QChar, Node *> &next = QHash<QChar, Node *>(),
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 49829d8e61..91229d8d1f 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -2918,14 +2918,14 @@ but this file does not exist. Possible reasons include:
endif()
_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
- set(import_scanner_args ${tool_wrapper} ${tool_path} ${cmd_args})
+ set(import_scanner_args "${tool_path}" ${cmd_args})
# Run qmlimportscanner to generate the cmake file that records the import entries
if(scan_at_build_time)
add_custom_command(
OUTPUT "${imports_file}"
COMMENT "Running qmlimportscanner for ${target}"
- COMMAND ${import_scanner_args}
+ COMMAND ${tool_wrapper} ${import_scanner_args}
WORKING_DIRECTORY ${target_source_dir}
DEPENDS
${tool_path}
@@ -2940,11 +2940,14 @@ but this file does not exist. Possible reasons include:
message(VERBOSE "Running qmlimportscanner for ${target}.")
list(JOIN import_scanner_args " " import_scanner_args_string)
message(DEBUG "qmlimportscanner command: ${import_scanner_args_string}")
- execute_process(
+
+ # Pack arguments to avoid escaping issues.
+ set(import_scanner_execute_process_args
COMMAND ${import_scanner_args}
- WORKING_DIRECTORY ${target_source_dir}
+ WORKING_DIRECTORY "${target_source_dir}"
RESULT_VARIABLE result
)
+ _qt_internal_execute_proccess_in_qt_env(import_scanner_execute_process_args)
if(result)
message(FATAL_ERROR
"Failed to scan target ${target} for QML imports: ${result}"
diff --git a/src/qml/common/qqmltranslation.cpp b/src/qml/common/qqmltranslation.cpp
index 7120071b1a..639cf9b559 100644
--- a/src/qml/common/qqmltranslation.cpp
+++ b/src/qml/common/qqmltranslation.cpp
@@ -3,6 +3,8 @@
#include "private/qqmltranslation_p.h"
+QT_BEGIN_NAMESPACE
+
QQmlTranslation::QQmlTranslation(const Data &d) : data(d) { }
QQmlTranslation::QQmlTranslation() : data(nullptr) { }
@@ -123,3 +125,5 @@ QString QQmlTranslation::QsTrIdData::idForQmlDebug() const
{
return QString::fromUtf8(id);
}
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 8dffb52960..c2f481f297 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -1554,7 +1554,7 @@ public:
if (index < data->stringTableSize)
return data->stringAtInternal(index);
- const uint dynamicIndex = index - data->stringTableSize;
+ const qsizetype dynamicIndex = index - data->stringTableSize;
Q_ASSERT(dynamicIndex < dynamicStrings.size());
return dynamicStrings.at(dynamicIndex);
}
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index bf36c790f1..655f3835cb 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -758,6 +758,15 @@ void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::Argument
}
args = args->next;
+ // QT_TR_NOOP can have a disambiguation string, QT_TRID_NOOP can't
+ if (args && base == QLatin1String("QT_TR_NOOP")) {
+ // we have a disambiguation string; we don't need to do anything with it
+ if (QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression))
+ args = args->next;
+ else // second argument is not a string, stop
+ return;
+ }
+
if (args)
return; // too many arguments, stop
@@ -780,6 +789,14 @@ void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::Argument
}
args = args->next;
+ if (args) {
+ // we have a disambiguation string; we don't need to do anything with it
+ if (QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression))
+ args = args->next;
+ else // third argument is not a string, stop
+ return;
+ }
+
if (args)
return; // too many arguments, stop
diff --git a/src/qml/doc/src/cmake/policy/qtp0001.qdoc b/src/qml/doc/src/cmake/policy/qtp0001.qdoc
index 93e829b99e..64d7b8abc1 100644
--- a/src/qml/doc/src/cmake/policy/qtp0001.qdoc
+++ b/src/qml/doc/src/cmake/policy/qtp0001.qdoc
@@ -19,10 +19,10 @@ a default \l {QML Import Path}{import path}, and its types can be
found without manual calls to \l QQmlEngine::addImportPath.
The \c OLD behavior of this policy is that, the \c RESOURCE_PREFIX argument for
-\c{qt_add_qml_module()} defaults to \c{":/"}.
+\c{qt_add_qml_module()} defaults to \c{"/"}.
The \c NEW behavior of this policy is that the \c RESOURCE_PREFIX argument
-for \c{qt_add_qml_module()} defaults to \c{":/qt/qml/"}. The new behavior
+for \c{qt_add_qml_module()} defaults to \c{"/qt/qml/"}. The new behavior
ensures that modules are put into the \l{QML Import Path} and can be
found without further setup.
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index 353505c78f..7452af10dc 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -447,6 +447,10 @@ qt_add_qml_module(someTarget
)
\endcode
+If \l QTP0001 is enabled (e.g. via
+\c {qt_standard_project_setup(REQUIRES 6.5)}), the default value is
+\c "/qt/qml/", otherwise it is \c {"/"}.
+
\target NO_RESOURCE_TARGET_PATH
When various files are added to the compiled-in resources, they are placed
under a path formed by concatenating the \c RESOURCE_PREFIX and the target path.
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index e2d69eb414..cba6a122b9 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -91,9 +91,6 @@ when passed from C++ to QML and vice-versa:
\row
\li QVector2D, QVector3D, QVector4D
\li \l vector2d, \l vector3d, \l vector4d
- \row
- \li Enums declared with Q_ENUM()
- \li \l enumeration
\endtable
\note Classes provided by the \l {Qt GUI} module, such as QColor, QFont,
@@ -142,16 +139,23 @@ additional features. See the \l {qtqml-javascript-hostenvironment.html}
{JavaScript Host Environment} for further details.)
-\section2 QVariantList and QVariantMap to JavaScript Array and Object
+\section2 QVariantList and QVariantMap to JavaScript Array-like and Object
The QML engine provides automatic type conversion between QVariantList and
-JavaScript arrays, and between QVariantMap and JavaScript objects.
+JavaScript array-likes, and between QVariantMap and JavaScript objects.
+
+An \e{array-like}, in ECMAScript terms, is an object used like an array. The
+array-likes produced by conversion from QVariantList (and other C++ sequential
+containers) are not only that but also offer the common array methods
+and automatically synchronize the \e length property. They can be used just
+like JavaScript Arrays for any practical purposes. The \e{Array.isArray()}
+method still returns false for them, though.
For example, the function defined in QML below expects two arguments, an
array and an object, and prints their contents using the standard JavaScript
syntax for array and object item access. The C++ code below calls this
function, passing a QVariantList and a QVariantMap, which are automatically
-converted to JavaScript array and object values, repectively:
+converted to JavaScript array-like and object values, respectively:
\table
\row
@@ -177,23 +181,24 @@ type or method parameter, the value can be created as a JavaScript array or
object in QML, and is automatically converted to a QVariantList or QVariantMap
when it is passed to C++.
-Mind that QVariantList and QVariantMap properties of C++ types are stored as
-values and cannot be changed in place by QML code. You can only replace the
-whole map or list, but not manipulate its contents. The following code does
-not work if the property \c l is a QVariantList:
+Since Qt 6.5, QVariantList properties of C++ types can be changed in place by
+QML code. Mind, however, that this does not hold for QVariantMap properties of
+C++ types. Those are stored as values and cannot be changed in place by QML
+code. You can only replace the whole map, but not manipulate its contents. The
+following code does not work if the property \c m is a QVariantMap:
\code
-MyListExposingItem {
- l: [1, 2, 3]
- Component.onCompleted: l[0] = 10
+MyMapExposingItem {
+ m: ({ one: 1 })
+ Component.onCompleted: m.ten = 10
}
\endcode
The following code does work:
\code
-MyListExposingItem {
- l: [1, 2, 3]
- Component.onCompleted: l = [10, 2, 3]
+MyMapExposingItem {
+ m: ({ one: 1 })
+ Component.onCompleted: m = { one: 1, ten: 10 }
}
\endcode
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index 1e31dc5e90..ef0f831299 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -394,17 +394,17 @@ types without breaking existing programs.
The REVISION tag is used to mark the \c root property as added in revision 1
of the type. Methods such as Q_INVOKABLE's, signals and slots can also be
-tagged for a revision using the \c Q_REVISION(x) macro:
+tagged for a revision using the \l Q_REVISION macro:
\code
class CppType : public BaseType
{
Q_OBJECT
- Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1)
+ Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0))
QML_ELEMENT
signals:
- Q_REVISION(1) void rootChanged();
+ Q_REVISION(1, 0) void rootChanged();
};
\endcode
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index fa317bdbd9..5d9c7a0b87 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -404,10 +404,10 @@
*/
/*!
- \macro QML_EXTENDED_NAMESPACE(EXTENDED_NAMESPACE)
+ \macro QML_EXTENDED_NAMESPACE(EXTENSION_NAMESPACE)
\relates QQmlEngine
- Declares that the enclosing type uses \a EXTENDED_NAMESPACE as an extension to
+ Declares that the enclosing \b type uses \a EXTENSION_NAMESPACE as an extension to
provide further enumerations in QML. This takes effect if the type
is exposed to QML using a \l QML_ELEMENT or \l QML_NAMED_ELEMENT() macro.
The enumerations need to be exposed to the metaobject system for this to work.
@@ -435,31 +435,85 @@
}
\endqml
- \note EXTENDED_NAMESPACE can also be a QObject or QGadget; in that case - and in contrast to
+ \note \a EXTENSION_NAMESPACE can also be a QObject or QGadget; in that case - and in contrast to
QML_EXTENDED, which also exposes methods and properties - only its enumerations
are exposed.
- \note \a EXTENDED_NAMESPACE must have a metaobject; i.e. it must either be a namespace which
+ \note \a EXTENSION_NAMESPACE must have a metaobject; i.e. it must either be a namespace which
contains the Q_NAMESPACE macro or a QObject/QGadget.
\include {qualified-class-name.qdocinc} {class name must be qualified}
- \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED(),
+ \sa QML_NAMESPACE_EXTENDED(), QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED(),
{Registering Extension Objects}, Q_ENUM, Q_ENUM_NS
*/
/*!
+ \macro QML_NAMESPACE_EXTENDED(EXTENSION_NAMESPACE)
+ \relates QQmlEngine
+
+ Behaves the same way as \l QML_EXTENDED_NAMESPACE with the distinction that what is being
+ extended is a namespace and not a type.
+
+ Declares that the enclosing \b namespace uses \a EXTENSION_NAMESPACE as an extension to
+ provide further enumerations in QML. This takes effect if the extended namespace is exposed to
+ QML using a \l QML_ELEMENT or \l QML_NAMED_ELEMENT() macro. The enumerations need to be exposed
+ to the metaobject system for this to work.
+
+ For example, in the following C++ code,
+ \code
+ namespace NS2 {
+ Q_NAMESPACE
+
+ enum class E2 { D = 3, E, F };
+ Q_ENUM_NS(E2)
+ }
+
+ namespace NS1 {
+ Q_NAMESPACE
+ QML_ELEMENT
+
+ enum class E1 { A, B, C };
+ Q_ENUM_NS(E1)
+
+ // Extends NS1 with NS2
+ QML_NAMESPACE_EXTENDED(NS2)
+ }
+ \endcode
+
+ the namespace \c NS1 is extended with \c NS2 and the \c E2 enum becomes available within \c NS1
+ from QML.
+ \qml
+ Item {
+ Component.onCompleted: console.log(NS1.E1.A, NS1.E2.D)
+ }
+ \endqml
+
+ \note \a EXTENSION_NAMESPACE can also be a QObject or QGadget; in that case - and in contrast to
+ QML_EXTENDED, which also exposes methods and properties - only its enumerations
+ are exposed.
+
+ \note \a EXTENSION_NAMESPACE must have a metaobject; i.e. it must either be a namespace which
+ contains the Q_NAMESPACE macro or a QObject/QGadget.
+
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
+ \sa QML_EXTENDED_NAMESPACE(), QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED(),
+ {Registering Extension Objects}, Q_ENUM, Q_ENUM_NS
+*/
+
+/*!
\macro QML_FOREIGN(FOREIGN_TYPE)
\relates QQmlEngine
Declares that any \l QML_ELEMENT, \l QML_NAMED_ELEMENT(), \l QML_ANONYMOUS,
\l QML_INTERFACE, \l QML_UNCREATABLE(), \l QML_SINGLETON,
\l QML_ADDED_IN_MINOR_VERSION(), \l QML_REMOVED_IN_MINOR_VERSION(),
- \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE() macros
- in the enclosing C++ type do not apply to the enclosing type but instead to
- \a FOREIGN_TYPE. The enclosing type still needs to be registered with the
- \l {The Meta-Object System}{meta object system} using a \l Q_GADGET or
- \l Q_OBJECT macro.
+ \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE(), or \l QML_NAMESPACE_EXTENDED()
+ macros in the enclosing C++ type do not apply to the enclosing type but
+ instead to \a FOREIGN_TYPE. The enclosing type still needs to be registered
+ with the \l {The Meta-Object System}{meta object system} using a \l Q_GADGET
+ or \l Q_OBJECT macro.
This is useful for registering types that cannot be amended to add the macros,
for example because they belong to 3rdparty libraries.
diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
index 83dbfaa6e7..08e211415b 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
@@ -127,8 +127,8 @@ Rectangle {
\section4 Valid Types in Custom Property Definitions
-Any of the \l {QML Value Types} aside from the \l enumeration type can be used
-as custom property types. For example, these are all valid property declarations:
+Any of the \l {QML Value Types} can be used as custom property types. For
+example, these are all valid property declarations:
\qml
Item {
@@ -1124,7 +1124,8 @@ Text {
}
\endqml
-More information on enumeration usage in QML can be found in the \l {QML Value Types} \l enumeration documentation.
+More information on enumeration usage in QML can be found in the documentation on
+\l {QML Enumerations}.
The ability to declare enumerations in QML was introduced in Qt 5.10.
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/enumerations.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/enumerations.qdoc
new file mode 100644
index 0000000000..0dbc96a5a5
--- /dev/null
+++ b/src/qml/doc/src/qmllanguageref/typesystem/enumerations.qdoc
@@ -0,0 +1,74 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtqml-typesystem-enumerations.html
+\title QML Enumerations
+\brief Description of QML Enumerations
+
+Enumerations used in QML can be defined either in QML or in C++.
+
+For information on defining enumerations in QML, see
+\l{QML Object Attributes#enumeration-attributes}{Enumeration Attributes}.
+
+When defined in C++, enumerations exposed to QML must be marked with the
+\l{Q_ENUM} or \l{Q_ENUM_NS} macros and be part of a type exposed to QML via the
+\l{QML_NAMED_ELEMENT} or \l{QML_ELEMENT} macros. For more details, see
+\l{Data Type Conversion Between QML and C++#enumeration-types}{Enumeration Types}.
+
+Only named QML types can hold enumerations usable in QML. Each enumeration must
+have a surrounding named type. \l{QML Namespaces} are also QML types and can hold
+enums.
+
+As a result, an enumeration value can be referred to as \c {<Type>.<Value>}. For
+example, the \l Text type has an \c AlignRight enumeration value:
+
+\qml
+Text { horizontalAlignment: Text.AlignRight }
+\endqml
+
+Enumeration values are properties of the type reference produced by the type
+name. You can also retrieve them using JavaScript’s square bracket syntax,
+though this is error-prone and not recommended:
+
+\qml
+// Avoid this if possible
+Text { horizontalAlignment: Text["AlignRight"] }
+\endqml
+
+\section1 Using Enumerations in QML
+
+Enumerations are not separate types in QML but are properties of their
+surrounding types. An enumeration value is represented as the underlying type of
+the enumeration. For most enumerations, it is safe to use JavaScript's
+\e Number type or QML's \e double type to store them. However, this does not
+always work for 64-bit integers, as their range exceeds the safe integer range
+of 64-bit doubles. Therefore, enumerations with values outside the safe integer
+range (-(2^53 - 1) to 2^53 - 1, inclusive) cannot be safely used in QML. For
+enumerations with values that fit into the numeric range of a 32-bit signed
+integer, you can safely use the QML \e int type as storage.
+
+For example:
+
+\qml
+import QtQuick
+
+Item {
+ // refer to Text.AlignRight using an int type
+ property int enumValue: textItem.horizontalAlignment
+
+ signal valueEmitted(int someValue)
+
+ Text {
+ id: textItem
+ horizontalAlignment: Text.AlignRight
+ }
+
+ // emit valueEmitted() signal, which expects an int, with Text.AlignRight
+ Component.onCompleted: valueEmitted(Text.AlignRight)
+}
+\endqml
+
+\sa {QML Value Types}
+\sa {QML Object Attributes#enumeration-attributes}{Enumeration Attributes}
+\sa {Data Type Conversion Between QML and C++#enumeration-types}{Enumeration Types}
+*/
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
index 8999697eb1..1dc7122dbb 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
@@ -42,7 +42,6 @@ document, with the following exceptions:
\list
\li \c void, which marks the absence of a value
\li \c list must be used in conjunction with an object or value type as element
- \li \c enumeration cannot be used directly as the enumeration must be defined by a registered QML object type
\endlist
\section2 Built-in Value Types Provided By The QML Language
@@ -539,60 +538,3 @@ property is only invoked when the property is reassigned to a different object v
\sa {QML Value Types}
*/
-
-/*!
- \qmlvaluetype enumeration
- \ingroup qmlvaluetypes
- \brief a named enumeration value.
-
- The \c enumeration type refers to a named enumeration value.
-
- Each named value can be referred to as \c {<Type>.<value>}. For
- example, the \l Text type has an \c AlignRight enumeration value:
-
- \qml
- Text { horizontalAlignment: Text.AlignRight }
- \endqml
-
- (For backwards compatibility, the enumeration value may also be
- specified as a string, e.g. "AlignRight". This form is not
- recommended for new code.)
-
- When integrating with C++, note that any \c enum value
- \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically
- converted into an \c enumeration value, and vice-versa.
-
- This value type is provided by the QML language. Some enumeration values
- are provided by the QtQuick import.
-
- \section1 Using the enumeration Type in QML
-
- The \c enumeration type is a representation of a C++ \c enum type. It is
- not possible to refer to the \c enumeration type in QML itself; instead, the
- \l int or \l var types can be used when referring to \c enumeration values
- from QML code.
-
- For example:
-
- \qml
- import QtQuick 2.0
-
- Item {
- // refer to Text.AlignRight using an int type
- property int enumValue: textItem.horizontalAlignment
-
- signal valueEmitted(int someValue)
-
- Text {
- id: textItem
- horizontalAlignment: Text.AlignRight
- }
-
- // emit valueEmitted() signal, which expects an int, with Text.AlignRight
- Component.onCompleted: valueEmitted(Text.AlignRight)
- }
- \endqml
-
- \sa {QML Value Types}
- \sa {qtqml-syntax-objectattributes.html#enumeration-attributes}{Enumeration Attributes}
-*/
diff --git a/src/qml/doc/src/qt6-changes.qdoc b/src/qml/doc/src/qt6-changes.qdoc
index 4f95103a4f..92f78c0e88 100644
--- a/src/qml/doc/src/qt6-changes.qdoc
+++ b/src/qml/doc/src/qt6-changes.qdoc
@@ -47,6 +47,10 @@
Qt.resolvedUrl can be used in both Qt 5 and 6.
+ As a porting aid, the \c{QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT} environment variable can be set
+ to 1 to obtain the Qt 5 behavior. This is possible since Qt 6.2.2. However, it is recommended
+ to only use this to unblock a port, and to use \c Qt.resolvedUrl as explained above.
+
\section2 Variant Properties
\c variant properties, which have been marked as obsolete since Qt 5, are now treated in exactly
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index f622c3fb23..8717462729 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -418,6 +418,17 @@ QJSValue::ErrorType QJSValue::errorType() const
Returns true if this QJSValue is an object of the Array class;
otherwise returns false.
+ \note This method is the equivalent of \e Array.isArray() in JavaScript. You
+ can use it to identify JavaScript arrays, but it will return \c false
+ for any array-like objects that are not JavaScript arrays. This includes
+ QML \e list objects for either value types or object types, JavaScript
+ typed arrays, JavaScript ArrayBuffer objects, and any custom array-like
+ objects you may create yourself. All of these \e behave like JavaScript
+ arrays, though: They generally expose the same methods and the
+ subscript operator can be used on them. Therefore, using this method to
+ determine whether an object could be used like an array is not
+ advisable.
+
\sa QJSEngine::newArray()
*/
bool QJSValue::isArray() const
@@ -1006,7 +1017,7 @@ bool QJSValue::equals(const QJSValue& other) const
\header \li Type \li Result
\row \li Undefined \li true
\row \li Null \li true
- \row \li Boolean \li true if both values are true, false otherwise
+ \row \li Boolean \li true if values are both true or both false, false otherwise
\row \li Number \li false if either value is NaN (Not-a-Number); true if values are equal, false otherwise
\row \li String \li true if both values are exactly the same sequence of characters, false otherwise
\row \li Object \li true if both values refer to the same object, false otherwise
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index eac2aca059..573f42956d 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -159,7 +159,7 @@ PropertyKey ForInIteratorObject::nextProperty() const
}
c = c->getPrototypeOf();
- d()->current.set(scope.engine, c->d());
+ d()->current.set(scope.engine, c ? c->d() : nullptr);
if (!c)
break;
delete d()->iterator;
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index ecd947ccc3..e19c57fd40 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -277,9 +277,13 @@ ReturnedValue QObjectWrapper::getProperty(
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (ep && ep->propertyCapture && !property->isConstant())
- if (!property->isBindable() || ep->propertyCapture->expression->mustCaptureBindableProperty())
- ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
+ if (ep && ep->propertyCapture && !property->isConstant()) {
+ if (!property->notifiesViaBindable()
+ || ep->propertyCapture->expression->mustCaptureBindableProperty()) {
+ ep->propertyCapture->captureProperty(
+ object, property->coreIndex(), property->notifyIndex());
+ }
+ }
if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
@@ -516,7 +520,7 @@ void QObjectWrapper::setProperty(
ScopedContext ctx(scope, f->scope());
// binding assignment.
- if (property->isBindable()) {
+ if (property->acceptsQBinding()) {
const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(object, idx);
QUntypedPropertyBinding binding;
@@ -527,6 +531,7 @@ void QObjectWrapper::setProperty(
} else {
binding = QQmlPropertyBinding::create(property, f->function(), object, callingQmlContext,
ctx, targetObject, targetIndex);
+
}
QUntypedBindable bindable;
void *argv = {&bindable};
@@ -1088,6 +1093,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
PersistentValue function;
PersistentValue thisObject;
QMetaMethod signal;
+ qsizetype maxNumArguments;
QObjectSlotDispatcher()
: QtPrivate::QSlotObjectBase(&impl)
@@ -1113,14 +1119,16 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
QQmlMetaObject::ArgTypeStorage<9> storage;
QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
- int argCount = storage.size();
+ const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
Scope scope(v4);
ScopedFunctionObject f(scope, This->function.value());
JSCallArguments jsCallData(scope, argCount);
- *jsCallData.thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
- for (int ii = 0; ii < argCount; ++ii) {
+ *jsCallData.thisObject = This->thisObject.isUndefined()
+ ? v4->globalObject->asReturnedValue()
+ : This->thisObject.value();
+ for (qsizetype ii = 0; ii < argCount; ++ii) {
QMetaType type = storage[ii];
if (type == QMetaType::fromType<QVariant>()) {
jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
@@ -1251,8 +1259,22 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
if (QObject *receiver = functionData.first) {
+ if (functionData.second == -1) {
+ slot->maxNumArguments = std::numeric_limits<qsizetype>::max();
+ } else {
+ // This means we are connecting to QObjectMethod which complains about extra arguments.
+ Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
+ const QMetaObject *metaObject = receiver->metaObject();
+ d->ensureMethodsCache(metaObject);
+ slot->maxNumArguments = std::accumulate(d->methods, d->methods + d->methodCount, 0,
+ [metaObject](int a, const QQmlPropertyData &b) {
+ return std::max(a, metaObject->method(b.coreIndex()).parameterCount());
+ });
+ }
+
QObjectPrivate::connect(signalObject, signalIndex, receiver, slot, Qt::AutoConnection);
} else {
+ slot->maxNumArguments = std::numeric_limits<qsizetype>::max();
qCInfo(lcObjectConnect,
"Could not find receiver of the connection, using sender as receiver. Disconnect "
"explicitly (or delete the sender) to make sure the connection is removed.");
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 2613cff99a..6454898b81 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -777,7 +777,14 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
nMatchOffsets += re->captureCount() * 2;
if (!regExp->global())
break;
- offset = qMax(offset, matchOffsets[oldSize + 1]) + 1;
+
+ const uint matchBegin = matchOffsets[oldSize];
+ const uint matchEnd = matchOffsets[oldSize + 1];
+
+ // If we have a zero-sized match, don't match at the same place again.
+ const uint matchOffset = (matchBegin == matchEnd) ? matchEnd + 1 : matchEnd;
+
+ offset = std::max(offset + 1, matchOffset);
}
if (regExp->global()) {
regExp->setLastIndex(0);
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 259334e12c..643d6c16a9 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -46,6 +46,11 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
return {staticValue._val};
}
+ static constexpr Value undefinded()
+ {
+ return fromStaticValue(Encode::undefined());
+ }
+
inline bool isString() const;
inline bool isStringOrSymbol() const;
inline bool isSymbol() const;
@@ -412,6 +417,9 @@ struct HeapValue : Value {
void set(EngineBase *e, HeapBasePtr b) {
WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue());
}
+ void set(EngineBase *e, ReturnedValue rv) {
+ WriteBarrier::write(e, base(), data_ptr(), rv);
+ }
};
template <size_t o>
diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h
index 66d2fc573d..5ff19dff89 100644
--- a/src/qml/qml/qqmlanybinding_p.h
+++ b/src/qml/qml/qqmlanybinding_p.h
@@ -71,19 +71,31 @@ public:
*/
static QQmlAnyBinding ofProperty(QObject *object, QQmlPropertyIndex index)
{
- QQmlAnyBinding binding;
+ const auto result = [](auto &&result) -> QQmlAnyBinding {
+ QQmlAnyBinding binding;
+ binding = std::forward<decltype(result)>(result);
+ return binding;
+ };
+
Q_ASSERT(object);
- auto coreIndex = index.coreIndex();
+ const auto coreIndex = index.coreIndex();
+
// we don't support bindable properties on value types so far
- if (!index.hasValueTypeIndex()
- && QQmlData::ensurePropertyCache(object)->property(coreIndex)->isBindable()) {
- auto metaProp = object->metaObject()->property(coreIndex);
- QUntypedBindable bindable = metaProp.bindable(object);
- binding = bindable.binding();
- } else {
- binding = QQmlPropertyPrivate::binding(object, index);
+ if (index.hasValueTypeIndex())
+ return result(QQmlPropertyPrivate::binding(object, index));
+
+ if (QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(object)) {
+ const QQmlPropertyData *property = propertyCache->property(coreIndex);
+ return (property->acceptsQBinding())
+ ? result(property->propertyBindable(object).binding())
+ : result(QQmlPropertyPrivate::binding(object, index));
}
- return binding;
+
+ const QMetaObject *metaObject = object->metaObject();
+ const QMetaProperty metaProp = metaObject->property(coreIndex);
+ return metaProp.isBindable()
+ ? result(metaProp.bindable(object).binding())
+ : result(QQmlPropertyPrivate::binding(object, index));
}
/*!
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 0251a72049..47326e9836 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -132,7 +132,7 @@ of their use.
\list
\li \l{Qt::createComponent()}{object Qt.createComponent(url)}
- \li \l{Qt::createQmlObject()}{object Qt.createQmlObject(string qml, object parent, string filepath)}
+ \li \l{Qt::createQmlObject()}{object Qt.createQmlObject(string qml, object parent, url url)}
\endlist
@@ -1286,12 +1286,18 @@ void QtObject::exit(int retCode) const
}
/*!
-\qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath)
+\qmlmethod object Qt::createQmlObject(string qml, object parent, url url)
-Returns a new object created from the given \a qml string which will have the specified \a parent,
-or \c null if there was an error in creating the object.
+Compiles the given \a qml string into a component and then returns a new object created from
+that component. The new object will have the specified \a parent. Returns \c null if there was
+an error in creating the component or the object.
-If \a filepath is specified, it will be used for error reporting for the created object.
+If \a url is specified, it will be used as URL of the component. This is useful for error
+reporting.
+
+\warning The new component will shadow any existing component of the same URL. You should not
+pass a URL of an existing component. In particular, by passing the URL of the surrounding QML
+file, you prevent access to the surrounding component from the new one.
Example (where \c parentItem is the id of an existing QML item):
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 7b2107b858..60e5dd4c9b 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -352,7 +352,7 @@ static void removePendingQPropertyBinding(
if (QQmlData *ddata = QQmlData::get(o)) {
const QQmlPropertyData *propData = ddata->propertyCache->property(
propertyName, o, ddata->outerContext);
- if (propData && propData->isBindable())
+ if (propData && propData->acceptsQBinding())
creator->removePendingBinding(o, propData->coreIndex());
return;
}
@@ -694,6 +694,9 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit
Sets the QQmlComponent to use the given QML \a data. If \a url
is provided, it is used to set the component name and to provide
a base path for items resolved by this component.
+
+ \warning The new component will shadow any existing component of
+ the same URL. You should not pass a URL of an existing component.
*/
void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
{
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index d7cf38984b..c4e646a55d 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -315,7 +315,7 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotif
const QMetaObject *metaObjectForBindable = nullptr;
if (auto const propCache = (ddata ? ddata->propertyCache.data() : nullptr)) {
Q_ASSERT(propCache->property(c));
- if (propCache->property(c)->isBindable())
+ if (propCache->property(c)->notifiesViaBindable())
metaObjectForBindable = propCache->metaObject();
} else {
const QMetaObject *m = o->metaObject();
@@ -340,7 +340,7 @@ void QQmlPropertyCapture::captureProperty(
Q_ASSERT(expression);
- if (propertyData->isBindable()) {
+ if (propertyData->notifiesViaBindable()) {
if (const QMetaObject *metaObjectForBindable = propertyCache->metaObject()) {
captureBindableProperty(o, metaObjectForBindable, propertyData->coreIndex());
return;
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 119053828f..0f92663268 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -918,7 +918,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !_valueTypeProperty;
if (allowedToRemoveBinding) {
- if (bindingProperty->isBindable()) {
+ if (bindingProperty->acceptsQBinding()) {
removePendingBinding(_bindingTarget, bindingProperty->coreIndex());
} else {
QQmlPropertyPrivate::removeBinding(
@@ -935,7 +935,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
_bindingTarget, signalIndex, context,
_scopeObject, runtimeFunction, currentQmlContext());
- if (bindingProperty->isBindable()) {
+ if (bindingProperty->notifiesViaBindable()) {
auto target = _bindingTarget;
if (bindingProperty->isAlias()) {
// If the property is an alias, we cannot obtain the bindable interface directly with qt_metacall
@@ -959,7 +959,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine);
bs->takeExpression(expr);
}
- } else if (bindingProperty->isBindable()) {
+ } else if (bindingProperty->acceptsQBinding()) {
QUntypedPropertyBinding qmlBinding;
if (binding->isTranslationBinding()) {
qmlBinding = QQmlTranslationPropertyBinding::create(bindingProperty, compilationUnit, binding);
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index e8d5b0c8cb..f09024d330 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -300,7 +300,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
for (auto idContext = context; idContext; idContext = idContext->parent()) {
const int objectId = idContext->propertyIndex(pathName.toString());
if (objectId != -1 && objectId < idContext->numIdValues()) {
- currentObject = context->idValue(objectId);
+ currentObject = idContext->idValue(objectId);
break;
}
}
@@ -711,7 +711,7 @@ bool QQmlProperty::isBindable() const
if (!d->object)
return false;
if (d->core.isValid())
- return d->core.isBindable();
+ return d->core.notifiesViaBindable();
return false;
}
@@ -1365,7 +1365,9 @@ struct BindingFixer
BindingFixer(QObject *object, const QQmlPropertyData &property,
QQmlPropertyData::WriteFlags flags)
{
- if (!property.isBindable() || !(flags & QQmlPropertyData::DontRemoveBinding))
+ // Even if QML cannot install bindings on this property, there may be a C++-created binding.
+ // If the property can notify via a bindable, there is a bindable that can hold a binding.
+ if (!property.notifiesViaBindable() || !(flags & QQmlPropertyData::DontRemoveBinding))
return;
QUntypedBindable bindable;
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 3f3f6540d5..c7cfde0b1b 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1013,7 +1013,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const
property.setReadable(true);
property.setWritable(data->isWritable());
property.setResettable(data->isResettable());
- property.setBindable(data->isBindable());
+ property.setBindable(data->notifiesViaBindable());
property.setAlias(data->isAlias());
}
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 375971f7fb..dafefc904b 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -758,7 +758,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
*type = QMetaType();
bool writable = false;
bool resettable = false;
- bool bindable = false;
+ bool notifiesViaBindable = false;
propertyFlags->setIsAlias(true);
@@ -845,7 +845,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
*type = resolveType(property->propType());
writable = property->isWritable();
resettable = property->isResettable();
- bindable = property->isBindable();
+ notifiesViaBindable = property->notifiesViaBindable();
if (property->isVarProperty())
propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
@@ -893,7 +893,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
resettable = writable && valueTypeMetaProperty.isResettable();
writable = writable && valueTypeMetaProperty.isWritable();
- bindable = valueTypeMetaProperty.isBindable();
+ // Do not update notifiesViaBindable. The core property counts for notifications.
+ propertyFlags->setIsDeepAlias(true);
}
}
}
@@ -901,7 +902,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
propertyFlags->setIsWritable(
writable && !alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly));
propertyFlags->setIsResettable(resettable);
- propertyFlags->setIsBindable(bindable);
+ propertyFlags->setIsBindable(notifiesViaBindable);
return QQmlError();
}
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index 5fe88327af..8eae7b6fbe 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -70,7 +70,7 @@ public:
// Lastly, isDirect and isOverridden apply to both functions and non-functions
private:
unsigned isConst : 1; // Property: has CONST flag/Method: is const
- unsigned isVMEFunction : 1; // Function was added by QML
+ unsigned isDeepAliasORisVMEFunction : 1; // Alias points into value type 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
@@ -111,6 +111,11 @@ public:
isAliasORisVMESignal = b;
}
+ void setIsDeepAlias(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isDeepAliasORisVMEFunction = b;
+ }
+
void setIsFinal(bool b) {
Q_ASSERT(type != FunctionType);
isFinalORisV4Function = b;
@@ -132,8 +137,9 @@ public:
void setIsVMEFunction(bool b) {
Q_ASSERT(type == FunctionType);
- isVMEFunction = b;
+ isDeepAliasORisVMEFunction = b;
}
+
void setHasArguments(bool b) {
Q_ASSERT(type == FunctionType);
isWritableORhasArguments = b;
@@ -204,7 +210,7 @@ public:
bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
- bool isVMEFunction() const { return isFunction() && m_flags.isVMEFunction; }
+ bool isVMEFunction() const { return isFunction() && m_flags.isDeepAliasORisVMEFunction; }
bool hasArguments() const { return isFunction() && m_flags.isWritableORhasArguments; }
bool isSignal() const { return isFunction() && m_flags.isResettableORisSignal; }
bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; }
@@ -214,7 +220,9 @@ public:
void setOverload(bool onoff) { m_flags.isOverload = onoff; }
bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; }
bool isConstructor() const { return isFunction() && m_flags.isConstructorORisBindable; }
- bool isBindable() const { return !isFunction() && m_flags.isConstructorORisBindable; }
+
+ bool notifiesViaBindable() const { return !isFunction() && m_flags.isConstructorORisBindable; }
+ bool acceptsQBinding() const { return notifiesViaBindable() && !m_flags.isDeepAliasORisVMEFunction; }
bool hasOverride() const { return overrideIndex() >= 0; }
bool hasRevision() const { return revision() != QTypeRevision::zero(); }
@@ -351,6 +359,17 @@ public:
return true;
}
+ QUntypedBindable propertyBindable(QObject *target) const
+ {
+ QUntypedBindable result;
+ void *argv[] = { &result };
+ if (hasStaticMetaCallFunction())
+ staticMetaCallFunction()(target, QMetaObject::BindableProperty, relativePropertyIndex(), argv);
+ else
+ doMetacall<QMetaObject::BindableProperty>(target, coreIndex(), argv);
+ return result;
+ }
+
static Flags defaultSignalFlags()
{
Flags f;
@@ -410,7 +429,7 @@ bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
QQmlPropertyData::Flags::Flags()
: otherBits(0)
, isConst(false)
- , isVMEFunction(false)
+ , isDeepAliasORisVMEFunction(false)
, isWritableORhasArguments(false)
, isResettableORisSignal(false)
, isAliasORisVMESignal(false)
@@ -427,7 +446,7 @@ QQmlPropertyData::Flags::Flags()
bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
{
return isConst == other.isConst &&
- isVMEFunction == other.isVMEFunction &&
+ isDeepAliasORisVMEFunction == other.isDeepAliasORisVMEFunction &&
isWritableORhasArguments == other.isWritableORhasArguments &&
isResettableORisSignal == other.isResettableORisSignal &&
isAliasORisVMESignal == other.isAliasORisVMESignal &&
diff --git a/src/qml/qml/qqmlpropertytopropertybinding.cpp b/src/qml/qml/qqmlpropertytopropertybinding.cpp
index f2edf3b87f..8ef99b0ddb 100644
--- a/src/qml/qml/qqmlpropertytopropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertytopropertybinding.cpp
@@ -104,7 +104,7 @@ void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags)
const QMetaProperty property = sourceMetaObject->property(m_sourcePropertyIndex);
if (!property.isConstant()) {
captureProperty(sourceMetaObject, QMetaObjectPrivate::signalIndex(property.notifySignal()),
- property.isBindable(), !vtd.isValid() && d->isBindable());
+ property.isBindable(), !vtd.isValid() && d->acceptsQBinding());
}
QQmlPropertyPrivate::writeValueProperty(
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index e011856807..6a1aab2d12 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -345,7 +345,8 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(
QV4::CompiledData::Binding::Flag flag
= QV4::CompiledData::Binding::IsSignalHandlerExpression;
- const bool isPropertyObserver = !signalPropertyData && qPropertyData && qPropertyData->isBindable();
+ const bool isPropertyObserver
+ = !signalPropertyData && qPropertyData && qPropertyData->notifiesViaBindable();
if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) {
int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
sigIndex = propertyCache->originalClone(sigIndex);
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 0c88f25690..d3cfeb3108 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -683,6 +683,11 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data)
void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
{
+ if (unit->qmlData->qmlUnit()->nObjects == 0) {
+ setError(QQmlTypeLoader::tr("Cached QML Unit has no objects"));
+ return;
+ }
+
m_document.reset(new QmlIR::Document(isDebugging()));
QQmlIRLoader loader(unit->qmlData, m_document.data());
loader.load();
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 72ad77b41c..68c1163c8c 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -502,6 +502,11 @@ void QQmlTypeLoader::Blob::importQmldirScripts(
for (const QQmlDirParser::Script &script : qmldirScripts) {
const QUrl scriptUrl = qmldirUrl.resolved(QUrl(script.fileName));
QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+
+ // Self-import via qmldir is OK-ish. We ignore it.
+ if (blob.data() == this)
+ continue;
+
addDependency(blob.data());
scriptImported(blob, import->location, script.nameSpace, import->qualifier);
}
diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h
index 6b3f9094f1..c401338afa 100644
--- a/src/qml/qml/qqmltypenamecache_p.h
+++ b/src/qml/qml/qqmltypenamecache_p.h
@@ -222,8 +222,7 @@ private:
template<typename Key>
Result typeSearch(const QVector<QQmlTypeModuleVersion> &modules, Key key) const
{
- QVector<QQmlTypeModuleVersion>::const_iterator end = modules.constEnd();
- for (QVector<QQmlTypeModuleVersion>::const_iterator it = modules.constBegin(); it != end; ++it) {
+ for (auto it = modules.crbegin(), end = modules.crend(); it != end; ++it) {
QQmlType type = it->type(key);
if (type.isValid())
return Result(type);
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 21a50b5561..94c2c254c0 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -1054,6 +1054,11 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
ctxt->engine(), pd->propType());
if (valueType) {
+
+ // For value type aliases, the core property provides the bindable.
+ if (c == QMetaObject::BindableProperty)
+ return QMetaObject::metacall(target, c, coreIndex, a);
+
removePendingBinding(target, coreIndex, encodedIndex);
valueType->read(target, coreIndex);
int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index e6a5691d3d..022d082546 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -347,23 +347,16 @@ bool QQmlDirParser::parse(const QString &source)
_linkTarget = sections[1];
} else if (sectionCount == 2) {
// No version specified (should only be used for relative qmldir files)
- const Component entry(sections[0], sections[1], QTypeRevision());
- _components.insert(entry.typeName, entry);
+ insertComponentOrScript(sections[0], sections[1], QTypeRevision());
} else if (sectionCount == 3) {
const QTypeRevision version = parseVersion(sections[1]);
if (version.isValid()) {
- const QString &fileName = sections[2];
-
- if (fileName.endsWith(QLatin1String(".js")) || fileName.endsWith(QLatin1String(".mjs"))) {
- // A 'js' extension indicates a namespaced script import
- const Script entry(sections[0], fileName, version);
- _scripts.append(entry);
- } else {
- const Component entry(sections[0], fileName, version);
- _components.insert(entry.typeName, entry);
- }
+ insertComponentOrScript(sections[0], sections[2], version);
} else {
- reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[1]));
+ reportError(
+ lineNumber, 0,
+ QStringLiteral("invalid version %1, expected <major>.<minor>")
+ .arg(sections[1]));
}
} else {
reportError(lineNumber, 0,
@@ -508,6 +501,16 @@ void QQmlDirParser::reportError(quint16 line, quint16 column, const QString &des
_errors.append(error);
}
+void QQmlDirParser::insertComponentOrScript(
+ const QString &name, const QString &fileName, QTypeRevision version)
+{
+ // A 'js' extension indicates a namespaced script import
+ if (fileName.endsWith(QLatin1String(".js")) || fileName.endsWith(QLatin1String(".mjs")))
+ _scripts.append(Script(name, fileName, version));
+ else
+ _components.insert(name, Component(name, fileName, version));
+}
+
void QQmlDirParser::setError(const QQmlJS::DiagnosticMessage &e)
{
_errors.clear();
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index 43409b7b41..07f783ab25 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -140,6 +140,8 @@ public:
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
void reportError(quint16 line, quint16 column, const QString &message);
+ void insertComponentOrScript(
+ const QString &name, const QString &fileName, QTypeRevision version);
private:
QList<QQmlJS::DiagnosticMessage> _errors;
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index b553f9b380..b35adfb66a 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -32,6 +32,8 @@ public:
enum RegisterShortcuts {
InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
+ This = QV4::CallData::This,
+ NewTarget = QV4::CallData::NewTarget,
FirstArgument = QV4::CallData::OffsetCount
};
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 653cc5581e..4bc215add7 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -183,12 +183,12 @@ bool QQmlJSImportVisitor::isTypeResolved(const QQmlJSScope::ConstPtr &type)
return isTypeResolved(type, handleUnresolvedType);
}
-static bool mayBeUnresolvedGeneralizedGroupedProperty(const QQmlJSScope::ConstPtr &scope)
+static bool mayBeUnresolvedGroupedProperty(const QQmlJSScope::ConstPtr &scope)
{
return scope->scopeType() == QQmlJSScope::GroupedPropertyScope && !scope->baseType();
}
-void QQmlJSImportVisitor::resolveAliasesAndIds()
+void QQmlJSImportVisitor::resolveAliases()
{
QQueue<QQmlJSScope::Ptr> objects;
objects.enqueue(m_exportedRootScope);
@@ -263,10 +263,15 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
newProperty.setIsWritable(targetProperty.isWritable());
newProperty.setIsPointer(targetProperty.isPointer());
- if (!typeScope.isNull()) {
- object->setPropertyLocallyRequired(
- newProperty.propertyName(),
- typeScope->isPropertyRequired(targetProperty.propertyName()));
+ const bool onlyId = !property.aliasExpression().contains(u'.');
+ if (onlyId) {
+ newProperty.setAliasTargetScope(type);
+ newProperty.setAliasTargetName(QStringLiteral("id-only-alias"));
+ } else {
+ const auto &ownerScope = QQmlJSScope::ownerOfProperty(
+ typeScope, targetProperty.propertyName()).scope;
+ newProperty.setAliasTargetScope(ownerScope);
+ newProperty.setAliasTargetName(targetProperty.propertyName());
}
if (const QString internalName = type->internalName(); !internalName.isEmpty())
@@ -274,23 +279,13 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
Q_ASSERT(newProperty.index() >= 0); // this property is already in object
object->addOwnProperty(newProperty);
+ m_aliasDefinitions.append({ object, property.propertyName() });
}
}
const auto childScopes = object->childScopes();
- for (const auto &childScope : childScopes) {
- if (mayBeUnresolvedGeneralizedGroupedProperty(childScope)) {
- const QString name = childScope->internalName();
- if (object->isNameDeferred(name)) {
- const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
- if (!deferred.isNull()) {
- QQmlJSScope::resolveGeneralizedGroup(
- childScope, deferred, m_rootScopeImports, &m_usedTypes);
- }
- }
- }
+ for (const auto &childScope : childScopes)
objects.enqueue(childScope);
- }
if (doRequeue)
requeue.enqueue(object);
@@ -314,6 +309,33 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
}
}
+void QQmlJSImportVisitor::resolveGroupProperties()
+{
+ QQueue<QQmlJSScope::Ptr> objects;
+ objects.enqueue(m_exportedRootScope);
+
+ while (!objects.isEmpty()) {
+ const QQmlJSScope::Ptr object = objects.dequeue();
+ const auto childScopes = object->childScopes();
+ for (const auto &childScope : childScopes) {
+ if (mayBeUnresolvedGroupedProperty(childScope)) {
+ const QString name = childScope->internalName();
+ if (object->isNameDeferred(name)) {
+ const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
+ if (!deferred.isNull()) {
+ QQmlJSScope::resolveGroup(
+ childScope, deferred, m_rootScopeImports, &m_usedTypes);
+ }
+ } else if (const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
+ QQmlJSScope::resolveGroup(
+ childScope, propType, m_rootScopeImports, &m_usedTypes);
+ }
+ }
+ objects.enqueue(childScope);
+ }
+ }
+}
+
QString QQmlJSImportVisitor::implicitImportDirectory(
const QString &localFile, QQmlJSResourceFileMapper *mapper)
{
@@ -407,7 +429,8 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
checkDeprecation(scope);
}
- resolveAliasesAndIds();
+ resolveAliases();
+ resolveGroupProperties();
for (const auto &scope : m_objectDefinitionScopes)
checkGroupedAndAttachedScopes(scope);
@@ -744,9 +767,30 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
}
}
+void QQmlJSImportVisitor::populatePropertyAliases()
+{
+ for (const auto &alias : std::as_const(m_aliasDefinitions)) {
+ const auto &[aliasScope, aliasName] = alias;
+ if (aliasScope.isNull())
+ continue;
+
+ auto property = aliasScope->ownProperty(aliasName);
+ if (!property.isValid() || !property.aliasTargetScope())
+ continue;
+
+ Property target(property.aliasTargetScope(), property.aliasTargetName());
+
+ do {
+ m_propertyAliases[target].append(alias);
+ property = target.scope->property(target.name);
+ target = Property(property.aliasTargetScope(), property.aliasTargetName());
+ } while (property.isAlias());
+ }
+}
+
void QQmlJSImportVisitor::checkRequiredProperties()
{
- for (const auto &required : m_requiredProperties) {
+ for (const auto &required : std::as_const(m_requiredProperties)) {
if (!required.scope->hasProperty(required.name)) {
m_logger->log(
QStringLiteral("Property \"%1\" was marked as required but does not exist.")
@@ -755,95 +799,149 @@ void QQmlJSImportVisitor::checkRequiredProperties()
}
}
- for (const auto &defScope : m_objectDefinitionScopes) {
- if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent() || defScope->isComponentRootElement())
+ const auto isInComponent = [this](const QQmlJSScope::ConstPtr &requiredScope) {
+ const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
+ for (auto s = requiredScope; s; s = s->parentScope()) {
+ if (s->isWrappedInImplicitComponent() || s->baseType() == compType)
+ return true;
+ }
+ return false;
+ };
+
+ const auto requiredHasBinding = [](const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
+ const QString &propName) {
+ for (const auto &scope : scopesToSearch) {
+ const auto &[begin, end] = scope->ownPropertyBindings(propName);
+ for (auto it = begin; it != end; ++it) {
+ if (!scope->property(propName).isAlias())
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ const auto requiredUsedInRootAlias = [&](const QQmlJSScope::ConstPtr &defScope,
+ const QQmlJSScope::ConstPtr &requiredScope,
+ const QString &propName) {
+ if (defScope->filePath() == requiredScope->filePath()) {
+ QQmlJSScope::ConstPtr fileRootScope = requiredScope;
+ while (fileRootScope->parentScope() != m_globalScope)
+ fileRootScope = fileRootScope->parentScope();
+
+ const auto &rootProperties = fileRootScope->ownProperties();
+ for (const auto &p : rootProperties) {
+ if (p.isAlias() && p.aliasTargetScope() == requiredScope
+ && p.aliasTargetName() == propName) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ };
+
+ const auto requiredSetThroughAlias = [&](const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
+ const QQmlJSScope::ConstPtr &requiredScope,
+ const QString &propName) {
+ const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
+ const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
+ for (const auto &alias : propertyAliases) {
+ for (const auto &s : scopesToSearch) {
+ if (s->hasOwnPropertyBindings(alias.name))
+ return true;
+ }
+ }
+ return false;
+ };
+
+ const auto warn = [this](const QList<QQmlJSScope::ConstPtr> scopesToSearch,
+ QQmlJSScope::ConstPtr prevRequiredScope,
+ const QString &propName,
+ QQmlJSScope::ConstPtr defScope,
+ QQmlJSScope::ConstPtr requiredScope,
+ QQmlJSScope::ConstPtr descendant) {
+ const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.size() > 1
+ ? scopesToSearch.at(scopesToSearch.size() - 2)
+ : QQmlJSScope::ConstPtr();
+
+ const QString propertyScopeName = !propertyScope.isNull()
+ ? getScopeName(propertyScope, QQmlJSScope::QMLScope)
+ : u"here"_s;
+
+ std::optional<QQmlJSFixSuggestion> suggestion;
+
+ QString message = QStringLiteral("Component is missing required property %1 from %2")
+ .arg(propName)
+ .arg(propertyScopeName);
+ if (requiredScope != descendant) {
+ const QString requiredScopeName = prevRequiredScope
+ ? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
+ : u"here"_s;
+
+ if (!prevRequiredScope.isNull()) {
+ auto sourceScope = prevRequiredScope->baseType();
+ suggestion = QQmlJSFixSuggestion{
+ "%1:%2:%3: Property marked as required in %4."_L1
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation()
+ };
+ suggestion->setFilename(sourceScope->filePath());
+ } else {
+ message += " (marked as required by %1)"_L1.arg(requiredScopeName);
+ }
+ }
+
+ m_logger->log(message, qmlRequired, defScope->sourceLocation(), true, true, suggestion);
+ };
+
+ populatePropertyAliases();
+
+ for (const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
+ if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent()
+ || defScope->isComponentRootElement()) {
continue;
+ }
QVector<QQmlJSScope::ConstPtr> scopesToSearch;
for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
- scopesToSearch << scope;
- const auto ownProperties = scope->ownProperties();
- for (auto propertyIt = ownProperties.constBegin();
- propertyIt != ownProperties.constEnd(); ++propertyIt) {
- const QString propName = propertyIt.key();
-
- QQmlJSScope::ConstPtr prevRequiredScope;
- for (QQmlJSScope::ConstPtr requiredScope : scopesToSearch) {
- if (requiredScope->isPropertyLocallyRequired(propName)) {
- bool found =
- std::find_if(scopesToSearch.constBegin(), scopesToSearch.constEnd(),
- [&](QQmlJSScope::ConstPtr scope) {
- return scope->hasPropertyBindings(propName);
- })
- != scopesToSearch.constEnd();
-
- if (!found) {
- const QString scopeId = m_scopesById.id(defScope, scope);
- bool propertyUsedInRootAlias = false;
- if (!scopeId.isEmpty()) {
- for (const QQmlJSMetaProperty &property :
- m_exportedRootScope->ownProperties()) {
- if (!property.isAlias())
- continue;
-
- QStringList aliasExpression =
- property.aliasExpression().split(u'.');
-
- if (aliasExpression.size() != 2)
- continue;
- if (aliasExpression[0] == scopeId
- && aliasExpression[1] == propName) {
- propertyUsedInRootAlias = true;
- break;
- }
- }
- }
-
- if (propertyUsedInRootAlias)
- continue;
-
- const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.size() > 1
- ? scopesToSearch.at(scopesToSearch.size() - 2)
- : QQmlJSScope::ConstPtr();
-
- const QString propertyScopeName = !propertyScope.isNull()
- ? getScopeName(propertyScope, QQmlJSScope::QMLScope)
- : u"here"_s;
-
- const QString requiredScopeName = prevRequiredScope
- ? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
- : u"here"_s;
-
- std::optional<QQmlJSFixSuggestion> suggestion;
-
- QString message =
- QStringLiteral(
- "Component is missing required property %1 from %2")
- .arg(propName)
- .arg(propertyScopeName);
- if (requiredScope != scope) {
- if (!prevRequiredScope.isNull()) {
- auto sourceScope = prevRequiredScope->baseType();
- suggestion = QQmlJSFixSuggestion {
- "%1:%2:%3: Property marked as required in %4"_L1
- .arg(sourceScope->filePath())
- .arg(sourceScope->sourceLocation().startLine)
- .arg(sourceScope->sourceLocation().startColumn)
- .arg(requiredScopeName),
- sourceScope->sourceLocation()
- };
- suggestion->setFilename(sourceScope->filePath());
- } else {
- message += QStringLiteral(" (marked as required by %1)")
- .arg(requiredScopeName);
- }
- }
-
- m_logger->log(message, qmlRequired, defScope->sourceLocation(), true,
- true, suggestion);
+ const auto descendants = QList<QQmlJSScope::ConstPtr>() << scope << scope->descendantScopes();
+ for (QQmlJSScope::ConstPtr descendant : std::as_const(descendants)) {
+ if (descendant->scopeType() != QQmlJSScope::QMLScope)
+ continue;
+ scopesToSearch << descendant;
+ const auto ownProperties = descendant->ownProperties();
+ for (auto propertyIt = ownProperties.constBegin();
+ propertyIt != ownProperties.constEnd(); ++propertyIt) {
+ const QString propName = propertyIt.key();
+
+ QQmlJSScope::ConstPtr prevRequiredScope;
+ for (QQmlJSScope::ConstPtr requiredScope : std::as_const(scopesToSearch)) {
+ if (isInComponent(requiredScope))
+ continue;
+
+ if (!requiredScope->isPropertyLocallyRequired(propName)) {
+ prevRequiredScope = requiredScope;
+ continue;
}
+
+ if (requiredHasBinding(scopesToSearch, propName))
+ continue;
+
+ if (requiredUsedInRootAlias(defScope, requiredScope, propName))
+ continue;
+
+ if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
+ continue;
+
+ warn(scopesToSearch, prevRequiredScope, propName, defScope,
+ requiredScope, descendant);
+
+ prevRequiredScope = requiredScope;
}
- prevRequiredScope = requiredScope;
}
}
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 8565e47cf1..8e4a87c46d 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -178,25 +178,47 @@ protected:
// the content of QmlIR::Object::functionsAndExpressions
QHash<QQmlJSScope::ConstPtr, QList<QString>> m_functionsAndExpressions;
- struct FunctionOrExpressionIdentifier
+ template <bool scopeIsConst = true>
+ struct ScopeAndNameT
{
- QQmlJSScope::ConstPtr scope;
- QString name;
- friend bool operator==(const FunctionOrExpressionIdentifier &x,
- const FunctionOrExpressionIdentifier &y)
+ using Scope = std::conditional_t<scopeIsConst, QQmlJSScope::ConstPtr, QQmlJSScope::Ptr>;
+
+ ScopeAndNameT() = default;
+ ScopeAndNameT(const Scope &scope, const QString &name) : scope(scope), name(name) { }
+ ScopeAndNameT(const ScopeAndNameT &) = default;
+ ScopeAndNameT(ScopeAndNameT &&) = default;
+ ScopeAndNameT &operator=(const ScopeAndNameT &) = default;
+ ScopeAndNameT &operator=(ScopeAndNameT &&) = default;
+ ~ScopeAndNameT() = default;
+
+ // Create const from non-const
+ ScopeAndNameT(typename std::enable_if<scopeIsConst, ScopeAndNameT<false>>::type &nonConst)
+ : scope(nonConst.scope), name(nonConst.name)
+ {
+ }
+
+ friend bool operator==(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs)
{
- return x.scope == y.scope && x.name == y.name;
+ return lhs.scope == rhs.scope && lhs.name == rhs.name;
}
- friend bool operator!=(const FunctionOrExpressionIdentifier &x,
- const FunctionOrExpressionIdentifier &y)
+ friend bool operator!=(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs)
{
- return !(x == y);
+ return !(lhs == rhs);
}
- friend size_t qHash(const FunctionOrExpressionIdentifier &x, size_t seed = 0)
+ friend size_t qHash(const ScopeAndNameT &san, size_t seed = 0)
{
- return qHashMulti(seed, x.scope, x.name);
+ return qHashMulti(seed, san.scope, san.name);
}
+
+ Scope scope;
+ QString name;
};
+ using ConstScopeAndName = ScopeAndNameT<true>;
+ using ScopeAndName = ScopeAndNameT<false>;
+
+ using FunctionOrExpressionIdentifier = ConstScopeAndName;
+ using Property = ConstScopeAndName;
+ using Alias = ConstScopeAndName;
// tells whether last-processed UiScriptBinding is truly a script binding
bool m_thisScriptBindingIsJavaScript = false;
@@ -314,6 +336,8 @@ protected:
QVector<QQmlJSScope::Ptr> m_objectDefinitionScopes;
QHash<QQmlJSScope::Ptr, QVector<WithVisibilityScope<QString>>> m_propertyBindings;
+ QVector<Alias> m_aliasDefinitions;
+ QHash<Property, QList<Alias>> m_propertyAliases;
QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers;
QSet<QQmlJSScope::ConstPtr> m_literalScopesToCheck;
@@ -324,7 +348,9 @@ private:
const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
const QString &handlerName, const QStringList &handlerParameters);
void importBaseModules();
- void resolveAliasesAndIds();
+ void resolveAliases();
+ void populatePropertyAliases();
+ void resolveGroupProperties();
void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding);
void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 33e5c08e42..a5b08b4fa0 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -328,6 +328,8 @@ class QQmlJSMetaProperty
QString m_notify;
QString m_privateClass;
QString m_aliasExpr;
+ QString m_aliasTargetName;
+ QWeakPointer<const QQmlJSScope> m_aliasTargetScope;
QWeakPointer<const QQmlJSScope> m_type;
QVector<QQmlJSAnnotation> m_annotations;
bool m_isList = false;
@@ -385,6 +387,18 @@ public:
QString aliasExpression() const { return m_aliasExpr; }
bool isAlias() const { return !m_aliasExpr.isEmpty(); } // exists for convenience
+ void setAliasTargetName(const QString &name) { m_aliasTargetName = name; }
+ QString aliasTargetName() const { return m_aliasTargetName; }
+
+ void setAliasTargetScope(const QSharedPointer<const QQmlJSScope> &scope)
+ {
+ m_aliasTargetScope = scope;
+ }
+ QSharedPointer<const QQmlJSScope> aliasTargetScope() const
+ {
+ return m_aliasTargetScope.toStrongRef();
+ }
+
void setIsFinal(bool isFinal) { m_isFinal = isFinal; }
bool isFinal() const { return m_isFinal; }
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index fcb6fab857..5067210261 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -674,7 +674,7 @@ void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::C
self->m_listType = listType;
}
-void QQmlJSScope::resolveGeneralizedGroup(
+void QQmlJSScope::resolveGroup(
const Ptr &self, const ConstPtr &baseType,
const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
{
@@ -1200,6 +1200,29 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo
return RootDocumentNameType();
}
+QVector<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
+{
+ QVector<QQmlJSScope::ConstPtr> result;
+ result.reserve(m_childScopes.size());
+ for (const auto &child : m_childScopes)
+ result.append(child);
+ return result;
+}
+
+QVector<QQmlJSScope::ConstPtr> QQmlJSScope::descendantScopes() const
+{
+ QVector<QQmlJSScope::ConstPtr> descendants;
+ QVector<QQmlJSScope::ConstPtr> toVisit(m_childScopes.cbegin(), m_childScopes.cend());
+
+ while (!toVisit.isEmpty()) {
+ QQmlJSScope::ConstPtr scope = toVisit.takeLast();
+ descendants << scope;
+ toVisit << scope->childScopes();
+ }
+
+ return descendants;
+}
+
/*!
\internal
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 8dfbebbd06..ce7eeb5f6f 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -596,14 +596,8 @@ QT_WARNING_POP
return m_childScopes;
}
- QVector<QQmlJSScope::ConstPtr> childScopes() const
- {
- QVector<QQmlJSScope::ConstPtr> result;
- result.reserve(m_childScopes.size());
- for (const auto &child : m_childScopes)
- result.append(child);
- return result;
- }
+ QVector<QQmlJSScope::ConstPtr> childScopes() const;
+ QVector<QQmlJSScope::ConstPtr> descendantScopes() const;
static QTypeRevision resolveTypes(
const Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
@@ -615,7 +609,7 @@ QT_WARNING_POP
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType);
static void resolveList(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType);
- static void resolveGeneralizedGroup(
+ static void resolveGroup(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &baseType,
const QQmlJSScope::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index e938e622f7..06b49d067d 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -2403,6 +2403,11 @@ QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
if (regIt == m_state.registers.end()) {
if (isArgument(reg))
return argumentType(reg);
+ if (reg == This)
+ return m_typeResolver->globalType(m_function->qmlScope);
+ // over-approximation: needed in qmllint to not crash on `eval()`-calls
+ if (reg == NewTarget)
+ return m_typeResolver->globalType(m_typeResolver->varType());
setError(u"Type error: could not infer the type of an expression"_s);
return {};
diff --git a/src/qmldom/qqmldomelements.cpp b/src/qmldom/qqmldomelements.cpp
index 83d41d04bc..1b66542fba 100644
--- a/src/qmldom/qqmldomelements.cpp
+++ b/src/qmldom/qqmldomelements.cpp
@@ -1182,6 +1182,11 @@ std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
return nullptr;
}
+void Binding::setValue(std::unique_ptr<BindingValue> &&value)
+{
+ m_value = std::move(value);
+}
+
Path Binding::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
{
return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations),
diff --git a/src/qmldom/qqmldomelements_p.h b/src/qmldom/qqmldomelements_p.h
index 75ced47f5b..a6bf60334f 100644
--- a/src/qmldom/qqmldomelements_p.h
+++ b/src/qmldom/qqmldomelements_p.h
@@ -532,7 +532,7 @@ public:
std::shared_ptr<ScriptExpression> scriptExpressionValue();
QList<QmlObject> annotations() const { return m_annotations; }
void setAnnotations(QList<QmlObject> annotations) { m_annotations = annotations; }
- void setValue(std::unique_ptr<BindingValue> &&value) { m_value = std::move(value); }
+ void setValue(std::unique_ptr<BindingValue> &&value);
Path addAnnotation(Path selfPathFromOwner, const QmlObject &a, QmlObject **aPtr = nullptr);
const RegionComments &comments() const { return m_comments; }
RegionComments &comments() { return m_comments; }
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index 3c1670f09f..c88449a11f 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -1613,24 +1613,24 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, Q
// use the overload which does not care about changing m_moduleIndexWithUri to find a candidate
- auto [candidate, origin] = moduleIndexWithUriHelper(self, uri, majorVersion, options);
+ auto candidate = moduleIndexWithUriHelper(self, uri, majorVersion, options);
// A ModuleIndex from m_moduleIndexWithUri can always be returned
- if (candidate && origin == ModuleLookupResult::FromGlobal)
- return candidate;
+ if (candidate.module && candidate.fromBase == ModuleLookupResult::FromGlobal)
+ return std::move(candidate.module);
// If we don't want to modify anything, return the candidate that we have found (if any)
if (changeable == Changeable::ReadOnly)
- return candidate;
+ return std::move(candidate.module);
// Else we want to create a modifyable version
- std::shared_ptr<ModuleIndex> newModulePtr = [&, candidate = candidate](){
+ std::shared_ptr<ModuleIndex> newModulePtr = [&] {
// which is a completely new module in case we don't have candidate
- if (!candidate)
+ if (!candidate.module)
return std::make_shared<ModuleIndex>(uri, majorVersion);
// or a copy of the candidate otherwise
- DomItem existingModObj = self.copy(candidate);
- return candidate->makeCopy(existingModObj);
+ DomItem existingModObj = self.copy(candidate.module);
+ return candidate.module->makeCopy(existingModObj);
}();
DomItem newModule = self.copy(newModulePtr);
diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index 2ab8a1795b..7bca0b8f7e 100644
--- a/src/qmlmodels/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -3013,8 +3013,9 @@ bool QQmlListModelParser::definesEmptyList(const QString &s)
The names used for roles must begin with a lower-case letter and should be
common to all elements in a given model. Values must be simple constants; either
- strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
- (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
+ strings (quoted and optionally within a call to
+ \l [QML] {Qt::} {QT_TR_NOOP()}, boolean values (true, false), numbers, or
+ enumeration values (such as AlignText.AlignHCenter).
Beginning with Qt 5.11 ListElement also allows assigning a function declaration to
a role. This allows the definition of ListElements with callable actions.
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
index 2b0faa8f4d..84fa9022c3 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -318,9 +318,18 @@ void MetaTypesJsonProcessor::sortTypes(QVector<QJsonObject> &types)
QString MetaTypesJsonProcessor::resolvedInclude(const QString &include)
{
- return (m_privateIncludes && include.endsWith(QLatin1String("_p.h")))
- ? QLatin1String("private/") + include
- : include;
+ if (!m_privateIncludes)
+ return include;
+
+ if (include.endsWith(QLatin1String("_p.h")))
+ return QLatin1String("private/") + include;
+
+ if (include.startsWith(QLatin1String("qplatform"))
+ || include.startsWith(QLatin1String("qwindowsystem"))) {
+ return QLatin1String("qpa/") + include;
+ }
+
+ return include;
}
void MetaTypesJsonProcessor::processTypes(const QJsonObject &types)
diff --git a/src/quick/doc/snippets/pointerHandlers/wheelHandlerAcceptedModifiers.qml b/src/quick/doc/snippets/pointerHandlers/wheelHandlerAcceptedModifiers.qml
new file mode 100644
index 0000000000..1a8db7b636
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/wheelHandlerAcceptedModifiers.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![entire]
+import QtQuick
+
+Rectangle {
+ width: 170; height: 120
+ color: "green"; antialiasing: true
+
+ WheelHandler {
+ property: "rotation"
+ acceptedModifiers: Qt.ControlModifier
+ }
+
+ WheelHandler {
+ property: "scale"
+ acceptedModifiers: Qt.NoModifier
+ }
+}
+//![entire]
diff --git a/src/quick/doc/snippets/pointerHandlers/wheelHandlerMargin.qml b/src/quick/doc/snippets/pointerHandlers/wheelHandlerMargin.qml
new file mode 100644
index 0000000000..82a4fd744b
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/wheelHandlerMargin.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![entire]
+import QtQuick
+
+Rectangle {
+ width: 170; height: 120
+ color: "green"; antialiasing: true
+
+ WheelHandler {
+ property: "rotation"
+ margin: 10
+ }
+}
+//![entire]
diff --git a/src/quick/doc/src/concepts/input/focus.qdoc b/src/quick/doc/src/concepts/input/focus.qdoc
index 8c6f73024b..9f5a97101f 100644
--- a/src/quick/doc/src/concepts/input/focus.qdoc
+++ b/src/quick/doc/src/concepts/input/focus.qdoc
@@ -76,7 +76,7 @@ The MyWidget code:
\snippet qml/focus/MyWidget.qml mywidget
We want the first \c MyWidget object to have the focus, so we set its
-\c focus property to \c true. However, by running the code, we can confirm that
+\c focus property to \c true. However, by running the code, it can happen that
the second widget receives the focus.
\image declarative-qmlfocus2.png
@@ -85,9 +85,9 @@ Looking at both \c MyWidget and \c window code, the problem is evident - there
are three types that set the \c focus property to \c true. The two
\c {MyWidget}s set the \c focus to \c true and the \c window component also sets the
focus. Ultimately, only one type can have keyboard focus, and the system has
-to decide which type receives the focus. When the second \c MyWidget is created,
-it receives the focus because it is the last type to set its \c focus
-property to \c true.
+to decide which type receives the focus. Since QML does not guarantee which element
+will have its properties initialized first, it might be that the last \c MyWidget
+gets the initial focus.
This problem is due to visibility. The \c MyWidget component would like to have
the focus, but it cannot control the focus when it is imported or reused.
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 790a1ddc8c..1c9f7d8dc9 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -144,15 +144,15 @@ available when you import \c QtQuick.
The following properties are also available:
\list
- \li \l enumeration \c font.weight
+ \li \l {QML Enumerations}{enumeration} \c font.weight
\li \l bool \c font.overline
\li \l bool \c font.strikeout
- \li \l enumeration \c font.capitalization
+ \li \l {QML Enumerations}{enumeration} \c font.capitalization
\li \l real \c font.letterSpacing
\li \l real \c font.wordSpacing
\li \l bool \c font.kerning
\li \l bool \c font.preferShaping
- \li \l enumeration \c font.hintingPreference
+ \li \l {QML Enumerations}{enumeration} \c font.hintingPreference
\li \l string \c font.styleName
\endlist
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
index b7a42904c6..b051362e2b 100644
--- a/src/quick/handlers/qquickwheelhandler.cpp
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -517,8 +517,16 @@ QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
return metaProperty;
}
+/*! \internal
+ \qmlproperty flags QtQuick::WheelHandler::acceptedButtons
+
+ This overrides QtQuick::PointerDeviceHandler::acceptedButtons
+ and hides it from the documentation as the property is not relevant for
+ WheelHandler.
+*/
+
/*!
- \qmlproperty flags WheelHandler::acceptedDevices
+ \qmlproperty flags QtQuick::WheelHandler::acceptedDevices
The types of pointing devices that can activate this handler.
@@ -536,6 +544,108 @@ QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
\c acceptedDevices remains set to its default value.
*/
+/*!
+ \qmlproperty flags QtQuick::WheelHandler::acceptedModifiers
+
+ If this property is set, it will require the given keyboard modifiers to
+ be pressed in order to react to wheel events, and otherwise ignore them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ the WheelHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers, one of which is
+ enabled only if the required keyboard modifier is pressed, while the other
+ ignores events if any modifier is pressed:
+
+ \snippet pointerHandlers/wheelHandlerAcceptedModifiers.qml entire
+
+ The available modifiers are as follows:
+
+ \value NoModifier No modifier key is allowed.
+ \value ShiftModifier A Shift key on the keyboard must be pressed.
+ \value ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value AltModifier An Alt key on the keyboard must be pressed.
+ \value MetaModifier A Meta key on the keyboard must be pressed.
+ \value KeypadModifier A keypad button must be pressed.
+ \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument).
+ A Mode_switch key on the keyboard must be pressed.
+ \value KeyboardModifierMask The handler does not care which modifiers are pressed.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*! \internal
+ \qmlproperty flags QtQuick::WheelHandler::acceptedPointerTypes
+
+ This overrides QtQuick::PointerDeviceHandler::acceptedPointerTypes
+ and hides it from the documentation as the property is not relevant for
+ WheelHandler.
+*/
+
+/*!
+ \readonly
+ \qmlproperty bool QtQuick::WheelHandler::active
+
+ This holds \c true whenever the WheelHandler has recently seen a
+ QWheelEvent, is keeping its properties up-to-date, and actively manipulating
+ its \l target (if any).
+
+ \sa activeTimeout
+*/
+
+/*! \internal
+ \qmlproperty flags QtQuick::WheelHandler::cursorShape
+
+ This overrides QtQuick::PointerHandler::cursorShape
+ and hides it from the documentation as the property is not relevant for
+ WheelHandler.
+*/
+
+/*! \internal
+ \qmlproperty flags QtQuick::WheelHandler::dragThreshold
+
+ This overrides QtQuick::PointerHandler::dragThreshold
+ and hides it from the documentation as the property is not relevant for
+ WheelHandler.
+*/
+
+/*! \internal
+ \qmlproperty flags QtQuick::WheelHandler::grabPermissions
+
+ This overrides QtQuick::PointerHandler::grabPermissions
+ and hides it from the documentation as the property is not relevant for
+ WheelHandler.
+*/
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which the WheelHandler can react. For example if \c margin
+ is set to \c 10, you could place the cursor up to 10 pixels outside the
+ visible edge of the item, and it will still react to the wheel:
+
+ \snippet pointerHandlers/wheelHandlerMargin.qml entire
+
+ The default value is \c 0.
+*/
+
+/*! \internal
+ \qmlsignal QtQuick::WheelHandler::grabChanged(PointerDevice::GrabTransition transition, eventPoint point)
+
+ This overrides QtQuick::PointerHandler::grabChanged
+ and hides it from the documentation as the signal is not relevant for
+ WheelHandler.
+*/
+
+/*! \internal
+ \qmlsignal QtQuick::WheelHandler::canceled(eventPoint point)
+
+ This overrides QtQuick::PointerHandler::canceled
+ and hides it from the documentation as the signal is not relevant for
+ WheelHandler.
+*/
+
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 0ce3ef879b..362f37f509 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -3927,7 +3927,7 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radiu
// 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
// the variables computed above later on we keep this logic.
- if (qFuzzyCompare(std::abs(cos_phi), 1.0)) {
+ if (qFuzzyCompare(std::abs(cos_phi), qreal(1.0))) {
m_path.lineTo(p1);
return;
}
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 82587a178a..ec339ccc3b 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -6316,7 +6316,7 @@ qreal QQuickItem::opacity() const
void QQuickItem::setOpacity(qreal newOpacity)
{
Q_D(QQuickItem);
- qreal o = qBound<qreal>(0, newOpacity, 1);
+ qreal o = std::clamp(newOpacity, qreal(0.0), qreal(1.0));
if (d->opacity() == o)
return;
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 063c48260a..7c5ef85566 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -1704,18 +1704,23 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
break;
}
}
- FxViewItem *topItem = snapItemAt(tempPosition + snapOffset + highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
- // StrictlyEnforceRange always keeps an item in range
+
+ // If there are pending changes, the item returned from snapItemAt might get deleted as
+ // soon as applyPendingChanges() is called (from e.g. updateHighlight()).
+ // Therefore, apply the pending changes before we call snapItemAt()
+ if (strictHighlightRange)
updateHighlight();
- topItem = currentItem;
- }
+
+ FxViewItem *topItem = snapItemAt(tempPosition + snapOffset + highlightRangeStart);
FxViewItem *bottomItem = snapItemAt(tempPosition + snapOffset + highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
+ if (strictHighlightRange && currentItem) {
// StrictlyEnforceRange always keeps an item in range
- updateHighlight();
- bottomItem = currentItem;
+ if (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))
+ topItem = currentItem;
+ if (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))
+ bottomItem = currentItem;
}
+
qreal pos = 0;
bool isInBounds = -position() > maxExtent && -position() <= minExtent;
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index 824a0bf096..2563bec022 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -45,7 +45,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
/*!
\qmltype Palette
\instantiates QQuickPalette
- \inherits QQuickColorGroup
+ \inherits ColorGroup
\inqmlmodule QtQuick
\ingroup qtquick-visual
\brief Contains color groups for each QML item state.
diff --git a/src/quick/items/qquickselectable_p.h b/src/quick/items/qquickselectable_p.h
index 726352719f..03c6cca717 100644
--- a/src/quick/items/qquickselectable_p.h
+++ b/src/quick/items/qquickselectable_p.h
@@ -23,6 +23,8 @@ QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QQuickSelectable
{
public:
+ virtual ~QQuickSelectable();
+
virtual QQuickItem *selectionPointerHandlerTarget() const = 0;
virtual bool startSelection(const QPointF &pos) = 0;
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 1b564df125..3cc63cece0 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -1413,6 +1413,8 @@
QT_BEGIN_NAMESPACE
+QQuickSelectable::~QQuickSelectable() { }
+
Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_UNREACHABLE(output) { dumpTable(); qWarning() << "output:" << output; Q_UNREACHABLE(); }
@@ -4550,7 +4552,7 @@ void QQuickTableViewPrivate::positionViewAtRow(int row, Qt::Alignment alignment,
Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
Q_TABLEVIEW_ASSERT(verticalAlignment, alignment);
- if (syncHorizontally) {
+ if (syncVertically) {
syncView->d_func()->positionViewAtRow(row, verticalAlignment, offset, subRect);
} else {
if (!scrollToRow(row, verticalAlignment, offset, subRect)) {
@@ -4570,7 +4572,7 @@ void QQuickTableViewPrivate::positionViewAtColumn(int column, Qt::Alignment alig
Qt::Alignment horizontalAlignment = alignment & (Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight);
Q_TABLEVIEW_ASSERT(horizontalAlignment, alignment);
- if (syncVertically) {
+ if (syncHorizontally) {
syncView->d_func()->positionViewAtColumn(column, horizontalAlignment, offset, subRect);
} else {
if (!scrollToColumn(column, horizontalAlignment, offset, subRect)) {
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index c9385630d9..bf195a90b3 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -123,6 +123,10 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
// Objective is to update the dirty status and rects.
for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
auto node = *i;
+ // Track the original version of isDirty() as it can change if
+ // when we subtract dirty regions but still need to mark the previously
+ // dirty region as dirty.
+ const bool wasDirty = node->isDirty();
if (!m_dirtyRegion.isEmpty()) {
// See if the current dirty regions apply to the current node
node->addDirtyRegion(m_dirtyRegion, true);
@@ -156,7 +160,11 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
// if isAlpha, add node's dirty rect to m_dirtyRegion
m_dirtyRegion += node->dirtyRegion();
}
- // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion
+ }
+
+ if (wasDirty) {
+ // If this node started out dirty, make sure its previous region is
+ // added to the dirty region so that it gets cleared properly.
QRegion prevDirty = node->previousDirtyRegion();
if (!prevDirty.isNull())
m_dirtyRegion += prevDirty;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 3d5a6c7705..9249b5377f 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -21,10 +21,6 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
-#endif
-
int qt_sg_envInt(const char *name, int defaultValue);
namespace QSGBatchRenderer
@@ -3173,19 +3169,6 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
if (directUpdatePtr)
batch->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
-#ifndef QT_NO_DEBUG
- if (qsg_test_and_clear_material_failure()) {
- qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
- Element *ee = e;
- while (ee) {
- qDebug() << " -" << ee->node;
- ee = ee->nextInBatch;
- }
- QSGNodeDumper::dump(rootNode());
- qFatal("Aborting: scene graph is invalid...");
- }
-#endif
-
m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
m_gstate.lineWidth = g->lineWidth();
@@ -3381,16 +3364,6 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
QSGMaterialShader::RenderState renderState = state(QSGMaterialShader::RenderState::DirtyStates(int(dirty)));
updateMaterialDynamicData(sms, renderState, material, batch, e, ubufOffset, ubufSize, directUpdatePtr);
-#ifndef QT_NO_DEBUG
- if (qsg_test_and_clear_material_failure()) {
- qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
- qDebug() << " - offending node is" << e->node;
- QSGNodeDumper::dump(rootNode());
- qFatal("Aborting: scene graph is invalid...");
- return false;
- }
-#endif
-
ubufOffset += aligned(ubufSize, m_ubufAlignment);
const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 408cb0e1ff..bdb65c846c 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -6,21 +6,6 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DEBUG
-bool qsg_material_failure = false;
-bool qsg_test_and_clear_material_failure()
-{
- bool fail = qsg_material_failure;
- qsg_material_failure = false;
- return fail;
-}
-
-void qsg_set_material_failure()
-{
- qsg_material_failure = true;
-}
-#endif
-
/*!
\group qtquick-scenegraph-materials
\title Qt Quick Scene Graph Material Classes
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 8569f9d0fe..a0f7957752 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -7,6 +7,8 @@
#include "qsgnodeupdater_p.h"
#include "qsgmaterial.h"
+#include <algorithm>
+
#include "limits.h"
QT_BEGIN_NAMESPACE
@@ -1309,7 +1311,7 @@ const qreal OPACITY_THRESHOLD = 0.001;
void QSGOpacityNode::setOpacity(qreal opacity)
{
- opacity = qBound<qreal>(0, opacity, 1);
+ opacity = std::clamp(opacity, qreal(0.0), qreal(1.0));
if (m_opacity == opacity)
return;
DirtyState dirtyState = DirtyOpacity;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 7668db7a69..2e02713b93 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -142,7 +142,7 @@ public:
{
m_time = 0;
m_timer.start();
- m_wallTime.restart();
+ m_wallTime.start();
QAnimationDriver::start();
}
@@ -184,7 +184,7 @@ public:
if (m_lag > 10 && m_bad > 2) {
m_mode = TimerMode;
qCDebug(QSG_LOG_INFO, "animation driver switched to timer mode");
- m_wallTime.restart();
+ m_wallTime.start();
}
} else {
m_lag = 0;
@@ -265,7 +265,7 @@ public:
void start() override
{
- m_wallTime.restart();
+ m_wallTime.start();
QAnimationDriver::start();
}
diff --git a/src/quick/scenegraph/qsgrhilayer.cpp b/src/quick/scenegraph/qsgrhilayer.cpp
index 5299cb54ce..77ad687e61 100644
--- a/src/quick/scenegraph/qsgrhilayer.cpp
+++ b/src/quick/scenegraph/qsgrhilayer.cpp
@@ -115,7 +115,16 @@ void QSGRhiLayer::setSize(const QSize &pixelSize)
if (pixelSize == m_pixelSize)
return;
- m_pixelSize = pixelSize;
+ const int textureSizeMax = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ m_pixelSize = pixelSize.boundedTo(QSize(textureSizeMax, textureSizeMax));
+
+ if (Q_UNLIKELY(m_pixelSize != pixelSize)) {
+ qWarning("QSGRhiLayer: Unsupported size requested: [%d, %d]. "
+ "Maximum texture size: %d",
+ pixelSize.width(),
+ pixelSize.height(),
+ textureSizeMax);
+ }
if (m_live && m_pixelSize.isNull())
releaseResources();
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 1b33ae8f39..d2a160c420 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -234,8 +234,8 @@ public:
, stopEventProcessing(false)
{
sgrc = static_cast<QSGDefaultRenderContext *>(renderContext);
-#if (defined(Q_OS_QNX) && defined(Q_PROCESSOR_X86)) || defined(Q_OS_INTEGRITY)
- // The SDP 6.6.0 x86 MESA driver requires a larger stack than the default.
+#if defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY)
+ // The render thread requires a larger stack than the default (256k).
setStackSize(1024 * 1024);
#endif
}
diff --git a/src/quick/util/qquickforeignutils.cpp b/src/quick/util/qquickforeignutils.cpp
index 2dfe9fb5aa..34b3225abe 100644
--- a/src/quick/util/qquickforeignutils.cpp
+++ b/src/quick/util/qquickforeignutils.cpp
@@ -40,7 +40,7 @@ QT_BEGIN_NAMESPACE
\li \l point \c eventPoint.scenePosition: see also \l QEventPoint::scenePosition
\li \l ulong \c eventPoint.pressTimestamp: see also \l QEventPoint::pressTimestamp
\li \l point \c eventPoint.scenePressPosition: see also \l QEventPoint::scenePressPosition
- \li \l enumeration \c eventPoint.state: see also \l QEventPoint::state
+ \li \l {QML Enumerations}{enumeration} \c eventPoint.state: see also \l QEventPoint::state
\li \l real \c eventPoint.timeHeld: see also \l QEventPoint::timeHeld
\li \l ulong \c eventPoint.timestamp: see also \l QEventPoint::timestamp
\li \l pointingDeviceUniqueId \c eventPoint.uniqueId: see also \l QEventPoint::uniqueId
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 7562b74d46..17529317d5 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -1982,6 +1982,8 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
{
const QPointF &startPoint = path.currentPosition();
const QPointF &endPoint = positionForCurve(data, startPoint);
+ if (startPoint == endPoint)
+ return;
QQuickSvgParser::pathArc(path,
_radiusX,
_radiusY,
diff --git a/src/quickcontrols/ios/Dial.qml b/src/quickcontrols/ios/Dial.qml
index 41ce35caf2..303b791004 100644
--- a/src/quickcontrols/ios/Dial.qml
+++ b/src/quickcontrols/ios/Dial.qml
@@ -15,40 +15,41 @@ T.Dial {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
+ // The handle extends outside background bounds,
+ // so we need to include that in the insets calculation
leftInset: handle ? handle.width / 2 : 0
rightInset: handle ? handle.width / 2 : 0
topInset: handle ? handle.height / 2 : 0
bottomInset: handle ? handle.height / 2 : 0
background: Item {
- implicitWidth: 104
- implicitHeight: 104
- x: control.leftInset + (control.availableWidth - width) / 2
- y: control.topInset + (control.availableHeight - height) / 2
+ implicitWidth: _groove.implicitWidth
+ implicitHeight: _groove.implicitHeight
- Rectangle {
+ readonly property int _strokeWidth: 4
+
+ readonly property Rectangle _groove: Rectangle {
+ parent: control.background
x: (parent.width - width) / 2
y: (parent.height - height) / 2
- implicitWidth: parent.implicitWidth
- implicitHeight: parent.implicitHeight
- width: Math.max(50, Math.min(control.background.width, control.background.height))
+ implicitWidth: 104
+ implicitHeight: 104
+ width: Math.min(parent.width, parent.height)
height: width
color: "transparent"
border.color: control.palette.mid
- border.width: 4
+ border.width: control.background._strokeWidth
radius: width * 0.5
z: -1
-
- opacity: control.enabled? 1 : 0.5
+ opacity: control.enabled ? 1 : 0.5
}
- Shape {
- x: (parent.width - width) / 2
- y: (parent.height - height) / 2
- implicitWidth: parent.implicitWidth
- implicitHeight: parent.implicitHeight
- width: Math.max(50, Math.min(control.background.width, control.background.height))
- height: width
+ readonly property Shape _track: Shape {
+ parent: control.background
+ x: control.background._groove.x
+ y: control.background._groove.y
+ width: control.background._groove.width
+ height: control.background._groove.height
layer.enabled: true
layer.samples: 4
@@ -56,13 +57,12 @@ T.Dial {
fillColor: "transparent"
strokeColor: control.palette.button
strokeWidth: 4
-
capStyle: ShapePath.RoundCap
PathAngleArc {
- centerX: control.background.children[0].width / 2
- centerY: control.background.children[0].height / 2
- radiusX: control.background.children[0].width / 2 - 2
+ centerX: control.background._track.width / 2
+ centerY: control.background._track.height / 2
+ radiusX: (control.background._track.width - control.background._strokeWidth) / 2
radiusY: radiusX
startAngle: -230
sweepAngle: 140 + control.angle
@@ -74,13 +74,16 @@ T.Dial {
handle: Item {
height: dialHandle.height - dialHandle.topInset - dialHandle.bottomInset
width: dialHandle.width - dialHandle.rightInset - dialHandle.leftInset
- x: control.background.x + control.background.width / 2 - width / 2
- y: control.background.y + control.background.height / 2 - height / 2
+ x: control.background ? control.background.x + (control.background.width - width) / 2 : 0
+ y: control.background ? control.background.y + (control.background.height - height) / 2 : 0
transform: [
Translate {
- x: Math.cos((angle - 90) * Math.PI / 180) * Math.min(control.background.width, control.background.height) * 0.5;
- y: Math.sin((angle - 90) * Math.PI / 180) * Math.min(control.background.width, control.background.height) * 0.5;
+ readonly property real radius: !control.background ? 0
+ : (Math.min(control.background.width, control.background.height)
+ - control.background._strokeWidth) / 2
+ x: Math.cos((angle - 90) * Math.PI / 180) * radius
+ y: Math.sin((angle - 90) * Math.PI / 180) * radius
}
]
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
index ab07c9d9b2..53f8fe8618 100644
--- a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
@@ -308,7 +308,7 @@ void QQuickMaterialTextContainer::paint(QPainter *painter)
// Draw the focus line at the bottom for filled containers.
if (m_filled) {
- if (!qFuzzyCompare(m_focusAnimationProgress, 1.0)) {
+ if (!qFuzzyCompare(m_focusAnimationProgress, qreal(1.0))) {
// Draw the enabled active indicator line (#10) that's at the bottom when it's not focused:
// https://m3.material.io/components/text-fields/specs#6d654d1d-262e-4697-858c-9a75e8e7c81d
// Don't bother drawing it when the animation has finished, as the focused active indicator
diff --git a/src/quickcontrols/material/qquickmaterialstyle.cpp b/src/quickcontrols/material/qquickmaterialstyle.cpp
index f8ced5821b..ffa877b2b3 100644
--- a/src/quickcontrols/material/qquickmaterialstyle.cpp
+++ b/src/quickcontrols/material/qquickmaterialstyle.cpp
@@ -10,6 +10,8 @@
#include <QtQml/qqmlinfo.h>
#include <QtQuickControls2/private/qquickstyle_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
static const QRgb colors[][14] = {
@@ -1184,14 +1186,16 @@ QColor QQuickMaterialStyle::color(QQuickMaterialStyle::Color color, QQuickMateri
static QColor lighterShade(const QColor &color, qreal amount)
{
QColor hsl = color.toHsl();
- hsl.setHslF(hsl.hueF(), hsl.saturationF(), qBound<qreal>(0.0, hsl.lightnessF() + amount, 1.0), color.alphaF());
+ hsl.setHslF(hsl.hueF(), hsl.saturationF(),
+ std::clamp(hsl.lightnessF() + amount, qreal(0.0), qreal(1.0)), color.alphaF());
return hsl.convertTo(color.spec());
}
static QColor darkerShade(const QColor &color, qreal amount)
{
QColor hsl = color.toHsl();
- hsl.setHslF(hsl.hueF(), hsl.saturationF(), qBound<qreal>(0.0, hsl.lightnessF() - amount, 1.0), color.alphaF());
+ hsl.setHslF(hsl.hueF(), hsl.saturationF(),
+ std::clamp(hsl.lightnessF() - amount, qreal(0.0), qreal(1.0)), color.alphaF());
return hsl.convertTo(color.spec());
}
diff --git a/src/quickcontrolsimpl/qquickanimatednode.cpp b/src/quickcontrolsimpl/qquickanimatednode.cpp
index 542955235b..95d6ab0457 100644
--- a/src/quickcontrolsimpl/qquickanimatednode.cpp
+++ b/src/quickcontrolsimpl/qquickanimatednode.cpp
@@ -31,7 +31,7 @@ int QQuickAnimatedNode::currentTime() const
void QQuickAnimatedNode::setCurrentTime(int time)
{
m_currentTime = time;
- m_timer.restart();
+ m_timer.start();
}
int QQuickAnimatedNode::duration() const
@@ -71,7 +71,7 @@ void QQuickAnimatedNode::start(int duration)
m_running = true;
m_currentLoop = 0;
- m_timer.restart();
+ m_timer.start();
if (duration > 0)
m_duration = duration;
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
index 101870ec84..ffbf5da45f 100644
--- a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
@@ -157,6 +157,8 @@ QWindow *QQuickAbstractDialog::parentWindow() const
void QQuickAbstractDialog::setParentWindow(QWindow *window)
{
qCDebug(lcDialogs) << "set parent window to" << window;
+ m_parentWindowExplicitlySet = bool(window);
+
if (m_parentWindow == window)
return;
@@ -164,6 +166,17 @@ void QQuickAbstractDialog::setParentWindow(QWindow *window)
emit parentWindowChanged();
}
+void QQuickAbstractDialog::resetParentWindow()
+{
+ m_parentWindowExplicitlySet = false;
+
+ if (!m_parentWindow)
+ return;
+
+ m_parentWindow = nullptr;
+ emit parentWindowChanged();
+}
+
/*!
\qmlproperty string QtQuick.Dialogs::Dialog::title
@@ -295,7 +308,7 @@ void QQuickAbstractDialog::open()
onShow(m_handle.get());
- m_visible = m_handle->show(m_flags, m_modality, m_parentWindow);
+ m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
if (!m_visible && useNativeDialog()) {
// Fall back to non-native dialog
destroy();
@@ -303,7 +316,7 @@ void QQuickAbstractDialog::open()
return;
onShow(m_handle.get());
- m_visible = m_handle->show(m_flags, m_modality, m_parentWindow);
+ m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
if (m_visible) {
// The conditions that caused the non-native fallback might have
@@ -335,6 +348,8 @@ void QQuickAbstractDialog::close()
onHide(m_handle.get());
m_handle->hide();
m_visible = false;
+ if (!m_parentWindowExplicitlySet)
+ m_parentWindow = nullptr;
emit visibleChanged();
if (m_result == Accepted)
@@ -389,16 +404,22 @@ void QQuickAbstractDialog::componentComplete()
qCDebug(lcDialogs) << "componentComplete";
m_complete = true;
- if (!m_parentWindow) {
- qCDebug(lcDialogs) << "- no parent window; searching for one";
- setParentWindow(findParentWindow());
- }
+ if (!m_visibleRequested)
+ return;
+
+ m_visibleRequested = false;
- if (m_visibleRequested) {
- qCDebug(lcDialogs) << "visible was bound to true before component completion; opening dialog";
+ if (windowForOpen()) {
open();
- m_visibleRequested = false;
+ return;
}
+
+ // Since visible were set to true by the user, we want the dialog to be open by default.
+ // There is no guarantee that the dialog will work when it exists in a object tree that lacks a window,
+ // and since qml components are sometimes instantiated before they're given a window
+ // (which is the case when using QQuickView), we want to delay the call to open(), until the window is provided.
+ if (const auto parentItem = findParentItem())
+ connect(parentItem, &QQuickItem::windowChanged, this, &QQuickAbstractDialog::deferredOpen, Qt::SingleShotConnection);
}
static const char *qmlTypeName(const QObject *object)
@@ -486,21 +507,33 @@ void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog)
Q_UNUSED(dialog);
}
-QWindow *QQuickAbstractDialog::findParentWindow() const
+QQuickItem *QQuickAbstractDialog::findParentItem() const
{
QObject *obj = parent();
while (obj) {
- QWindow *window = qobject_cast<QWindow *>(obj);
- if (window)
- return window;
QQuickItem *item = qobject_cast<QQuickItem *>(obj);
- if (item && item->window())
- return item->window();
+ if (item)
+ return item;
obj = obj->parent();
}
return nullptr;
}
+QWindow *QQuickAbstractDialog::windowForOpen() const
+{
+ if (m_parentWindowExplicitlySet)
+ return m_parentWindow;
+ else if (auto parentItem = findParentItem())
+ return parentItem->window();
+ return m_parentWindow;
+}
+
+void QQuickAbstractDialog::deferredOpen(QWindow *window)
+{
+ m_parentWindow = window;
+ open();
+}
+
QT_END_NAMESPACE
#include "moc_qquickabstractdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
index e314eb27b4..9b00bc4a23 100644
--- a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
@@ -31,13 +31,14 @@ QT_BEGIN_NAMESPACE
class QWindow;
class QPlatformDialogHelper;
+class QQuickItem;
class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickAbstractDialog : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QQmlListProperty<QObject> data READ data FINAL)
- Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged FINAL)
+ Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged RESET resetParentWindow FINAL)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFlags NOTIFY flagsChanged FINAL)
Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged FINAL)
@@ -58,6 +59,7 @@ public:
QWindow *parentWindow() const;
void setParentWindow(QWindow *window);
+ void resetParentWindow();
QString title() const;
void setTitle(const QString &title);
@@ -107,12 +109,10 @@ protected:
virtual void onShow(QPlatformDialogHelper *dialog);
virtual void onHide(QPlatformDialogHelper *dialog);
- QWindow *findParentWindow() const;
+ QQuickItem *findParentItem() const;
+ QWindow *windowForOpen() const;
+ void deferredOpen(QWindow *window);
- bool m_visibleRequested = false;
- bool m_visible = false;
- bool m_complete = false;
- bool m_firstShow = true;
StandardCode m_result = Rejected;
QWindow *m_parentWindow = nullptr;
QString m_title;
@@ -121,6 +121,11 @@ protected:
QQuickDialogType m_type = QQuickDialogType::FileDialog;
QList<QObject *> m_data;
std::unique_ptr<QPlatformDialogHelper> m_handle;
+ bool m_visibleRequested = false;
+ bool m_visible = false;
+ bool m_complete = false;
+ bool m_parentWindowExplicitlySet = false;
+ bool m_firstShow = true;
};
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/util/FocusFrame.qml b/src/quicknativestyle/util/FocusFrame.qml
index eebee62c31..2a6ca12bed 100644
--- a/src/quicknativestyle/util/FocusFrame.qml
+++ b/src/quicknativestyle/util/FocusFrame.qml
@@ -5,24 +5,15 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-Item {
+Rectangle {
id: root
- // It's important that this item has a zero size. Otherwise, if the parent of the
- // targetItem is e.g a layout, we will change the layout if we parent this item inside it.
- width: 0
- height: 0
- // Stack on top of all siblings of the targetItem
- z: 100
-
function moveToItem(item, margins, radius) {
if (!item) {
targetItem = null;
parent = null;
- visible = false;
return;
}
- visible = true
parent = item.parent
targetItem = item
leftOffset = margins.left
@@ -45,20 +36,18 @@ Item {
// systemFrameColor is set to NSColor.keyboardFocusIndicatorColor from cpp
property color systemFrameColor
- Rectangle {
- id: focusFrame
- z: 10
- x: targetItem ? targetItem.x + leftOffset - frameSize - root.x : 0
- y: targetItem ? targetItem.y + topOffset - frameSize - root.y : 0
- width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0
- height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0
- radius: frameRadius
- visible: targetItem && targetItem.visible
- color: "transparent"
+ x: targetItem ? targetItem.x + leftOffset - frameSize : 0
+ y: targetItem ? targetItem.y + topOffset - frameSize : 0
+ // Stack on top of all siblings of the targetItem
+ z: 100
+ width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0
+ height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0
+ radius: frameRadius
+ visible: targetItem && targetItem.visible
+ color: "transparent"
- border.color: systemFrameColor
- border.width: frameSize
- }
+ border.color: systemFrameColor
+ border.width: frameSize
ParallelAnimation {
id: animation
@@ -71,7 +60,7 @@ Item {
easing.type: Easing.OutCubic
}
NumberAnimation {
- target: focusFrame
+ target: root
property: "opacity"
duration: 300
from: 0
diff --git a/src/quicknativestyle/util/qquickmacfocusframe.mm b/src/quicknativestyle/util/qquickmacfocusframe.mm
index 8ebaf223be..bd01895831 100644
--- a/src/quicknativestyle/util/qquickmacfocusframe.mm
+++ b/src/quicknativestyle/util/qquickmacfocusframe.mm
@@ -56,6 +56,7 @@ void QQuickMacFocusFrame::moveToItem(QQuickItem *item)
return;
createFocusFrame(context);
}
+ QQuickItemPrivate::get(m_focusFrame.get())->setTransparentForPositioner(true);
const QQuickFocusFrameDescription &config = getDescriptionForItem(item);
QMetaObject::invokeMethod(m_focusFrame.data(), "moveToItem",
diff --git a/src/quicktemplates/CMakeLists.txt b/src/quicktemplates/CMakeLists.txt
index 3c92a96fbd..2b2badd182 100644
--- a/src/quicktemplates/CMakeLists.txt
+++ b/src/quicktemplates/CMakeLists.txt
@@ -104,6 +104,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickswitchdelegate.cpp qquickswitchdelegate_p.h
qquicktabbar.cpp qquicktabbar_p.h
qquicktabbutton.cpp qquicktabbutton_p.h
+ qquicktemplatesutils.cpp
+ qquicktemplatesutils_p.h
qquicktextarea.cpp qquicktextarea_p.h
qquicktextarea_p_p.h
qquicktextfield.cpp qquicktextfield_p.h
diff --git a/src/quicktemplates/qquickabstractbutton.cpp b/src/quicktemplates/qquickabstractbutton.cpp
index 85028a1d84..61c13d749e 100644
--- a/src/quicktemplates/qquickabstractbutton.cpp
+++ b/src/quicktemplates/qquickabstractbutton.cpp
@@ -113,6 +113,8 @@ void QQuickAbstractButtonPrivate::setMovePoint(const QPointF &point)
bool QQuickAbstractButtonPrivate::handlePress(const QPointF &point, ulong timestamp)
{
Q_Q(QQuickAbstractButton);
+ if (pressed)
+ return true;
QQuickControlPrivate::handlePress(point, timestamp);
setPressPoint(point);
q->setPressed(true);
diff --git a/src/quicktemplates/qquickapplicationwindow.cpp b/src/quicktemplates/qquickapplicationwindow.cpp
index 34bebbf021..2708c5f3f8 100644
--- a/src/quicktemplates/qquickapplicationwindow.cpp
+++ b/src/quicktemplates/qquickapplicationwindow.cpp
@@ -5,6 +5,7 @@
#include "qquickcontentitem_p.h"
#include "qquickpopup_p_p.h"
#include "qquickcontrol_p_p.h"
+#include "qquicktemplatesutils_p.h"
#include "qquicktextarea_p.h"
#include "qquicktextfield_p.h"
#include "qquicktoolbar_p.h"
@@ -270,7 +271,7 @@ static QQuickItem *findActiveFocusControl(QQuickWindow *window)
{
QQuickItem *item = window->activeFocusItem();
while (item) {
- if (qobject_cast<QQuickControl *>(item) || qobject_cast<QQuickTextField *>(item) || qobject_cast<QQuickTextArea *>(item))
+ if (QQuickTemplatesUtils::isInteractiveControlType(item))
return item;
item = item->parentItem();
}
diff --git a/src/quicktemplates/qquickcontrol.cpp b/src/quicktemplates/qquickcontrol.cpp
index c1a3ca7be8..42cdcee3ed 100644
--- a/src/quicktemplates/qquickcontrol.cpp
+++ b/src/quicktemplates/qquickcontrol.cpp
@@ -8,6 +8,7 @@
#include <QtGui/qguiapplication.h>
#include "qquicklabel_p.h"
#include "qquicklabel_p_p.h"
+#include "qquicktemplatesutils_p.h"
#include "qquicktextarea_p.h"
#include "qquicktextarea_p_p.h"
#include "qquicktextfield_p.h"
@@ -722,12 +723,12 @@ bool QQuickControlPrivate::calcHoverEnabled(const QQuickItem *item)
if (qobject_cast<const QQuickPopupItem *>(p))
break;
- if (const QQuickControl *control = qobject_cast<const QQuickControl *>(p))
- return control->isHoverEnabled();
-
- QVariant v = p->property("hoverEnabled");
- if (v.isValid() && v.userType() == QMetaType::Bool)
- return v.toBool();
+ if (QQuickTemplatesUtils::isInteractiveControlType(p)) {
+ const QVariant hoverEnabledProperty = p->property("hoverEnabled");
+ Q_ASSERT(hoverEnabledProperty.isValid());
+ Q_ASSERT(hoverEnabledProperty.userType() == QMetaType::Bool);
+ return hoverEnabledProperty.toBool();
+ }
p = p->parentItem();
}
diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp
index 88813db376..8ab1d4dbc9 100644
--- a/src/quicktemplates/qquickdrawer.cpp
+++ b/src/quicktemplates/qquickdrawer.cpp
@@ -14,6 +14,8 @@
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*!
@@ -674,7 +676,7 @@ qreal QQuickDrawer::position() const
void QQuickDrawer::setPosition(qreal position)
{
Q_D(QQuickDrawer);
- position = qBound<qreal>(0.0, position, 1.0);
+ position = std::clamp(position, qreal(0.0), qreal(1.0));
if (qFuzzyCompare(d->position, position))
return;
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index bc573a3313..deb5651391 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -995,12 +995,17 @@ void QQuickPopupPrivate::toggleOverlay()
void QQuickPopupPrivate::updateContentPalettes(const QPalette& parentPalette)
{
// Inherit parent palette to all child objects
- inheritPalette(parentPalette);
-
+ if (providesPalette())
+ inheritPalette(parentPalette);
// Inherit parent palette to items within popup (such as headers and footers)
QQuickItemPrivate::get(popupItem)->updateChildrenPalettes(parentPalette);
}
+void QQuickPopupPrivate::updateChildrenPalettes(const QPalette& parentPalette)
+{
+ updateContentPalettes(parentPalette);
+}
+
void QQuickPopupPrivate::showDimmer()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h
index 9da91a4e26..c1935587a2 100644
--- a/src/quicktemplates/qquickpopup_p_p.h
+++ b/src/quicktemplates/qquickpopup_p_p.h
@@ -122,6 +122,8 @@ public:
QPalette defaultPalette() const override;
+ void updateChildrenPalettes(const QPalette &parentPalette) override;
+
enum TransitionState {
NoTransition, EnterTransition, ExitTransition
};
diff --git a/src/quicktemplates/qquickslider.cpp b/src/quicktemplates/qquickslider.cpp
index c095561d79..b922bbae08 100644
--- a/src/quicktemplates/qquickslider.cpp
+++ b/src/quicktemplates/qquickslider.cpp
@@ -7,6 +7,8 @@
#include <QtQuick/private/qquickwindow_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*!
@@ -124,13 +126,13 @@ qreal QQuickSliderPrivate::positionAt(const QPointF &point) const
if (!qFuzzyIsNull(extent))
pos = (q->height() - point.y() - q->bottomPadding() - offset) / extent;
}
- return qBound<qreal>(0.0, pos, 1.0);
+ return std::clamp(pos, qreal(0.0), qreal(1.0));
}
void QQuickSliderPrivate::setPosition(qreal pos)
{
Q_Q(QQuickSlider);
- pos = qBound<qreal>(0.0, pos, 1.0);
+ pos = std::clamp(pos, qreal(0.0), qreal(1.0));
if (qFuzzyCompare(position, pos))
return;
diff --git a/src/quicktemplates/qquickswipedelegate.cpp b/src/quicktemplates/qquickswipedelegate.cpp
index d617f4b3da..3c8df36564 100644
--- a/src/quicktemplates/qquickswipedelegate.cpp
+++ b/src/quicktemplates/qquickswipedelegate.cpp
@@ -16,6 +16,8 @@
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQuick/private/qquicktransitionmanager_p_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*!
@@ -589,7 +591,7 @@ qreal QQuickSwipe::position() const
void QQuickSwipe::setPosition(qreal position)
{
Q_D(QQuickSwipe);
- const qreal adjustedPosition = qBound<qreal>(-1.0, position, 1.0);
+ const qreal adjustedPosition = std::clamp(position, qreal(-1.0), qreal(1.0));
if (adjustedPosition == d->position)
return;
diff --git a/src/quicktemplates/qquickswitch.cpp b/src/quicktemplates/qquickswitch.cpp
index 1b2473cbdb..3234934e5e 100644
--- a/src/quicktemplates/qquickswitch.cpp
+++ b/src/quicktemplates/qquickswitch.cpp
@@ -9,6 +9,8 @@
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*!
@@ -122,7 +124,7 @@ qreal QQuickSwitch::position() const
void QQuickSwitch::setPosition(qreal position)
{
Q_D(QQuickSwitch);
- position = qBound<qreal>(0.0, position, 1.0);
+ position = std::clamp(position, qreal(0.0), qreal(1.0));
if (qFuzzyCompare(d->position, position))
return;
diff --git a/src/quicktemplates/qquickswitchdelegate.cpp b/src/quicktemplates/qquickswitchdelegate.cpp
index f17faf8aa9..adfcc69f16 100644
--- a/src/quicktemplates/qquickswitchdelegate.cpp
+++ b/src/quicktemplates/qquickswitchdelegate.cpp
@@ -5,6 +5,8 @@
#include "qquickitemdelegate_p_p.h"
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*!
@@ -119,7 +121,7 @@ qreal QQuickSwitchDelegate::position() const
void QQuickSwitchDelegate::setPosition(qreal position)
{
Q_D(QQuickSwitchDelegate);
- position = qBound<qreal>(0.0, position, 1.0);
+ position = std::clamp(position, qreal(0.0), qreal(1.0));
if (qFuzzyCompare(d->position, position))
return;
diff --git a/src/quicktemplates/qquicktemplatesutils.cpp b/src/quicktemplates/qquicktemplatesutils.cpp
new file mode 100644
index 0000000000..e998904cac
--- /dev/null
+++ b/src/quicktemplates/qquicktemplatesutils.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicktemplatesutils_p.h"
+
+#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+#include <QtQuickTemplates2/private/qquicktextarea_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQuickTemplatesUtils {
+
+/*!
+ \internal
+
+ Returns \c true if \a item is a \c QQuickControl or similar interactive
+ type that should be a QQuickControl-subclass but cannot due to existing
+ inheritance; e.g. \c QQuickTextField or \c QQuickTextArea.
+
+ \c QQuickPopup is not considered a controls type for this function due to
+ the original use cases that resulted in this being factored out.
+
+ \c QQuickLabel is not interactive.
+*/
+bool isInteractiveControlType(const QQuickItem *item)
+{
+ return qobject_cast<const QQuickControl *>(item)
+ || qobject_cast<const QQuickTextField *>(item)
+ || qobject_cast<const QQuickTextArea *>(item);
+}
+
+} // namespace QQuickTemplatesUtils
+
+QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquicktemplatesutils_p.h b/src/quicktemplates/qquicktemplatesutils_p.h
new file mode 100644
index 0000000000..8be302c096
--- /dev/null
+++ b/src/quicktemplates/qquicktemplatesutils_p.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKTEMPLATESUTILS_P_H
+#define QQUICKTEMPLATESUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qquickitem.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQuickTemplatesUtils {
+bool isInteractiveControlType(const QQuickItem *item);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQUICKTEMPLATESUTILS_P_H
diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp
index 1263d7667c..f44dc21ef5 100644
--- a/src/quicktestutils/quick/viewtestutils.cpp
+++ b/src/quicktestutils/quick/viewtestutils.cpp
@@ -484,11 +484,19 @@ namespace QQuickTest {
{
if (!initView(view, url))
return false;
+ const QPoint framePos(view.framePosition());
view.show();
if (!QTest::qWaitForWindowExposed(&view))
return false;
if (!view.rootObject())
return false;
+ if (view.flags().testFlag(Qt::FramelessWindowHint))
+ return true;
+ const bool positionOk = QTest::qWaitFor([&]{ return framePos != view.position(); });
+ if (!positionOk) {
+ qCritical() << "Position failed to update";
+ return false;
+ }
return true;
}
diff --git a/src/quicktestutils/quick/visualtestutils_p.h b/src/quicktestutils/quick/visualtestutils_p.h
index 24b3bebef0..c57868ee68 100644
--- a/src/quicktestutils/quick/visualtestutils_p.h
+++ b/src/quicktestutils/quick/visualtestutils_p.h
@@ -230,6 +230,13 @@ namespace QQuickVisualTestUtils
if (!(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) \
QSKIP("Window activation is not supported on this platform");
+#define SKIP_IF_NO_MOUSE_HOVER \
+do { \
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen")) \
+ || (QGuiApplication::platformName() == QLatin1String("minimal"))) \
+ QSKIP("Mouse hovering is not supported on the offscreen/minimal platforms"); \
+} while (false)
+
QT_END_NAMESPACE
#endif // QQUICKVISUALTESTUTILS_P_H
diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
index 802adaee14..47d28cb9a4 100644
--- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
+++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
@@ -295,6 +295,12 @@ void tst_QQmlPreview::blacklist()
QVERIFY(!blacklist3.isBlacklisted("/usr/src"));
QVERIFY(!blacklist3.isBlacklisted("/opt/share"));
QVERIFY(!blacklist3.isBlacklisted("/opt"));
+
+ QQmlPreviewBlacklist blacklist4;
+ blacklist4.whitelist(":/some/directory/with/file.qml");
+ blacklist4.blacklist(":/some/directory/with");
+ QVERIFY(blacklist4.isBlacklisted(":/some/directory/with"));
+ QVERIFY(!blacklist4.isBlacklisted(":/some/directory/with/file.qml"));
}
void tst_QQmlPreview::error()
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 00854ccb43..a3dc327cc2 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -6426,6 +6426,13 @@ void tst_QJSEngine::multiMatchingRegularExpression()
QVERIFY(result.isString());
QCOMPARE(result.toString(), "33.312.345,897"_L1);
+
+ const QJSValue result2 = engine.evaluate(R"(
+ "4F159D7AD40255D94A5B7EB9AAACD7408C79245D".replace(/(....)/g, '$1 ')
+ )");
+
+ QVERIFY(result2.isString());
+ QCOMPARE(result2.toString(), "4F15 9D7A D402 55D9 4A5B 7EB9 AAAC D740 8C79 245D "_L1);
}
QTEST_MAIN(tst_QJSEngine)
diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
index e2a939ac87..485c88c8fe 100644
--- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
@@ -11,6 +11,8 @@ qt_internal_add_test(tst_qmlcppcodegen
Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ confused_test_module
+ confused_test_moduleplugin
)
qt_internal_add_test(tst_qmlcppcodegen_interpreted
@@ -21,6 +23,8 @@ qt_internal_add_test(tst_qmlcppcodegen_interpreted
Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ confused_test_module
+ confused_test_moduleplugin
DEFINES
QT_TEST_FORCE_INTERPRETER
)
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 620818c5d6..d80783078f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -1,6 +1,8 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+add_subdirectory(Confused)
+
set(cpp_sources
ambiguous.h
birthdayparty.cpp birthdayparty.h
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/Confused/CMakeLists.txt
new file mode 100644
index 0000000000..1d03252ff9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/CMakeLists.txt
@@ -0,0 +1,27 @@
+qt_add_library(confused_test_module STATIC)
+qt_autogen_tools_initial_setup(confused_test_module)
+
+qt_policy(SET QTP0001 NEW)
+# Not QTP0004, since we have a manually written qmldir in a strange place
+
+set_source_files_properties("Test/broken.js"
+ PROPERTIES QT_RESOURCE_ALIAS "Test/broken.qml"
+)
+
+qt_add_qml_module(confused_test_module
+ URI Confused
+ VERSION 1.0
+ QML_FILES
+ Main.qml
+ Main2.qml
+ Test/test.js
+ Test/broken.js
+ RESOURCES
+ Test/qmldir
+)
+
+target_link_libraries(confused_test_module
+ PRIVATE Qt6::Qml
+)
+
+qt_autogen_tools_initial_setup(confused_test_moduleplugin)
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/Main.qml b/tests/auto/qml/qmlcppcodegen/data/Confused/Main.qml
new file mode 100644
index 0000000000..b1020610cf
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/Main.qml
@@ -0,0 +1,6 @@
+import QtQml
+import "Test" as T
+
+QtObject {
+ Component.onCompleted: T.Test.Print()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/Main2.qml b/tests/auto/qml/qmlcppcodegen/data/Confused/Main2.qml
new file mode 100644
index 0000000000..1878122b91
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/Main2.qml
@@ -0,0 +1,6 @@
+import QtQml
+import "Test" as T
+
+QtObject {
+ Component.onCompleted: T.Broken.Print()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/Test/broken.js b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/broken.js
new file mode 100644
index 0000000000..682e1d6e89
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/broken.js
@@ -0,0 +1,3 @@
+.pragma library
+
+function Print() { console.log("Hello from Broken") }
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/Test/qmldir b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/qmldir
new file mode 100644
index 0000000000..5315ead60c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/qmldir
@@ -0,0 +1,2 @@
+Test test.js
+Broken broken.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/Confused/Test/test.js b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/test.js
new file mode 100644
index 0000000000..b571c1657e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Confused/Test/test.js
@@ -0,0 +1,3 @@
+.pragma library
+
+function Print() { console.log("Hello from Test") }
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 6a61f08986..dae83e2c86 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -21,6 +21,7 @@
using namespace Qt::StringLiterals;
Q_IMPORT_QML_PLUGIN(TestTypesPlugin)
+Q_IMPORT_QML_PLUGIN(ConfusedPlugin)
class tst_QmlCppCodegen : public QObject
{
@@ -44,6 +45,7 @@ private slots:
void scopedEnum();
void sequenceToIterable();
void compositeTypeMethod();
+ void confusedModule();
void excessiveParameters();
void jsImport();
void jsmoduleImport();
@@ -513,6 +515,26 @@ void tst_QmlCppCodegen::compositeTypeMethod()
QTRY_VERIFY(spy.size() > 0);
}
+void tst_QmlCppCodegen::confusedModule()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/Confused/Main.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QTest::ignoreMessage(QtDebugMsg, "Hello from Test");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QQmlComponent component2(&engine, QUrl(u"qrc:/qt/qml/Confused/Main2.qml"_s));
+ QVERIFY2(!component2.isError(), component2.errorString().toUtf8());
+ // TODO: We would like to have a better error here, but we currently cannot propagate it.
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "qrc:/qt/qml/Confused/Main2.qml:5: "
+ "TypeError: Property 'Print' of object [object Object] is not a function");
+ QScopedPointer<QObject> object2(component2.create());
+ QVERIFY(!object2.isNull());
+}
+
void tst_QmlCppCodegen::excessiveParameters()
{
QQmlEngine engine;
diff --git a/tests/auto/qml/qmllint/data/GPane.qml b/tests/auto/qml/qmllint/data/GPane.qml
new file mode 100644
index 0000000000..af2cac1e30
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/GPane.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import QtQuick.Layouts
+
+ColumnLayout {
+ default property alias paneData: content.data
+
+ Column {
+ id: content
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/aliasGroup.qml b/tests/auto/qml/qmllint/data/aliasGroup.qml
new file mode 100644
index 0000000000..b3b59251a2
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/aliasGroup.qml
@@ -0,0 +1,20 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ property QtObject o: SampleDataStoreManagerSettingsSection {
+ contentControl.contentItem: QtObject { objectName: "a" }
+ }
+
+ component SampleDataStoreManagerSettingsSection: QtObject {
+ property alias contentControl: sectionContentItem
+
+ property QtObject o: QtObject {
+ id: sectionContentItem
+ property QtObject contentItem
+
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qmllint/data/aliasToRequiredPropertyIsNotRequiredItself.qml b/tests/auto/qml/qmllint/data/aliasToRequiredPropertyIsNotRequiredItself.qml
new file mode 100644
index 0000000000..247758a7b3
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/aliasToRequiredPropertyIsNotRequiredItself.qml
@@ -0,0 +1,30 @@
+import QtQml
+
+QtObject {
+ // Not at the root level
+ property QtObject o1: QtObject {
+ component Comp1 : QtObject {
+ required property int i1
+ }
+
+ property Comp1 c1: Comp1 {
+ id: compId1
+ i1: 1
+ }
+
+ property alias aliasToRequired1: compId1.i1
+ }
+
+
+ // At the root level
+ component Comp2 : QtObject {
+ required property int i2
+ }
+
+ property Comp2 c2: Comp2 {
+ id: compId2
+ i2: 2
+ }
+
+ property alias aliasToRequired2: compId2.i2
+}
diff --git a/tests/auto/qml/qmllint/data/deceptiveLayout.qml b/tests/auto/qml/qmllint/data/deceptiveLayout.qml
new file mode 100644
index 0000000000..b5683ba899
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/deceptiveLayout.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import QtQuick.Layouts
+
+GPane {
+ Layout.alignment: Qt.AlignHCenter
+
+ Text {
+ width: parent.width
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/evals.qml b/tests/auto/qml/qmllint/data/evals.qml
new file mode 100644
index 0000000000..d3d858cf8a
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/evals.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ function f() { eval(); }
+ function g() { eval("1 + 1"); }
+}
diff --git a/tests/auto/qml/qmllint/data/missingRequiredPropertyOnObjectDefinitionBinding.qml b/tests/auto/qml/qmllint/data/missingRequiredPropertyOnObjectDefinitionBinding.qml
new file mode 100644
index 0000000000..723fccf09a
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/missingRequiredPropertyOnObjectDefinitionBinding.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ property QtObject o: QtObject {
+ required property int i
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAlias.qml b/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAlias.qml
new file mode 100644
index 0000000000..de7ad1b820
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAlias.qml
@@ -0,0 +1,18 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ component C : QtObject {
+ required property int i
+ }
+
+ component C2 : C {
+ id: c2
+ property alias ai: c2.i
+ }
+
+ property C2 c2: C2 {
+ ai: 1
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAliasOfAlias.qml b/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAliasOfAlias.qml
new file mode 100644
index 0000000000..75450d8d49
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/setRequiredPropertyThroughAliasOfAlias.qml
@@ -0,0 +1,20 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ component C : QtObject {
+ id: c
+ required property int i
+ property alias ai: c.i
+ }
+
+ component C2 : C {
+ id: c2
+ property alias aai: c2.ai
+ }
+
+ property C2 c2: C2 {
+ aai: 1
+ }
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 690a3a272a..3bb3853d6a 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1011,9 +1011,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
"Cannot assign literal of type null to double") } } };
QTest::newRow("missingRequiredAlias")
<< QStringLiteral("missingRequiredAlias.qml")
- << Result { { Message {
- QStringLiteral("Component is missing required property requiredAlias from "
- "RequiredWithRootLevelAlias") } } };
+ << Result{ { Message{ u"Component is missing required property foo from Item"_s } } };
QTest::newRow("missingSingletonPragma")
<< QStringLiteral("missingSingletonPragma.qml")
<< Result { { Message { QStringLiteral(
@@ -1108,6 +1106,9 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("StoreNameMethod")
<< QStringLiteral("storeNameMethod.qml")
<< Result { { Message { QStringLiteral("Cannot assign to method foo") } } };
+ QTest::newRow("missingRequiredOnObjectDefinitionBinding")
+ << QStringLiteral("missingRequiredPropertyOnObjectDefinitionBinding.qml")
+ << Result{ { { uR"(Component is missing required property i from here)"_s, 4, 26 } } };
}
void TestQmllint::dirtyQmlCode()
@@ -1289,6 +1290,16 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("groupedAttachedLayout") << QStringLiteral("groupedAttachedLayout.qml");
QTest::newRow("constInvokable") << QStringLiteral("useConstInvokable.qml");
QTest::newRow("scopedAndUnscopedEnums") << QStringLiteral("enumValid.qml");
+
+ QTest::addRow("deceptiveLayout") << u"deceptiveLayout.qml"_s;
+ QTest::newRow("aliasGroup") << QStringLiteral("aliasGroup.qml");
+ QTest::newRow("aliasToRequiredProperty")
+ << QStringLiteral("aliasToRequiredPropertyIsNotRequiredItself.qml");
+ QTest::newRow("setRequiredTroughAlias") << QStringLiteral("setRequiredPropertyThroughAlias.qml");
+ QTest::newRow("setRequiredTroughAliasOfAlias")
+ << QStringLiteral("setRequiredPropertyThroughAliasOfAlias.qml");
+ QTest::newRow("evals")
+ << QStringLiteral("evals.qml");
}
void TestQmllint::cleanQmlCode()
diff --git a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
index 9e2ba24969..cd2f752e4b 100644
--- a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
+++ b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
@@ -5,6 +5,7 @@
#include <QtCore/QScopedPointer>
#include <QtQml/private/qqmlanybinding_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/private/qobject_p.h>
#include "withbindable.h"
class tst_qqmlanybinding : public QQmlDataTest
@@ -18,6 +19,7 @@ private slots:
void basicActions_data();
void basicActions();
void unboundQQmlPropertyBindingDoesNotCrash();
+ void ofDynamicMetaObject();
};
tst_qqmlanybinding::tst_qqmlanybinding()
@@ -127,6 +129,47 @@ void tst_qqmlanybinding::unboundQQmlPropertyBindingDoesNotCrash()
QCOMPARE(prop.read(), 1);
}
+class QObjectDynamicMetaObject : public QDynamicMetaObjectData
+{
+public:
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+ const QMetaObject *toDynamicMetaObject(QObject *) const final
+ {
+ return &QObject::staticMetaObject;
+ }
+#else
+ QMetaObject *toDynamicMetaObject(QObject *) final
+ {
+ return const_cast<QMetaObject *>(&QObject::staticMetaObject);
+ }
+#endif
+
+ int metaCall(QObject *o, QMetaObject::Call c, int id, void **argv) final
+ {
+ return o->qt_metacall(c, id, argv);
+ }
+};
+
+void tst_qqmlanybinding::ofDynamicMetaObject()
+{
+ QObject o;
+ const QQmlPropertyIndex objectNameIndex(
+ QObject::staticMetaObject.indexOfProperty("objectName"));
+ QObjectPrivate::get(&o)->metaObject = new QObjectDynamicMetaObject;
+
+ QQmlAnyBinding a = QQmlAnyBinding::ofProperty(&o, objectNameIndex);
+ QVERIFY(!a);
+
+ QPropertyBinding<QString> binding
+ = Qt::makePropertyBinding([]() { return QStringLiteral("foo"); });
+ o.bindableObjectName().setBinding(binding);
+ QQmlAnyBinding b = QQmlAnyBinding::ofProperty(&o, objectNameIndex);
+ QVERIFY(b);
+
+ QCOMPARE(QPropertyBindingPrivate::get(b.asUntypedPropertyBinding()),
+ QPropertyBindingPrivate::get(binding));
+}
+
QTEST_MAIN(tst_qqmlanybinding)
#include "tst_qqmlanybinding.moc"
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 63655fa60d..1612848dfb 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -3733,6 +3733,8 @@ void tst_qqmlecmascript::attachedPropertyScope()
void tst_qqmlecmascript::scriptConnect()
{
+ QTest::failOnWarning(QRegularExpression(".*"));
+
QQmlEngine engine;
{
@@ -3784,8 +3786,6 @@ void tst_qqmlecmascript::scriptConnect()
QCOMPARE(object->methodCalled(), false);
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression("When matching arguments for MyQmlObject_QML_[0-9]+::methodNoArgs\\(\\):"));
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Too many arguments, ignoring 5"));
emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
QCOMPARE(object->methodCalled(), true);
}
@@ -3799,8 +3799,6 @@ void tst_qqmlecmascript::scriptConnect()
QVERIFY(object != nullptr);
QCOMPARE(object->methodCalled(), false);
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression("When matching arguments for MyQmlObject_QML_[0-9]+::methodNoArgs\\(\\):"));
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Too many arguments, ignoring 5"));
emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
QCOMPARE(object->methodCalled(), true);
}
@@ -4625,7 +4623,7 @@ void tst_qqmlecmascript::singletonTypeImportOrder()
QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
- QCOMPARE(object->property("v").toInt(), 1);
+ QCOMPARE(object->property("v").toInt(), 2);
}
void tst_qqmlecmascript::singletonTypeResolution()
diff --git a/tests/auto/qml/qqmllanguage/CMakeLists.txt b/tests/auto/qml/qqmllanguage/CMakeLists.txt
index fc9bbf17df..c36ee66579 100644
--- a/tests/auto/qml/qqmllanguage/CMakeLists.txt
+++ b/tests/auto/qml/qqmllanguage/CMakeLists.txt
@@ -70,3 +70,7 @@ set_target_properties(tst_qqmllanguage PROPERTIES
)
_qt_internal_qml_type_registration(tst_qqmllanguage)
+
+add_subdirectory(OtherModuleTest)
+target_link_libraries(tst_qqmllanguage PUBLIC other_module)
+target_link_libraries(tst_qqmllanguage PUBLIC other_moduleplugin)
diff --git a/tests/auto/qml/qqmllanguage/OtherModuleTest/CMakeLists.txt b/tests/auto/qml/qqmllanguage/OtherModuleTest/CMakeLists.txt
new file mode 100644
index 0000000000..84a2a46ec1
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/OtherModuleTest/CMakeLists.txt
@@ -0,0 +1,15 @@
+qt_add_library(other_module STATIC)
+qt_autogen_tools_initial_setup(other_module)
+
+qt_policy(set QTP0001 NEW)
+
+qt6_add_qml_module(other_module
+ URI OtherModuleTest
+ VERSION 1.0
+ SOURCES
+ other.h
+)
+
+qt_autogen_tools_initial_setup(other_moduleplugin)
+
+add_dependencies(tst_qqmllanguage other_module other_moduleplugin)
diff --git a/tests/auto/qml/qqmllanguage/OtherModuleTest/other.h b/tests/auto/qml/qqmllanguage/OtherModuleTest/other.h
new file mode 100644
index 0000000000..b6cb53044a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/OtherModuleTest/other.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef OTHER_H
+#define OTHER_H
+
+#include <qqmlregistration.h>
+#include <QQmlEngine>
+
+namespace YepNamespaceB {
+class YepAttached : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ YepAttached(QObject *parent) : QObject(parent) { }
+ Q_INVOKABLE QString s() const { return QStringLiteral("OtherModuleTest Attached Type"); }
+};
+class Yep : public QObject
+{
+ Q_OBJECT
+ QML_ATTACHED(YepAttached)
+ QML_ELEMENT
+
+public:
+ static YepAttached *qmlAttachedProperties(QObject *object) { return new YepAttached(object); }
+};
+
+class YepSingleton : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ Q_INVOKABLE QString s() const { return QStringLiteral("OtherModuleTest Singleton"); }
+};
+
+class MyObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ Q_INVOKABLE QString s() const { return QStringLiteral("OtherModuleTest"); }
+};
+} // namespace YepNamespaceB
+
+#endif // OTHER_H
diff --git a/tests/auto/qml/qqmllanguage/data/JsSelfImport/CustomPrinter.qml b/tests/auto/qml/qqmllanguage/data/JsSelfImport/CustomPrinter.qml
new file mode 100644
index 0000000000..92bfded464
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/JsSelfImport/CustomPrinter.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+import QtQml
+
+QtObject {
+ function ppp() : string {
+ return "customPrint"
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/JsSelfImport/JsLib.js b/tests/auto/qml/qqmllanguage/data/JsSelfImport/JsLib.js
new file mode 100644
index 0000000000..d793783280
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/JsSelfImport/JsLib.js
@@ -0,0 +1,6 @@
+.pragma library
+.import JsSelfImport as A
+
+function func() {
+ return A.CustomPrinter.ppp()
+}
diff --git a/tests/auto/qml/qqmllanguage/data/JsSelfImport/Main.qml b/tests/auto/qml/qqmllanguage/data/JsSelfImport/Main.qml
new file mode 100644
index 0000000000..486768eb9c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/JsSelfImport/Main.qml
@@ -0,0 +1,6 @@
+import QtQml
+import JsSelfImport
+
+QtObject {
+ objectName: JsLib.func()
+}
diff --git a/tests/auto/qml/qqmllanguage/data/JsSelfImport/qmldir b/tests/auto/qml/qqmllanguage/data/JsSelfImport/qmldir
new file mode 100644
index 0000000000..2a0d8973fa
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/JsSelfImport/qmldir
@@ -0,0 +1,5 @@
+module JsSelfImport
+singleton CustomPrinter 254.0 CustomPrinter.qml
+JsLib 254.0 JsLib.js
+Main 254.0 Main.qml
+
diff --git a/tests/auto/qml/qqmllanguage/data/aliasOfBindableValueTypeProperty.qml b/tests/auto/qml/qqmllanguage/data/aliasOfBindableValueTypeProperty.qml
new file mode 100644
index 0000000000..f8740e71e0
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/aliasOfBindableValueTypeProperty.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+import Test
+
+BindablePoint {
+ id: root
+ property int aaChanges: 0
+ property int bbChanges: 0
+ property alias aa: root.point.x
+ property alias bb: root.point.y
+ onAaChanged: ++aaChanges
+ bb: objectName
+ onBbChanged: ++bbChanges
+ objectName: "14"
+
+ function reassign() {
+ bb = Qt.binding(function() { return 16 + 0.5 });
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderAB.qml b/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderAB.qml
new file mode 100644
index 0000000000..3548bc7a84
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderAB.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+import StaticTest as ST
+
+import OtherModuleTest
+import StaticTest
+
+QtObject {
+ property QtObject mo: ST.MyObject { }
+ // Both StaticTest and OtherModuleTest provide MyObject
+ property string s: (mo as MyObject).s()
+}
diff --git a/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderBA.qml b/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderBA.qml
new file mode 100644
index 0000000000..537677a89e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/asCastTypeResolutionImportOrderBA.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+import StaticTest as ST
+
+import StaticTest
+import OtherModuleTest
+
+QtObject {
+ property QtObject mo: ST.MyObject { }
+ // Both StaticTest and OtherModuleTest provide MyObject
+ property string s: (mo as MyObject).s()
+}
diff --git a/tests/auto/qml/qqmllanguage/data/attachedTypeResolutionImportOrder.qml b/tests/auto/qml/qqmllanguage/data/attachedTypeResolutionImportOrder.qml
new file mode 100644
index 0000000000..c0a3c0e896
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/attachedTypeResolutionImportOrder.qml
@@ -0,0 +1,9 @@
+import StaticTest
+import OtherModuleTest
+
+import QtQml
+
+QtObject {
+ // Both StaticTest and OtherModuleTest provide Yep
+ property string s: Yep.s()
+}
diff --git a/tests/auto/qml/qqmllanguage/data/singletonResolutionImportOrder.qml b/tests/auto/qml/qqmllanguage/data/singletonResolutionImportOrder.qml
new file mode 100644
index 0000000000..f8f24524e9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/singletonResolutionImportOrder.qml
@@ -0,0 +1,9 @@
+import StaticTest
+import OtherModuleTest
+
+import QtQml
+
+QtObject {
+ // Both StaticTest and OtherModuleTest provide YepSingleton
+ property string s: YepSingleton.s()
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index c65cbe329d..ca554d895e 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -165,6 +165,8 @@ void registerTypes()
qmlRegisterTypesAndRevisions<Counter>("Test", 1);
qmlRegisterTypesAndRevisions<NestedVectors>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<BindablePoint>("Test", 1);
}
QVariant myCustomVariantTypeConverter(const QString &data)
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index bfee2d07c9..438eda3cef 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -2640,4 +2640,58 @@ private:
std::vector<std::vector<int>> m_list;
};
+namespace YepNamespaceA {
+class YepAttached : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ YepAttached(QObject *parent) : QObject(parent) { }
+ Q_INVOKABLE QString s() const { return QStringLiteral("StaticTest Attached Type"); }
+};
+class Yep : public QObject
+{
+ Q_OBJECT
+ QML_ATTACHED(YepAttached)
+ QML_ELEMENT
+
+public:
+ static YepAttached *qmlAttachedProperties(QObject *object) { return new YepAttached(object); }
+};
+
+class YepSingleton : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ Q_INVOKABLE QString s() const { return QStringLiteral("StaticTest Singleton"); }
+};
+
+class MyObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ Q_INVOKABLE QString s() const { return QStringLiteral("StaticTest"); }
+};
+} // namespace YepNamespaceA
+
+class BindablePoint : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QPointF point BINDABLE bindablePoint READ default WRITE default)
+public:
+ BindablePoint(QObject *parent = nullptr) : QObject(parent), m_point(QPointF(101, 102)) {}
+
+ QBindable<QPointF> bindablePoint() { return QBindable<QPointF>(&m_point); }
+
+private:
+ QProperty<QPointF> m_point;
+};
+
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index b7dff336af..df89dcff39 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qtest.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlincubator.h>
#include <QtCore/qiterable.h>
@@ -43,6 +44,8 @@
using namespace Qt::StringLiterals;
+Q_IMPORT_QML_PLUGIN(OtherModuleTestPlugin)
+
DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
static inline bool isCaseSensitiveFileSystem(const QString &path) {
@@ -435,6 +438,15 @@ private slots:
void overrideInnerBinding();
+ void jsSelfImport();
+
+ void singletonResultionImportOrder();
+ void attachedTypeResultionImportOrder();
+ void asCastTypeResolutionImportOrderAB();
+ void asCastTypeResolutionImportOrderBA();
+
+ void aliasOfBindableValueTypeProperty();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -8277,6 +8289,110 @@ void tst_qqmllanguage::overrideInnerBinding()
QCOMPARE(font.family(), "Ariallll");
}
+void tst_qqmllanguage::jsSelfImport()
+{
+ QQmlEngine engine;
+ engine.addImportPath(dataDirectory());
+ QQmlComponent c(&engine, "JsSelfImport", "Main");
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->objectName(), "customPrint");
+}
+
+void tst_qqmllanguage::singletonResultionImportOrder()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("singletonResolutionImportOrder.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("s").toString(), "OtherModuleTest Singleton");
+}
+
+void tst_qqmllanguage::attachedTypeResultionImportOrder()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("attachedTypeResolutionImportOrder.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("s").toString(), "OtherModuleTest Attached Type");
+}
+
+void tst_qqmllanguage::asCastTypeResolutionImportOrderAB()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("asCastTypeResolutionImportOrderAB.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("s").toString(), "StaticTest");
+}
+
+void tst_qqmllanguage::asCastTypeResolutionImportOrderBA()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("asCastTypeResolutionImportOrderBA.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg,
+ QRegularExpression(".*TypeError: Cannot call method 's' of null"));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+}
+
+void tst_qqmllanguage::aliasOfBindableValueTypeProperty()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("aliasOfBindableValueTypeProperty.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("aa"), 101);
+ QCOMPARE(o->property("bb"), 14);
+ QCOMPARE(o->property("aaChanges"), 1);
+ QCOMPARE(o->property("bbChanges"), 1);
+
+ o->setObjectName("15");
+ QCOMPARE(o->property("aa"), 101);
+ QCOMPARE(o->property("bb"), 15);
+ QCOMPARE(o->property("aaChanges"), 2);
+ QCOMPARE(o->property("bbChanges"), 2);
+
+ o->setProperty("point", QPointF(17, 18));
+ QCOMPARE(o->property("aa"), 17);
+ QCOMPARE(o->property("bb"), 18);
+ QCOMPARE(o->property("aaChanges"), 3);
+ QCOMPARE(o->property("bbChanges"), 3);
+
+ BindablePoint *b = qobject_cast<BindablePoint *>(o.data());
+ QVERIFY(b);
+ b->bindablePoint().setValue(QPointF(19, 20));
+ QCOMPARE(o->property("aa"), 19);
+ QCOMPARE(o->property("bb"), 20);
+ QCOMPARE(o->property("aaChanges"), 4);
+ QCOMPARE(o->property("bbChanges"), 4);
+
+ QMetaObject::invokeMethod(o.data(), "reassign");
+ QCOMPARE(o->property("aa"), 19);
+ QCOMPARE(o->property("bb"), 16.5);
+ QCOMPARE(o->property("aaChanges"), 5);
+ QCOMPARE(o->property("bbChanges"), 5);
+
+ const QMetaObject *mo = o->metaObject();
+ const int aaIndex = mo->indexOfProperty("aa");
+ QVERIFY(aaIndex >= 0);
+
+ // Querying the bindable of a value type alias results in the bindable of the core property.
+ QUntypedBindable bindable;
+ void *args[] { &bindable };
+ QCOMPARE(o->metaObject()->metacall(o.data(), QMetaObject::BindableProperty, aaIndex, args), -1);
+ QVERIFY(bindable.isValid());
+ QCOMPARE(bindable.metaType(), QMetaType::fromType<QPointF>());
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 8f0e657e54..4010be56b3 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -273,18 +273,28 @@ void tst_qqmllistmodel::static_i18n_data()
<< QVariant(QString::fromUtf8("na\303\257ve"))
<< QString();
+ QTest::newRow("QT_TR_NOOP_disambig")
+ << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"na\303\257ve\", \"disambig\") }")
+ << QVariant(QString::fromUtf8("na\303\257ve"))
+ << QString();
+
QTest::newRow("QT_TRANSLATE_NOOP")
<< "ListElement { foo: QT_TRANSLATE_NOOP(\"MyListModel\", \"hello\") }"
<< QVariant(QString("hello"))
<< QString();
+ QTest::newRow("QT_TRANSLATE_NOOP_disambig")
+ << "ListElement { foo: QT_TRANSLATE_NOOP(\"MyListModel\", \"hello\", \"greeting\") }"
+ << QVariant(QString("hello"))
+ << QString();
+
QTest::newRow("QT_TRID_NOOP")
<< QString::fromUtf8("ListElement { foo: QT_TRID_NOOP(\"qtn_1st_text\") }")
<< QVariant(QString("qtn_1st_text"))
<< QString();
QTest::newRow("QT_TR_NOOP extra param")
- << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"hello\",\"world\") }")
+ << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"hello\",\"world\", \"!\") }")
<< QVariant(QString())
<< QString("ListElement: cannot use script for property value");
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index 67d15e5043..7b42e09b0d 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -447,11 +447,12 @@ void tst_qqmllocale::toString_data()
functionCallScript = "locale.toString(123.456)";
QTest::newRow(qPrintable(functionCallScript)) << "de_DE" << functionCallScript << "123,456" << QString();
- functionCallScript = "locale.toString(123.456, 'e')";
+ // Exponent case should match what's asked for:
+ functionCallScript = "locale.toString(123.456, 'E')";
QTest::newRow(qPrintable(functionCallScript)) << "de_DE" << functionCallScript << "1,234560E+02" << QString();
functionCallScript = "locale.toString(123.456, 'e', 1)";
- QTest::newRow(qPrintable(functionCallScript)) << "de_DE" << functionCallScript << "1,2E+02" << QString();
+ QTest::newRow(qPrintable(functionCallScript)) << "de_DE" << functionCallScript << "1,2e+02" << QString();
// Test toString(Date, string) and toString(Date[, FormatType]).
const QDateTime midnight2000(QDate(2000, 1, 1), QTime(0, 0));
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 235de5f406..396f876fe4 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -2418,6 +2418,7 @@ void tst_qqmlproperty::initFlags_data()
QTest::addColumn<bool>("passObject");
QTest::addColumn<QString>("name");
QTest::addColumn<QQmlPropertyPrivate::InitFlags>("flags");
+ QTest::addColumn<bool>("useInnerObject");
const QString names[] = {
QStringLiteral("foo"),
@@ -2443,7 +2444,10 @@ void tst_qqmlproperty::initFlags_data()
for (const auto &flagSet : flagSets) {
const QString rowName = QStringLiteral("%1,%2,%3")
.arg(passObject).arg(name).arg(flagSet.toInt());
- QTest::addRow("%s", qPrintable(rowName)) << passObject << name << flagSet;
+ QTest::addRow("%s", qPrintable(rowName + ",outer"))
+ << passObject << name << flagSet << false;
+ QTest::addRow("%s", qPrintable(rowName + ",inner"))
+ << passObject << name << flagSet << true;
}
}
}
@@ -2454,6 +2458,7 @@ void tst_qqmlproperty::initFlags()
QFETCH(bool, passObject);
QFETCH(QString, name);
QFETCH(QQmlPropertyPrivate::InitFlags, flags);
+ QFETCH(bool, useInnerObject);
QQmlEngine engine;
QQmlComponent c(&engine);
@@ -2465,15 +2470,31 @@ void tst_qqmlproperty::initFlags()
property int bar: 12
property alias abar: self.bar
}
- )", QUrl());
- QVERIFY(c.isReady());
+ )", QUrl("outer"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(qmlContext(o.data()));
+ QObject *object = nullptr;
+
+ QScopedPointer<QObject> i;
+ if (useInnerObject) {
+ QQmlComponent c2(&engine);
+ c2.setData(R"(
+ import QtQml
+ QtObject {}
+ )", QUrl("inner"));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ i.reset(c2.create(qmlContext(o.data())));
+ object = i.data();
+ } else {
+ object = o.data();
+ }
+
+ QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(qmlContext(object));
const QQmlProperty property = QQmlPropertyPrivate::create(
- passObject ? o.data() : nullptr, name, context, flags);
+ passObject ? object : nullptr, name, context, flags);
const bool usesId = name.startsWith(QStringLiteral("self."));
const bool hasSignal = name.endsWith(QStringLiteral("foo"));
@@ -2485,6 +2506,8 @@ void tst_qqmlproperty::initFlags()
QVERIFY(!property.isValid());
} else if (hasSignal && !(flags & QQmlPropertyPrivate::InitFlag::AllowSignal)) {
QVERIFY(!property.isValid());
+ } else if (useInnerObject && passObject) {
+ QVERIFY(!property.isValid());
} else {
QVERIFY(property.isValid());
if (name.endsWith(QStringLiteral("bar"))) {
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
index c4cfd085af..30c72e1d02 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
@@ -1,22 +1,9 @@
[touchAndDragHandlerOnFlickable]
windows gcc
-opensuse-leap
[touchDragFlickableBehindSlider]
windows gcc
[touchDragFlickableBehindButton]
windows gcc
-# QTBUG-95887
-[mouseDragSlider]
-opensuse-leap
-# QTBUG-95887
-[touchDragFlickableBehindButton]
-opensuse-leap
-# QTBUG-95887
-[mouseClickButton]
-opensuse-leap
-# QTBUG-95887
-[mouseDragFlickableBehindSlider]
-opensuse-leap
# QTBUG-103061
[touchDragFlickableBehindItemWithHandlers]
android
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
index 1a9a139aef..edcfaeea7b 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
@@ -71,6 +71,7 @@ void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const
window.reset(new QQuickView);
window->setSource(testFileUrl(fileName));
QTRY_COMPARE(window->status(), QQuickView::Ready);
+ window.data()->setFlag(Qt::FramelessWindowHint);
QQuickViewTestUtils::centerOnScreen(window.data());
QQuickViewTestUtils::moveMouseAway(window.data());
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index 920bf77978..b3b2259319 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -458,10 +458,13 @@ void tst_HoverHandler::window() // QTBUG-98717
{
QQmlEngine engine;
QQmlComponent component(&engine);
+ const QPoint pos(30, 30);
component.loadUrl(testFileUrl("windowCursorShape.qml"));
QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create()));
QVERIFY(!window.isNull());
+ window->setFramePosition(pos);
window->show();
+ QTRY_COMPARE(window->framePosition(), pos);
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
#if QT_CONFIG(cursor)
if (isPlatformWayland())
diff --git a/tests/auto/quick/qquickanimators/BLACKLIST b/tests/auto/quick/qquickanimators/BLACKLIST
index f0da331c9f..6e9e3f5047 100644
--- a/tests/auto/quick/qquickanimators/BLACKLIST
+++ b/tests/auto/quick/qquickanimators/BLACKLIST
@@ -1,4 +1,3 @@
# QTBUG-89023
[testTransitionsWithImplicitFrom]
ubuntu-22.04
-opensuse-leap
diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
index 92f75620bd..baea27c036 100644
--- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
+++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
@@ -139,7 +139,7 @@ void tst_Animators::testTransitionsWithImplicitFrom()
// transition to the "right" state
rectangle->setState("right");
- QTRY_VERIFY(!controller->m_runningAnimators.isEmpty());
+ QVERIFY(QTest::qWaitFor([&]{ return !controller->m_runningAnimators.isEmpty();}));
auto r_job = *controller->m_runningAnimators.constBegin();
QVERIFY(r_job);
QCOMPARE(r_job->from(), 0);
@@ -153,7 +153,7 @@ void tst_Animators::testTransitionsWithImplicitFrom()
// transition back to the "left" state
rectangle->setState("left");
- QTRY_VERIFY(!controller->m_runningAnimators.isEmpty());
+ QVERIFY(QTest::qWaitFor([&]{ return !controller->m_runningAnimators.isEmpty();}));
auto l_job = *controller->m_runningAnimators.constBegin();
QVERIFY(l_job);
QCOMPARE(l_job->from(), 100); // this was not working in older Qt versions
diff --git a/tests/auto/quick/qquickapplication/BLACKLIST b/tests/auto/quick/qquickapplication/BLACKLIST
index 1b7464e7c4..b8bc4363f1 100644
--- a/tests/auto/quick/qquickapplication/BLACKLIST
+++ b/tests/auto/quick/qquickapplication/BLACKLIST
@@ -1,3 +1,2 @@
-[active]
-opensuse-42.3
-opensuse-leap
+[state]
+opensuse-leap # QTBUG-122031
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index b3633a9dcc..0bac4baaea 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -91,7 +91,8 @@ void tst_qquickapplication::active()
} else {
// Otherwise, app activation is triggered by window activation.
window.show();
- window.requestActivate();
+ if (QGuiApplication::platformName().toLower() != "xcb")
+ window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QGuiApplication::focusWindow(), &window);
QVERIFY(item->property("active").toBool());
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 4ce2b7e3c6..58269a6463 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1753,8 +1753,8 @@ void tst_qquickflickable::margins()
QTRY_COMPARE(obj->contentX(), -30.);
// Reduce top margin
- obj->setTopMargin(20);
- QTRY_COMPARE(obj->contentY(), -20.);
+ obj->setTopMargin(10);
+ QTRY_COMPARE(obj->contentY(), -10.);
// position to the far right, including margin
obj->setContentX(1600 + 50 - obj->width());
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index e4da77acba..a686534d20 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -1,6 +1,3 @@
-[enforceRange_withoutHighlight]
-opensuse-42.3
-opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index c43278c553..b63dec7320 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -2009,6 +2009,7 @@ void tst_QQuickListView::enforceRange_withoutHighlight()
QTRY_COMPARE(listview->contentY(), expectedPos);
expectedPos += 20 + 10; // scroll past 1st section and section delegate of 2nd section
+ QTRY_VERIFY(window.data()->isActive());
QTest::keyClick(window.data(), Qt::Key_Down);
QTRY_COMPARE(listview->contentY(), expectedPos);
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index 7e9aa8c100..389f037166 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -4,6 +4,8 @@
#include <QSignalSpy>
+#include <QtCore/QElapsedTimer>
+
#include <QtQml/QQmlContext>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 7482367057..5942588c8d 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -197,6 +197,8 @@ private slots:
void positionViewAtLastRow();
void positionViewAtLastColumn_data();
void positionViewAtLastColumn();
+ void positionViewAtRow_syncView();
+ void positionViewAtColumn_syncView();
void itemAtCell_data();
void itemAtCell();
void leftRightTopBottomProperties_data();
@@ -4370,6 +4372,80 @@ void tst_QQuickTableView::positionViewAtLastColumn()
}
}
+void tst_QQuickTableView::positionViewAtRow_syncView()
+{
+ // Check that if you call positionViewAtRow on a sync child, both
+ // the syncView and the syncChild will be positioned.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewV);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewV->setModel(model);
+ QCOMPARE(tableViewV->syncDirection(), Qt::Vertical);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->topRow(), 0);
+ QCOMPARE(tableView->leftColumn(), 0);
+ QCOMPARE(tableViewV->topRow(), 0);
+ QCOMPARE(tableViewV->leftColumn(), 0);
+
+ tableViewV->positionViewAtRow(50, QQuickTableView::AlignTop);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->leftColumn(), 0);
+ QCOMPARE(tableView->topRow(), 50);
+ QCOMPARE(tableViewV->leftColumn(), 0);
+ QCOMPARE(tableViewV->topRow(), 50);
+
+ // Trying to position the sync child in an unsynced
+ // direction should only move the sync child.
+ tableViewV->positionViewAtColumn(90, QQuickTableView::AlignLeft);
+ WAIT_UNTIL_POLISHED_ARG(tableViewV);
+
+ QCOMPARE(tableView->leftColumn(), 0);
+ QCOMPARE(tableView->topRow(), 50);
+ QCOMPARE(tableViewV->leftColumn(), 90);
+ QCOMPARE(tableViewV->topRow(), 50);
+}
+
+void tst_QQuickTableView::positionViewAtColumn_syncView()
+{
+ // Check that if you call positionViewAtColumn on a sync child, both
+ // the syncView and the syncChild will be positioned.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableViewH->setModel(model);
+ QCOMPARE(tableViewH->syncDirection(), Qt::Horizontal);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->topRow(), 0);
+ QCOMPARE(tableView->leftColumn(), 0);
+ QCOMPARE(tableViewH->topRow(), 0);
+ QCOMPARE(tableViewH->leftColumn(), 0);
+
+ tableViewH->positionViewAtColumn(50, QQuickTableView::AlignLeft);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->leftColumn(), 50);
+ QCOMPARE(tableView->topRow(), 0);
+ QCOMPARE(tableViewH->leftColumn(), 50);
+ QCOMPARE(tableViewH->topRow(), 0);
+
+ // Trying to position the sync child in an unsynced
+ // direction should only move the sync child.
+ tableViewH->positionViewAtRow(90, QQuickTableView::AlignTop);
+ WAIT_UNTIL_POLISHED_ARG(tableViewH);
+
+ QCOMPARE(tableView->leftColumn(), 50);
+ QCOMPARE(tableView->topRow(), 0);
+ QCOMPARE(tableViewH->leftColumn(), 50);
+ QCOMPARE(tableViewH->topRow(), 90);
+}
+
void tst_QQuickTableView::itemAtCell_data()
{
QTest::addColumn<QPoint>("cell");
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 797c8b16cf..4ba1c3bec6 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -4283,7 +4283,7 @@ void tst_qquicktext::baselineOffset_data()
QTest::newRow("scaled font")
<< "hello world"
<< "hello\nworld"
- << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
+ << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 10000; fontSizeMode: Text.HorizontalFit")
<< &expectedBaselineScaled
<< &expectedBaselineTop;
@@ -4379,7 +4379,7 @@ void tst_qquicktext::baselineOffset_data()
QTest::newRow("scaled font with padding")
<< "hello world"
<< "hello\nworld"
- << QByteArray("width: 200; topPadding: 10; bottomPadding: 20; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
+ << QByteArray("width: 200; topPadding: 10; bottomPadding: 20; minimumPointSize: 1; font.pointSize: 10000; fontSizeMode: Text.HorizontalFit")
<< &expectedBaselineScaled
<< &expectedBaselineTop;
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 44745f8263..399627c4f1 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -2873,7 +2873,7 @@ void tst_qquicktextedit::cursorVisible()
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
- QCOMPARE(&view, qGuiApp->focusWindow());
+ QTRY_COMPARE(&view, qGuiApp->focusWindow());
QCOMPARE(edit.isCursorVisible(), false);
@@ -4282,8 +4282,8 @@ void tst_qquicktextedit::preeditCursorRectangle()
QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
QVERIFY(edit);
+ QTRY_VERIFY(edit->findChild<QQuickItem *>("cursor") != nullptr);
QQuickItem *cursor = edit->findChild<QQuickItem *>("cursor");
- QVERIFY(cursor);
QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
@@ -5714,7 +5714,7 @@ void tst_qquicktextedit::undo()
window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
- QVERIFY(textEdit->hasActiveFocus());
+ QTRY_VERIFY(textEdit->hasActiveFocus());
QVERIFY(!textEdit->canUndo());
QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
@@ -6185,7 +6185,7 @@ void tst_qquicktextedit::emptytags_QTBUG_22058()
window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("inputField")));
- QVERIFY(input->hasActiveFocus());
+ QTRY_VERIFY(input->hasActiveFocus());
QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
event.setCommitString("<b>Bold<");
@@ -6435,7 +6435,7 @@ void tst_qquicktextedit::keyEventPropagation()
QSignalSpy upSpy(root, SIGNAL(keyUp(int)));
QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit *>();
- QVERIFY(textEdit->hasActiveFocus());
+ QTRY_VERIFY(textEdit->hasActiveFocus());
simulateKey(&view, Qt::Key_Back);
QCOMPARE(downSpy.size(), 1);
QCOMPARE(upSpy.size(), 1);
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index c944406e10..c6781c03f5 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -7044,8 +7044,8 @@ void tst_qquicktextinput::checkCursorDelegateWhenPaddingChanged()
QQuickTextInput *textInput = view.rootObject()->findChild<QQuickTextInput *>("textInput");
QVERIFY(textInput);
+ QTRY_VERIFY(textInput->findChild<QQuickItem *>("cursorDelegate"));
QQuickItem *cursorDelegate = textInput->findChild<QQuickItem *>("cursorDelegate");
- QVERIFY(cursorDelegate);
QCOMPARE(cursorDelegate->x(), textInput->leftPadding());
QCOMPARE(cursorDelegate->y(), textInput->topPadding());
diff --git a/tests/auto/quickcontrols/controls/data/tst_button.qml b/tests/auto/quickcontrols/controls/data/tst_button.qml
index 4ca8ebfd6d..f363e1b294 100644
--- a/tests/auto/quickcontrols/controls/data/tst_button.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_button.qml
@@ -169,14 +169,19 @@ TestCase {
let control1 = createTemporaryObject(button, testCase)
verify(control1)
+ let pressedChangedCount1 = 0
let pressedCount1 = 0
- let pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
+ let pressedChangedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
+ verify(pressedChangedSpy1.valid)
+
+ let pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressed"})
verify(pressedSpy1.valid)
let touch = touchEvent(control1)
touch.press(0, control1, 0, 0).commit().move(0, control1, control1.width - 1, control1.height - 1).commit()
+ compare(pressedChangedSpy1.count, ++pressedChangedCount1)
compare(pressedSpy1.count, ++pressedCount1)
compare(control1.pressed, true)
@@ -185,34 +190,61 @@ TestCase {
touch.stationary(0).move(1, control1).commit()
touch.stationary(0).release(1).commit()
+ compare(pressedChangedSpy1.count, pressedChangedCount1)
compare(pressedSpy1.count, pressedCount1)
compare(control1.pressed, true)
let control2 = createTemporaryObject(button, testCase, {y: control1.height})
verify(control2)
+ let pressedChangedCount2 = 0
let pressedCount2 = 0
- let pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
+ let pressedChangedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
+ verify(pressedChangedSpy2.valid)
+
+ let pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressed"})
verify(pressedSpy2.valid)
// press the second button
touch.stationary(0).press(2, control2, 0, 0).commit()
+ compare(pressedChangedSpy2.count, ++pressedChangedCount2)
compare(pressedSpy2.count, ++pressedCount2)
compare(control2.pressed, true)
- compare(pressedSpy1.count, pressedCount1)
+ compare(pressedChangedSpy1.count, pressedChangedCount1)
+ compare(pressedSpy1.count, pressedChangedCount1)
compare(control1.pressed, true)
// release both buttons
touch.release(0, control1).release(2, control2).commit()
- compare(pressedSpy2.count, ++pressedCount2)
+ compare(pressedChangedSpy2.count, ++pressedChangedCount2)
+ compare(pressedSpy2.count, pressedCount2)
compare(control2.pressed, false)
+ compare(pressedChangedSpy1.count, ++pressedChangedCount1)
+ compare(control1.pressed, false)
+
+ // press two buttons with two fingers, then release: each should only be pressed once
+ touch.press(1, control1, 0, 0).commit()
+
+ compare(pressedChangedSpy1.count, ++pressedChangedCount1)
compare(pressedSpy1.count, ++pressedCount1)
+ compare(control1.pressed, true)
+
+ touch.stationary(1).press(2, control2).commit()
+
+ compare(pressedChangedSpy2.count, ++pressedChangedCount2)
+ compare(pressedSpy2.count, ++pressedCount2)
+ compare(control2.pressed, true)
+
+ touch.release(1, control1).release(2, control2).commit()
compare(control1.pressed, false)
+ compare(control2.pressed, false)
+ compare(pressedSpy1.count, pressedCount1)
+ compare(pressedSpy2.count, pressedCount2)
}
function test_keys() {
diff --git a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
index 0491ab039e..2b0e90586d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
@@ -804,7 +804,7 @@ TestCase {
}
if (distanceToReachContentY != 0)
- mouseRelease(tumbler, tumblerXCenter(), itemCenterPos(1) + (data.contentY - defaultListViewTumblerOffset), Qt.LeftButton);
+ mouseRelease(tumbler, tumblerXCenter(), itemCenterPos(1).y + (data.contentY - defaultListViewTumblerOffset), Qt.LeftButton);
}
function test_listViewFlickAboveBounds_data() {
diff --git a/tests/auto/quickcontrols/palette/data/inheritPaletteForPopupWithinItemView.qml b/tests/auto/quickcontrols/palette/data/inheritPaletteForPopupWithinItemView.qml
new file mode 100644
index 0000000000..21e808aa2c
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/inheritPaletteForPopupWithinItemView.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 200
+ height: 200
+
+ property alias listView: listView
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: 1
+ delegate: Item {
+ property alias button: button
+ Popup {
+ id: popup
+ contentItem: Button {
+ id: button
+ text: "Test"
+ }
+ }
+ Component.onCompleted: { popup.open() }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/tst_palette.cpp b/tests/auto/quickcontrols/palette/tst_palette.cpp
index 621475b86e..ddb9d08ff2 100644
--- a/tests/auto/quickcontrols/palette/tst_palette.cpp
+++ b/tests/auto/quickcontrols/palette/tst_palette.cpp
@@ -66,6 +66,7 @@ private slots:
void comboBoxPopupWithThemeDefault();
void toolTipPaletteUpdate();
+ void inheritPaletteForPopupWithinItemView();
};
tst_palette::tst_palette()
@@ -610,6 +611,36 @@ void tst_palette::toolTipPaletteUpdate()
QCOMPARE(toolTipPalette->toolTipText(), windowPalette->toolTipText());
}
+void tst_palette::inheritPaletteForPopupWithinItemView()
+{
+ QQuickApplicationHelper helper(this, "inheritPaletteForPopupWithinItemView.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickItem *listView = window->property("listView").value<QQuickItem *>();
+ QVERIFY(listView);
+ QQuickItem *contentItem = listView->property("contentItem").value<QQuickItem *>();
+ QVERIFY(contentItem);
+ QQuickItem *item = contentItem->childItems().value(0);
+ QVERIFY(item);
+ auto *button = item->property("button").value<QQuickButton *>();
+ QVERIFY(button);
+
+ auto windowPalette = QQuickWindowPrivate::get(window)->palette();
+ auto buttonPalette = QQuickItemPrivate::get(button)->palette();
+
+ QCOMPARE(windowPalette->window(), buttonPalette->window());
+ QCOMPARE(windowPalette->buttonText(), buttonPalette->buttonText());
+
+ windowPalette->setWindow(Qt::red);
+ windowPalette->setButtonText(Qt::blue);
+
+ QCOMPARE(windowPalette->window(), buttonPalette->window());
+ QCOMPARE(windowPalette->buttonText(), buttonPalette->buttonText());
+}
+
QTEST_MAIN(tst_palette)
#include "tst_palette.moc"
diff --git a/tests/auto/quickcontrols/pointerhandlers/data/controlandmousearea.qml b/tests/auto/quickcontrols/pointerhandlers/data/controlandmousearea.qml
index 00c680453a..730b187e2b 100644
--- a/tests/auto/quickcontrols/pointerhandlers/data/controlandmousearea.qml
+++ b/tests/auto/quickcontrols/pointerhandlers/data/controlandmousearea.qml
@@ -12,6 +12,7 @@ Item {
MouseArea {
id: outerMouseArea
+ objectName: "outerMouseArea"
x: 10
y: 10
width: 200
@@ -25,14 +26,18 @@ Item {
Button {
id: buttonInTheMiddle
+ objectName: "buttonInTheMiddle"
width: parent.width - 20
height: parent.height - 20
anchors.right: parent.right
anchors.bottom: parent.bottom
text: hovered ? "hovered" : ""
+ // Explicitly set this, as it's false on platforms like Android.
+ hoverEnabled: true
MouseArea {
id: innerMouseArea
+ objectName: "innerMouseArea"
width: parent.width - 20
height: parent.height - 20
anchors.right: parent.right
diff --git a/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp b/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
index 025c0f838c..7c411d3658 100644
--- a/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
+++ b/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
@@ -51,6 +51,8 @@ tst_pointerhandlers::tst_pointerhandlers()
void tst_pointerhandlers::hover_controlInsideControl()
{
+ SKIP_IF_NO_MOUSE_HOVER;
+
// Test that if you move the mouse over a control that is
// a child of another control, both controls end up hovered.
// A control should basically not block (accept) hover events.
@@ -102,6 +104,8 @@ void tst_pointerhandlers::hover_controlInsideControl()
void tst_pointerhandlers::hover_controlAndMouseArea()
{
+ SKIP_IF_NO_MOUSE_HOVER;
+
QQuickView view(testFileUrl("controlandmousearea.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
diff --git a/tests/auto/quickcontrols/qquickcontrol/data/hoverInMouseArea.qml b/tests/auto/quickcontrols/qquickcontrol/data/hoverInMouseArea.qml
new file mode 100644
index 0000000000..e0ef55336b
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickcontrol/data/hoverInMouseArea.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias control: control
+ property alias textArea: textArea
+ property alias textField: textField
+
+ MouseArea {
+ anchors.fill: parent
+
+ Column {
+ Control {
+ id: control
+ implicitWidth: 100
+ implicitHeight: 30
+ hoverEnabled: true
+ background: Rectangle {
+ color: control.hovered ? "salmon" : "grey"
+ }
+ }
+
+ TextArea {
+ id: textArea
+ hoverEnabled: true
+ }
+
+ TextField {
+ id: textField
+ hoverEnabled: true
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp b/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
index ddb4b7ff8b..334cbed67e 100644
--- a/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
+++ b/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
@@ -7,6 +7,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
+#include <QtQuickTemplates2/private/qquicktextarea_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
#include <QtQuick/private/qquicktext_p_p.h>
@@ -24,6 +26,7 @@ private slots:
void flickable();
void fractionalFontSize();
void resizeBackgroundKeepsBindings();
+ void hoverInMouseArea();
private:
QScopedPointer<QPointingDevice> touchDevice;
@@ -106,6 +109,34 @@ void tst_QQuickControl::resizeBackgroundKeepsBindings()
QVERIFY(background->bindableHeight().hasBinding());
}
+void tst_QQuickControl::hoverInMouseArea()
+{
+ SKIP_IF_NO_MOUSE_HOVER;
+
+ QQuickApplicationHelper helper(this, QStringLiteral("hoverInMouseArea.qml"));
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ const auto *control = window->property("control").value<QQuickControl *>();
+ QVERIFY(control);
+ PointLerper pointLerper(window);
+ pointLerper.move(mapCenterToWindow(control));
+ // It's necessary to use PointLerper here,
+ // otherwise the isHovered checks are flaky on most platforms.
+ QVERIFY(control->isHovered());
+
+ const auto *textArea = window->property("textArea").value<QQuickTextArea *>();
+ QVERIFY(textArea);
+ pointLerper.move(mapCenterToWindow(textArea));
+ QVERIFY(textArea->isHovered());
+
+ const auto *textField = window->property("textField").value<QQuickTextField *>();
+ QVERIFY(textField);
+ pointLerper.move(mapCenterToWindow(textField));
+ QVERIFY(textField->isHovered());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickControl)
#include "tst_qquickcontrol.moc"
diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
index bec3a1f27c..041c3d7ab9 100644
--- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
@@ -146,11 +146,8 @@ void tst_QQuickMenu::count()
void tst_QQuickMenu::mouse()
{
- SKIP_IF_NO_WINDOW_ACTIVATION
-
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_WINDOW_ACTIVATION;
+ SKIP_IF_NO_MOUSE_HOVER;
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1332,9 +1329,7 @@ void tst_QQuickMenu::subMenuMouse_data()
void tst_QQuickMenu::subMenuMouse()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_MOUSE_HOVER;
QFETCH(bool, cascade);
@@ -1451,9 +1446,7 @@ void tst_QQuickMenu::subMenuDisabledMouse_data()
// QTBUG-69540
void tst_QQuickMenu::subMenuDisabledMouse()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_MOUSE_HOVER;
QFETCH(bool, cascade);
@@ -2021,9 +2014,7 @@ void tst_QQuickMenu::disableWhenTriggered_data()
// Tests that the menu is dismissed when a menu item sets "enabled = false" in onTriggered().
void tst_QQuickMenu::disableWhenTriggered()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_MOUSE_HOVER;
QFETCH(int, menuItemIndex);
QFETCH(int, subMenuItemIndex);
diff --git a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
index b331fda1a3..ee593e1701 100644
--- a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
@@ -67,11 +67,8 @@ void tst_qquickmenubar::delegate()
void tst_qquickmenubar::mouse()
{
- SKIP_IF_NO_WINDOW_ACTIVATION
-
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_WINDOW_ACTIVATION;
+ SKIP_IF_NO_MOUSE_HOVER;
QQmlApplicationEngine engine(testFileUrl("menubar.qml"));
@@ -720,9 +717,7 @@ void tst_qquickmenubar::addRemove()
void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
+ SKIP_IF_NO_MOUSE_HOVER;
QQmlApplicationEngine engine(testFileUrl("checkHighlightWhenDismissed.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index b046be8d44..966f908e80 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -1431,6 +1431,7 @@ void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
// The stack view should have two items, and it should pop the second when escape is pressed
@@ -1498,6 +1499,7 @@ void tst_QQuickPopup::closeOnEscapeWithVisiblePopup()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->findChild<QQuickPopup *>("popup");
@@ -1626,6 +1628,7 @@ void tst_QQuickPopup::disabledPalette()
QQuickWindow *window = helper.window;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
@@ -1665,6 +1668,7 @@ void tst_QQuickPopup::disabledParentPalette()
QQuickWindow *window = helper.window;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
@@ -1776,6 +1780,7 @@ void tst_QQuickPopup::tabFence()
QQuickWindow *window = helper.window;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("dialog").value<QQuickPopup*>();
@@ -1885,6 +1890,7 @@ void tst_QQuickPopup::destroyDuringExitTransition()
QQuickWindow *window = helper.window;
window->show();
+ window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
{
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/BLACKLIST b/tests/auto/quickcontrols/qquicktreeviewdelegate/BLACKLIST
deleted file mode 100644
index ed6d7fd2cf..0000000000
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/BLACKLIST
+++ /dev/null
@@ -1,6 +0,0 @@
-# perhaps related to QTBUG-103072
-[dragToSelect]
-android
-# perhaps related to QTBUG-103064
-[pressAndHoldToSelect]
-android
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml
new file mode 100644
index 0000000000..5b9bab708c
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Rectangle {
+ width: 480
+ height: 640
+ color: dialog.selectedColor
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(ColorDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(ColorDialog.Rejected)
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml
new file mode 100644
index 0000000000..b146d61513
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Rectangle {
+ width: 480
+ height: 640
+ color: dialog.selectedColor
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(ColorDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(ColorDialog.Rejected)
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ visible: true
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml
new file mode 100644
index 0000000000..5215feb243
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Window {
+ width: 480
+ height: 640
+
+ property alias dialog: dialog
+
+ function getSubWindow1 () {
+ return subwindow1
+ }
+
+ function getSubWindow2 () {
+ return subwindow2
+ }
+
+ function goToSubWindow1() {
+ dialog.close()
+ dialog.parentWindow = subwindow1
+ dialog.open()
+ }
+
+ function goToSubWindow2() {
+ dialog.close()
+ dialog.parentWindow = subwindow2
+ dialog.open()
+ }
+
+ function resetParentWindow() {
+ dialog.close()
+ dialog.parentWindow = undefined
+ dialog.open()
+ }
+
+ Window {
+ id: subwindow1
+ width: 480
+ height: 640
+ visible: true
+ }
+
+ Window {
+ id: subwindow2
+ width: 480
+ height: 640
+ visible: true
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp b/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
index c8a1eb2231..9a2bd299d3 100644
--- a/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
@@ -4,6 +4,7 @@
#include <QtTest/qtest.h>
#include <QtQuickTest/quicktest.h>
#include <QtTest/qsignalspy.h>
+#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/dialogstestutils_p.h>
@@ -53,6 +54,9 @@ private slots:
void changeColorFromTextFields();
void windowTitle_data();
void windowTitle();
+ void workingInsideQQuickViewer_data();
+ void workingInsideQQuickViewer();
+ void dialogCanMoveBetweenWindows();
private:
bool closePopup(DialogTestHelper<QQuickColorDialog, QQuickColorDialogImpl> *dialogHelper,
@@ -456,6 +460,67 @@ void tst_QQuickColorDialogImpl::windowTitle()
CLOSE_DIALOG("Ok");
}
+void tst_QQuickColorDialogImpl::workingInsideQQuickViewer_data()
+{
+ QTest::addColumn<bool>("initialVisible");
+ QTest::addColumn<QString>("urlToQmlFile");
+ QTest::newRow("visible: false") << false << "colorDialogWithoutWindow.qml";
+ QTest::newRow("visible: true") << true << "colorDialogWithoutWindowVisibleTrue.qml";
+}
+
+void tst_QQuickColorDialogImpl::workingInsideQQuickViewer() // QTBUG-106598
+{
+ QFETCH(bool, initialVisible);
+ QFETCH(QString, urlToQmlFile);
+
+ QQuickView dialogView;
+ dialogView.setSource(testFileUrl(urlToQmlFile));
+ dialogView.show();
+
+ auto dialog = dialogView.findChild<QQuickColorDialog *>("ColorDialog");
+ QVERIFY(dialog);
+ QCOMPARE(dialog->isVisible(), initialVisible);
+
+ dialog->open();
+ QVERIFY(dialog->isVisible());
+}
+
+void tst_QQuickColorDialogImpl::dialogCanMoveBetweenWindows()
+{
+ DialogTestHelper<QQuickColorDialog, QQuickColorDialogImpl> dialogHelper(this, "windowSwapping.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.quickDialog->parent(), dialogHelper.window());
+ QVariant subWindow1;
+ QVariant subWindow2;
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "getSubWindow1", Q_RETURN_ARG(QVariant, subWindow1));
+ QMetaObject::invokeMethod(dialogHelper.window(), "getSubWindow2", Q_RETURN_ARG(QVariant, subWindow2));
+
+ // Confirm that the dialog can swap to different windows
+ QMetaObject::invokeMethod(dialogHelper.window(), "goToSubWindow1");
+ QCOMPARE(dialogHelper.dialog->parentWindow(), qvariant_cast<QQuickWindow *>(subWindow1));
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "goToSubWindow2");
+ QCOMPARE(dialogHelper.dialog->parentWindow(), qvariant_cast<QQuickWindow *>(subWindow2));
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "resetParentWindow");
+ QTRY_COMPARE(dialogHelper.quickDialog->parent(), dialogHelper.window());
+
+ dialogHelper.quickDialog->contentItem()->window()->close();
+ QVERIFY(dialogHelper.openDialog());
+ dialogHelper.quickDialog->contentItem()->window()->show();
+
+ QVERIFY(QTest::qWaitForWindowActive(dialogHelper.quickDialog->contentItem()->window()));
+ QVERIFY(QQuickTest::qWaitForPolish(dialogHelper.quickDialog->contentItem()->window()));
+
+ CLOSE_DIALOG("Ok");
+}
+
QTEST_MAIN(tst_QQuickColorDialogImpl)
#include "tst_qquickcolordialogimpl.moc"
diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
index f64a44d66a..7a10eaba5b 100644
--- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
+++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
@@ -6,6 +6,7 @@
#include <QQmlEngine>
#include <QQmlComponent>
#include <QDebug>
+#include <QElapsedTimer>
// This benchmark produces performance statistics
// for the standard set of elements, properties and expressions which
diff --git a/examples/qml/qmldom/CMakeLists.txt b/tests/manual/qmldom/CMakeLists.txt
index 383f685bd9..8de61c2261 100644
--- a/examples/qml/qmldom/CMakeLists.txt
+++ b/tests/manual/qmldom/CMakeLists.txt
@@ -12,14 +12,12 @@ endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmldomloadeditwrite")
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml)
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml QmlDomPrivate)
add_compile_definitions(
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../../../tests/auto/qmldom/domdata"
)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../src/qmldom/standalone qmldom)
-
qt_add_executable(qmldomloadeditwrite
qmldomloadeditwrite.cpp
)
@@ -27,7 +25,7 @@ qt_add_executable(qmldomloadeditwrite
target_link_libraries(qmldomloadeditwrite PUBLIC
Qt6::Core
Qt6::Qml
- qmldomlib
+ Qt::QmlDomPrivate
)
install(TARGETS qmldomloadeditwrite
diff --git a/examples/qml/qmldom/qmldomloadeditwrite.cpp b/tests/manual/qmldom/qmldomloadeditwrite.cpp
index 7c7609651b..68a6b993f3 100644
--- a/examples/qml/qmldom/qmldomloadeditwrite.cpp
+++ b/tests/manual/qmldom/qmldomloadeditwrite.cpp
@@ -1,15 +1,15 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// common declarations
-#include "qmldom/qqmldomitem_p.h"
+#include <QtQmlDom/private/qqmldomitem_p.h>
// comparisons of two DomItems
-#include "qmldom/qqmldomcompare_p.h"
+#include <QtQmlDom/private/qqmldomcompare_p.h>
// field filters to compare only selected fields (ignore for example location changes)
-#include "qmldom/qqmldomfieldfilter_p.h"
+#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-#include "qmldom/qqmldomelements_p.h"
+#include <QtQmlDom/private/qqmldomelements_p.h>
// cast of the top level items (DomEnvironments,...)
-#include "qmldom/qqmldomtop_p.h"
+#include <QtQmlDom/private/qqmldomtop_p.h>
#include <QtTest/QtTest>
#include <QCborValue>
diff --git a/tools/qmlcachegen/CMakeLists.txt b/tools/qmlcachegen/CMakeLists.txt
index 3f58390f2d..5923937364 100644
--- a/tools/qmlcachegen/CMakeLists.txt
+++ b/tools/qmlcachegen/CMakeLists.txt
@@ -1,8 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmlcachegen.pro.
-
#####################################################################
## qmlcachegen Tool:
#####################################################################
@@ -20,37 +18,7 @@ qt_internal_add_tool(${target_name}
LIBRARIES
Qt::QmlCompilerPrivate
)
-qt_internal_return_unless_building_tools()
-
-#### Keys ignored in scope 1:.:.:qmlcachegen.pro:<TRUE>:
-# CMAKE_BIN_DIR = "$$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])"
-# QMAKE_SUBSTITUTES = "cmake_config_file"
-# QMAKE_TARGET_DESCRIPTION = "QML" "Cache" "Generator"
-# _OPTION = "host_build"
-# build_integration.files = "qmlcache.prf" "qtquickcompiler.prf"
-# build_integration.path = "$$[QT_HOST_DATA]/mkspecs/features"
-# cmake_build_integration.files = "$$cmake_config_file.output"
-# cmake_build_integration.path = "$$[QT_INSTALL_LIBS]/cmake/Qt6QuickCompiler"
-# cmake_config_file.input = "$$PWD/Qt6QuickCompilerConfig.cmake.in"
-# cmake_config_file.output = "$$MODULE_BASE_OUTDIR/lib/cmake/Qt6QuickCompiler/Qt6QuickCompilerConfig.cmake"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:qmlcachegen.pro:prefix_build:
-# INSTALLS = "cmake_build_integration" "build_integration"
-#### Keys ignored in scope 3:.:.:qmlcachegen.pro:else:
-# COPIES = "cmake_build_integration" "build_integration"
-
-#### Keys ignored in scope 4:.:.:qmlcachegen.pro:CMAKE_BIN_DIR___contains___^\\.\\./._x_:
-# CMAKE_BIN_DIR = "$$[QT_HOST_BINS]/"
-# CMAKE_BIN_DIR_IS_ABSOLUTE = "True"
-
-#### Keys ignored in scope 5:.:.:qmlcachegen.pro:QMAKE_HOST.os___equals___Windows:
-# CMAKE_BIN_SUFFIX = ".exe"
-
-# special case begin
# Install public prf files.
set(qmlcachegen_mkspecs
"${CMAKE_CURRENT_SOURCE_DIR}/qmlcache.prf"
@@ -60,4 +28,5 @@ set(mkspecs_install_dir "${INSTALL_MKSPECSDIR}")
qt_path_join(mkspecs_install_dir "${QT_INSTALL_DIR}" "${mkspecs_install_dir}" "features")
qt_copy_or_install(FILES "${qmlcachegen_mkspecs}"
DESTINATION ${mkspecs_install_dir})
-# special case end
+
+qt_internal_return_unless_building_tools()