From bc63196948baa60330b0f193eb7b4350f23e410e Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Fri, 18 Mar 2022 14:16:09 +0100 Subject: [PATCH] qmlcompiler: Improve grouped property support Still lacks support for merging and resolving grouped bindings, but this will be handled in another patch. Task-number: QTBUG-100168 Original-patch-by: Fabian Kosmale Change-Id: I1bb73f383acc3c8512676db47c5944f369b904b7 Reviewed-by: Andrei Golubev --- src/qmlcompiler/qqmljsimportvisitor.cpp | 37 +++++++++++++++++-- src/qmlcompiler/qqmljsmetatypes_p.h | 24 +++++++++++- tests/auto/qml/qqmljsscope/data/GroupBase.qml | 5 +++ .../qml/qqmljsscope/data/groupProperties.qml | 5 +++ .../auto/qml/qqmljsscope/tst_qqmljsscope.cpp | 14 +++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qqmljsscope/data/GroupBase.qml create mode 100644 tests/auto/qml/qqmljsscope/data/groupProperties.qml diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index deb6b22331..94ab9b19ee 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -1574,6 +1574,24 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri m_scopesById.insert(name, m_currentScope); } +inline void createGroupBinding(QQmlJSScope::Ptr &scope, const QString &name, + const QQmlJS::SourceLocation &srcLocation) +{ + auto parentScope = scope->parentScope(); + const auto propertyBindings = parentScope->ownPropertyBindings(name); + const bool alreadyHasGroupedBinding = std::any_of( + propertyBindings.first, propertyBindings.second, + [](const QQmlJSMetaPropertyBinding &binding) { + return binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty; + }); + if (alreadyHasGroupedBinding) + return; + + QQmlJSMetaPropertyBinding groupBinding(srcLocation, name); + groupBinding.setGroupBinding(static_cast>(scope)); + parentScope->addOwnPropertyBinding(std::move(groupBinding)); +} + bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) { m_savedBindingOuterScope = m_currentScope; @@ -1589,9 +1607,17 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) if (name.isEmpty()) break; - enterEnvironmentNonUnique(name.front().isUpper() ? QQmlJSScope::AttachedPropertyScope - : QQmlJSScope::GroupedPropertyScope, - name, group->firstSourceLocation()); + const bool isAttachedProperty = name.front().isUpper(); + if (isAttachedProperty) { + // attached property + enterEnvironmentNonUnique(QQmlJSScope::AttachedPropertyScope, + name, group->firstSourceLocation()); + } else { + // grouped property + enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, + name, group->firstSourceLocation()); + createGroupBinding(m_currentScope, name, group->firstSourceLocation()); + } } auto name = group->name; @@ -1982,7 +2008,12 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope : QQmlJSScope::GroupedPropertyScope; + bool exists = enterEnvironmentNonUnique(scopeKind, idName, group->firstSourceLocation()); + + if (scopeKind == QQmlJSScope::GroupedPropertyScope) + createGroupBinding(m_currentScope, idName, group->firstSourceLocation()); + ++scopesEnteredCounter; needsResolution = needsResolution || !exists; } diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index b1b11c3213..18ffe8c8db 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -466,7 +466,23 @@ private: friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); } }; struct GroupProperty { - friend bool operator==(GroupProperty , GroupProperty ) { return true; } + /* Given a group property declaration like + anchors.left: root.left + the QQmlJSMetaPropertyBinding will have name "anchors", and a m_bindingContent + of type GroupProperty, with groupScope pointing to the scope introudced by anchors + In that scope, there will be another QQmlJSMetaPropertyBinding, with name "left" and + m_bindingContent Script (for root.left). + There should never be more than one GroupProperty for the same name in the same + scope, though: If the scope also contains anchors.top: root.top that should reuse the + GroupProperty content (and add a top: root.top binding in it). There might however + still be an additional object or script binding ( anchors: {left: foo, right: bar }; + anchors: root.someFunction() ) or another binding to the property in a "derived" + type. + + ### TODO: Obtaining the effective binding result requires some resolving function + */ + QWeakPointer groupScope; + friend bool operator==(GroupProperty a, GroupProperty b) { return a.groupScope == b.groupScope; } friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); } }; using type = std::variant &groupScope) + { + ensureSetBindingTypeOnce(); + m_bindingContent = Content::GroupProperty { groupScope }; + } + void setBoolLiteral(bool value) { ensureSetBindingTypeOnce(); diff --git a/tests/auto/qml/qqmljsscope/data/GroupBase.qml b/tests/auto/qml/qqmljsscope/data/GroupBase.qml new file mode 100644 index 0000000000..4b4c49cbd6 --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/GroupBase.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + anchors.top: parent.top +} diff --git a/tests/auto/qml/qqmljsscope/data/groupProperties.qml b/tests/auto/qml/qqmljsscope/data/groupProperties.qml new file mode 100644 index 0000000000..312067c901 --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/groupProperties.qml @@ -0,0 +1,5 @@ +import QtQuick + +GroupBase { + anchors.left: parent.right +} diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp index 5fa29af236..ba75ea7e2d 100644 --- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp +++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include class tst_qqmljsscope : public QQmlDataTest { @@ -95,11 +97,13 @@ private Q_SLOTS: void signalCreationDifferences(); void allTypesAvailable(); void shadowing(); + #ifdef LABS_QML_MODELS_PRESENT void componentWrappedObjects(); void labsQmlModelsSanity(); #endif void unknownCppBase(); + void groupedProperties(); public: tst_qqmljsscope() @@ -262,5 +266,15 @@ void tst_qqmljsscope::unknownCppBase() // we should not crash here, then it is a success } +void tst_qqmljsscope::groupedProperties() +{ + QQmlJSScope::ConstPtr root = run(u"groupProperties.qml"_qs); + QVERIFY(root); + + QVERIFY(root->hasProperty(u"anchors"_qs)); + auto anchorBindings = root->propertyBindings(u"anchors"_qs); + QVERIFY(!anchorBindings.isEmpty()); +} + QTEST_MAIN(tst_qqmljsscope) #include "tst_qqmljsscope.moc"