From 66122492a6358b07b041096b24e87241a9ae9696 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 15 Jul 2020 15:47:13 +0200 Subject: [PATCH] QmlDesigner: Extend Connections view support - Connection view support to components - Connection view support to singletons - Minor reparenting improvements Task: QDS-2411 Change-Id: I337535012dbb3d3a1722d75d89156463eabb8a4c Reviewed-by: Vikas Pachdha --- .../connectioneditor/connectionmodel.cpp | 112 +++++++++++++----- .../components/connectioneditor/delegates.cpp | 48 ++++++-- .../designercore/include/nodemetainfo.h | 2 + .../designercore/metainfo/nodemetainfo.cpp | 13 +- 4 files changed, 135 insertions(+), 40 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 4bafa046474..04c6c343dc5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -221,6 +221,13 @@ void ConnectionModel::updateTargetNode(int rowNumber) isSingleton = true; break; } + } else if (isAlias) { + if (data.typeName == newTarget.split(".").constFirst()) { + if (connectionView()->model()->metaInfo(data.typeName.toUtf8()).isValid()) { + isSingleton = true; + break; + } + } } } } @@ -229,7 +236,7 @@ void ConnectionModel::updateTargetNode(int rowNumber) if (!newTarget.isEmpty()) { //if it's a singleton, then let's reparent connections to rootNode, //if it's an alias, then reparent to alias property owner: - const ModelNode parent = connectionView()->modelNodeForId(isSingleton + const ModelNode parent = connectionView()->modelNodeForId((isSingleton || (isSingleton && isAlias)) ? connectionView()->rootModelNode().id() : isAlias ? newTarget.split(".").constFirst() @@ -255,35 +262,36 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const { - BindingProperty bindingProperty = connection.bindingProperty("target"); + ModelNode result; + + const BindingProperty bindingProperty = connection.bindingProperty("target"); + const QString bindExpression = bindingProperty.expression(); if (bindingProperty.isValid()) { - bool isAlias = bindingProperty.expression().contains("."); - - if (bindingProperty.expression() == QLatin1String("parent")) - return connection.parentProperty().parentModelNode(); - else if (isAlias) { - QStringList substr = bindingProperty.expression().split("."); + if (bindExpression == QLatin1String("parent")) { + result = connection.parentProperty().parentModelNode(); + } else if (bindExpression.contains(".")) { + QStringList substr = bindExpression.split("."); + const QString itemId = substr.constFirst(); if (substr.size() > 1) { - ModelNode aliasParent = connectionView()->modelNodeForId(substr.constFirst()); - QString aliasBody = substr.at(1); - if (aliasParent.hasProperty(aliasBody.toUtf8())) { - AbstractProperty abstractProp = aliasParent.property(aliasBody.toUtf8()); - if (abstractProp.isBindingProperty()) { - BindingProperty binding = abstractProp.toBindingProperty(); - if (connectionView()->hasId(binding.expression())) { - ModelNode resolve = connectionView()->modelNodeForId(binding.expression()); - if (resolve.isValid()) - return resolve; - } + const ModelNode aliasParent = (itemId == QLatin1String("parent") + ? connection.parentProperty().parentModelNode() + : connectionView()->modelNodeForId(itemId)); + substr.removeFirst(); //remove id, only alias pieces left + const QString aliasBody = substr.join("."); + if (aliasParent.isValid() && aliasParent.hasBindingProperty(aliasBody.toUtf8())) { + const BindingProperty binding = aliasParent.bindingProperty(aliasBody.toUtf8()); + if (binding.isValid() && connectionView()->hasId(binding.expression())) { + result = connectionView()->modelNodeForId(binding.expression()); } } } + } else { + result = connectionView()->modelNodeForId(bindExpression); } - return connectionView()->modelNodeForId(bindingProperty.expression()); } - return ModelNode(); + return result; } void ConnectionModel::addConnection() @@ -355,8 +363,7 @@ void ConnectionModel::deleteConnectionByRow(int currentRow) if (allSignals.size() > 1) { if (allSignals.contains(targetSignal)) node.removeProperty(targetSignal.name()); - } - else { + } else { node.destroy(); } } @@ -448,21 +455,44 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co { QStringList stringList; - if (connection.isValid()) { + auto getAliasMetaSignals = [&](QString aliasPart, NodeMetaInfo metaInfo) { + if (metaInfo.isValid() && metaInfo.hasProperty(aliasPart.toUtf8())) { + NodeMetaInfo propertyMetaInfo = connectionView()->model()->metaInfo( + metaInfo.propertyTypeName(aliasPart.toUtf8())); + if (propertyMetaInfo.isValid()) { + return propertyNameListToStringList(propertyMetaInfo.signalNames()); + } + } + return QStringList(); + }; + if (connection.isValid()) { //separate check for singletons if (connection.hasBindingProperty("target")) { - BindingProperty bp = connection.bindingProperty("target"); + const BindingProperty bp = connection.bindingProperty("target"); if (bp.isValid()) { - if (RewriterView *rv = connectionView()->rewriterView()) { + const QString bindExpression = bp.expression(); + + if (const RewriterView * const rv = connectionView()->rewriterView()) { for (const QmlTypeData &data : rv->getQMLTypes()) { if (!data.typeName.isEmpty()) { - if (data.typeName == bp.expression()) { + if (data.typeName == bindExpression) { NodeMetaInfo metaInfo = connectionView()->model()->metaInfo(data.typeName.toUtf8()); if (metaInfo.isValid()) { - stringList.append(propertyNameListToStringList(metaInfo.signalNames())); - return stringList; + stringList << propertyNameListToStringList(metaInfo.signalNames()); + break; + } + } else if (bindExpression.contains(".")) { + //if it doesn't fit the same name, maybe it's an alias? + QStringList expression = bindExpression.split("."); + if ((expression.size() > 1) && (expression.constFirst() == data.typeName)) { + expression.removeFirst(); + + stringList << getAliasMetaSignals( + expression.join("."), + connectionView()->model()->metaInfo(data.typeName.toUtf8())); + break; } } } @@ -474,6 +504,30 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co ModelNode targetNode = getTargetNodeForConnection(connection); if (targetNode.isValid() && targetNode.metaInfo().isValid()) { stringList.append(propertyNameListToStringList(targetNode.metaInfo().signalNames())); + } else { + //most likely it's component's internal alias: + + if (connection.hasBindingProperty("target")) { + const BindingProperty bp = connection.bindingProperty("target"); + + if (bp.isValid()) { + const QString bindExpression = bp.expression(); + QStringList expression = bp.expression().split("."); + if (expression.size() > 1) { + const QString itemId = expression.constFirst(); + if (connectionView()->hasId(itemId)) { + ModelNode parentItem = connectionView()->modelNodeForId(itemId); + if (parentItem.isValid() + && parentItem.hasMetaInfo() + && parentItem.metaInfo().isValid()) { + expression.removeFirst(); + stringList << getAliasMetaSignals(expression.join("."), + parentItem.metaInfo()); + } + } + } + } + } } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp index 864404047ee..83682d5d448 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp @@ -295,6 +295,35 @@ QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionVie switch (index.column()) { case ConnectionModel::TargetModelNodeRow: { + + auto addMetaInfoProperties = [&](const NodeMetaInfo& itemMetaInfo, QString itemName){ + if (itemMetaInfo.isValid()) { + for (const PropertyName &propertyName : itemMetaInfo.directPropertyNames()) { + TypeName propertyType = itemMetaInfo.propertyTypeName(propertyName); + if (!propertyType.isEmpty()) { + //first letter is a reliable item indicator + QChar firstLetter = QString::fromUtf8(propertyType).at(0); + if (firstLetter.isLetter() && firstLetter.isUpper()) { + if (!itemMetaInfo.propertyIsEnumType(propertyName) + && !itemMetaInfo.propertyIsPrivate(propertyName) + && !itemMetaInfo.propertyIsListProperty(propertyName) + && !itemMetaInfo.propertyIsPointer(propertyName)) { + NodeMetaInfo propertyMetaInfo = + connectionModel->connectionView()->model()->metaInfo(propertyType); + if (propertyMetaInfo.isValid()) { + if (propertyMetaInfo.isQmlItem()) { + connectionComboBox->addItem(itemName + + "." + + propertyName); + } + } + } + } + } + } + } + }; + for (const ModelNode &modelNode : connectionModel->connectionView()->allModelNodes()) { if (!modelNode.id().isEmpty()) { connectionComboBox->addItem(modelNode.id()); @@ -308,25 +337,24 @@ QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionVie } } } + + //Components + if (modelNode.isComponent()) { + NodeMetaInfo componentMetaInfo = modelNode.metaInfo(); + addMetaInfoProperties(componentMetaInfo, modelNode.id()); + } } } //singletons: if (RewriterView* rv = connectionModel->connectionView()->rewriterView()) { for (const QmlTypeData &data : rv->getQMLTypes()) { if (!data.typeName.isEmpty()) { + //singleton itself connectionComboBox->addItem(data.typeName); + //its properties, mostly looking for aliases: NodeMetaInfo metaInfo = connectionModel->connectionView()->model()->metaInfo(data.typeName.toUtf8()); - - if (metaInfo.isValid()) { - for (const PropertyName &propertyName : metaInfo.propertyNames()) { - if (metaInfo.propertyTypeName(propertyName) == "alias") { - connectionComboBox->addItem(data.typeName - + "." - + QString::fromUtf8(propertyName)); - } - } - } + addMetaInfoProperties(metaInfo, data.typeName); } } } diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 05a08d14675..b5e2e7626ca 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -75,6 +75,7 @@ public: bool propertyIsListProperty(const PropertyName &propertyName) const; bool propertyIsEnumType(const PropertyName &propertyName) const; bool propertyIsPrivate(const PropertyName &propertyName) const; + bool propertyIsPointer(const PropertyName &propertyName) const; QString propertyEnumScope(const PropertyName &propertyName) const; QStringList propertyKeysForEnum(const PropertyName &propertyName) const; QVariant propertyCastedValue(const PropertyName &propertyName, const QVariant &value) const; @@ -99,6 +100,7 @@ public: bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; bool isGraphicalItem() const; + bool isQmlItem() const; bool isLayoutable() const; bool isView() const; bool isTabView() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 3b64f74e0c0..afd2aaa9a59 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -427,7 +427,7 @@ QVector getQmlTypes(const CppComponentValue *objectValue, const Co PropertyMemberProcessor processor(context); objectValue->processMembers(&processor); - foreach (const PropertyInfo &property, processor.properties()) { + for (const PropertyInfo &property : processor.properties()) { const PropertyName name = property.first; const QString nameAsString = QString::fromUtf8(name); if (!objectValue->isWritable(nameAsString) && objectValue->isPointer(nameAsString)) { @@ -1500,6 +1500,11 @@ bool NodeMetaInfo::propertyIsPrivate(const PropertyName &propertyName) const return propertyName.startsWith("__"); } +bool NodeMetaInfo::propertyIsPointer(const PropertyName &propertyName) const +{ + return m_privateData->isPropertyPointer(propertyName); +} + QString NodeMetaInfo::propertyEnumScope(const PropertyName &propertyName) const { return m_privateData->propertyEnumScope(propertyName); @@ -1660,6 +1665,12 @@ bool NodeMetaInfo::isGraphicalItem() const || isSubclassOf("QtQuick.Controls.Popup"); } +bool NodeMetaInfo::isQmlItem() const +{ + return isSubclassOf("QtQuick.QtObject") + || isSubclassOf("QtQml.QtObject"); +} + void NodeMetaInfo::clearCache() { Internal::NodeMetaInfoPrivate::clearCache();