From 513d1e7e02c1cc48daa74522f837aa0c0e2c716b Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 18 Aug 2020 17:52:44 +0200 Subject: QmlDesigner: Improve connection editor dialog * Add ComboBoxes to connection editor dialog * Add type toggle to dialog * Add slots and properties to ComboBoxes * Parse connection expression and fill ComboBoxes Task-number: QDS-2498 Task-number: QDS-2495 Task-number: QDS-2496 Change-Id: I2cca6d4c85d1508e54d4ad8863056f22ad777ae6 Reviewed-by: Thomas Hartmann --- .../components/bindingeditor/actioneditor.cpp | 183 +++++++++++++++++++-- 1 file changed, 170 insertions(+), 13 deletions(-) (limited to 'src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp') diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index ac86377816..486e33640d 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,14 @@ #include #include +#include +#include + +#include +#include + +static Q_LOGGING_CATEGORY(ceLog, "qtc.qmldesigner.connectioneditor", QtWarningMsg) + namespace QmlDesigner { static ActionEditor *s_lastActionEditor = nullptr; @@ -58,15 +67,14 @@ void ActionEditor::prepareDialog() { if (s_lastActionEditor) s_lastActionEditor->hideWidget(); - s_lastActionEditor = this; - m_dialog = new BindingEditorDialog(Core::ICore::dialogParent(), - BindingEditorDialog::DialogType::ActionDialog); + s_lastActionEditor = this; + m_dialog = new ActionEditorDialog(Core::ICore::dialogParent()); - QObject::connect(m_dialog, &BindingEditorDialog::accepted, + QObject::connect(m_dialog, &AbstractEditorDialog::accepted, this, &ActionEditor::accepted); - QObject::connect(m_dialog, &BindingEditorDialog::rejected, + QObject::connect(m_dialog, &AbstractEditorDialog::rejected, this, &ActionEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -88,14 +96,14 @@ void ActionEditor::hideWidget() { if (s_lastActionEditor == this) s_lastActionEditor = nullptr; - if (m_dialog) - { - m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override + + if (m_dialog) { + m_dialog->unregisterAutoCompletion(); // we have to do it separately, otherwise we have an autocompletion action override m_dialog->close(); } } -QString ActionEditor::bindingValue() const +QString ActionEditor::connectionValue() const { if (!m_dialog) return {}; @@ -103,7 +111,7 @@ QString ActionEditor::bindingValue() const return m_dialog->editorValue(); } -void ActionEditor::setBindingValue(const QString &text) +void ActionEditor::setConnectionValue(const QString &text) { if (m_dialog) m_dialog->setEditorValue(text); @@ -129,11 +137,160 @@ void ActionEditor::setModelIndex(const QModelIndex &index) m_index = index; } +void ActionEditor::setModelNode(const ModelNode &modelNode) +{ + if (modelNode.isValid()) + m_modelNode = modelNode; +} + +bool isLiteral(QmlJS::AST::Node *ast) +{ + if (QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast)) + return true; + else + return false; +} + +void ActionEditor::prepareConnections() +{ + if (!m_modelNode.isValid()) + return; + + BindingEditorWidget *bindingEditorWidget = m_dialog->bindingEditorWidget(); + + if (!bindingEditorWidget) { + qCInfo(ceLog) << Q_FUNC_INFO << "BindingEditorWidget is missing!"; + return; + } + + if (!bindingEditorWidget->qmlJsEditorDocument()) { + qCInfo(ceLog) << Q_FUNC_INFO << "QmlJsEditorDocument is missing!"; + return; + } + + // Prepare objects for analysing slots + const QmlJSTools::SemanticInfo &semanticInfo = bindingEditorWidget->qmljsdocument->semanticInfo(); + const QList path = semanticInfo.rangePath(0); + const QmlJS::ContextPtr &context = semanticInfo.context; + const QmlJS::ScopeChain &scopeChain = semanticInfo.scopeChain(path); + + static QList typeWhiteList({"string", + "real", "int", "double", + "bool", + "QColor", "color", + "QtQuick.Item", "QQuickItem"}); + + static QList methodBlackList({"toString", "destroy"}); + + QList connections; + QList singletons; + QStringList states; + + const QList allNodes = m_modelNode.view()->allModelNodes(); + for (const auto &modelNode : allNodes) { + // Skip nodes without specified id + if (!modelNode.hasId()) + continue; + + ActionEditorDialog::ConnectionOption connection(modelNode.id()); + + for (const auto &propertyName : modelNode.metaInfo().propertyNames()) { + if (!typeWhiteList.contains(modelNode.metaInfo().propertyTypeName(propertyName))) + continue; + + const QString name = QString::fromUtf8(propertyName); + TypeName type = modelNode.metaInfo().propertyTypeName(propertyName); + if (type.contains(".")) + type.remove(0, 6); + + connection.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + + for (const VariantProperty &variantProperty : modelNode.variantProperties()) { + if (variantProperty.isValid()) { + if (variantProperty.isDynamic()) { + if (!typeWhiteList.contains(variantProperty.dynamicTypeName())) + continue; + + const QString name = QString::fromUtf8(variantProperty.name()); + TypeName type = variantProperty.dynamicTypeName(); + if (type.contains(".")) + type.remove(0, 6); + + connection.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + } + } + + for (const auto &slotName : modelNode.metaInfo().slotNames()) { + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create( + QLatin1String(""), QmlJS::Dialect::JavaScript); + newDoc->setSource(QLatin1String(slotName)); + newDoc->parseExpression(); + + QmlJS::AST::ExpressionNode *expression = newDoc->expression(); + + if (expression && !isLiteral(expression)) { + QmlJS::ValueOwner *interp = context->valueOwner(); + const QmlJS::Value *value = interp->convertToObject(scopeChain.evaluate(expression)); + + if (const QmlJS::FunctionValue *f = value->asFunctionValue()) { + // Only add slots with zero arguments + if (f->namedArgumentCount() == 0 && !methodBlackList.contains(slotName)) + connection.methods.append(QString::fromUtf8(slotName)); + } + } + } + + connection.methods.removeDuplicates(); + connections.append(connection); + } + + // Singletons + if (RewriterView *rv = m_modelNode.view()->rewriterView()) { + for (const QmlTypeData &data : rv->getQMLTypes()) { + if (!data.typeName.isEmpty()) { + NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo(data.typeName.toUtf8()); + if (metaInfo.isValid()) { + ActionEditorDialog::SingletonOption singelton; + for (const PropertyName &propertyName : metaInfo.propertyNames()) { + TypeName type = metaInfo.propertyTypeName(propertyName); + + if (!typeWhiteList.contains(type)) + continue; + + const QString name = QString::fromUtf8(propertyName); + if (type.contains(".")) + type.remove(0, 6); + + singelton.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + + if (!singelton.properties.isEmpty()) { + singelton.item = data.typeName; + singletons.append(singelton); + } + } + } + } + } + + // States + for (const QmlModelState &state : QmlItemNode(m_modelNode).states().allStates()) + states.append(state.name()); + + + if (!connections.isEmpty() && !m_dialog.isNull()) + m_dialog->setAllConnections(connections, singletons, states); +} + void ActionEditor::updateWindowName() { - if (!m_dialog.isNull()) - { - m_dialog->setWindowTitle(tr("Connection Editor")); + if (!m_dialog.isNull()) { + m_dialog->setWindowTitle(m_dialog->defaultTitle()); m_dialog->raise(); } } -- cgit v1.2.3