From 759b560baba2d8fdc2e3f598475ff1c675c7791d Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 23 Aug 2023 12:12:07 +0200 Subject: [PATCH] Cleanup ConnectionEditor models Simplified interface for the BindingModel and DynamicPropertiesModel. Change-Id: I772f31be704afe2a43c6368aefab1b026b85ec8b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectionseditor/BindingsListView.qml | 2 - .../connectionseditor/PropertiesListView.qml | 2 - src/plugins/qmldesigner/CMakeLists.txt | 6 +- .../connectioneditor/bindingmodel.cpp | 788 +++------- .../connectioneditor/bindingmodel.h | 100 +- .../connectioneditor/bindingmodelitem.cpp | 55 + .../connectioneditor/bindingmodelitem.h | 35 + .../connectioneditorutils.cpp | 392 +++++ .../connectioneditor/connectioneditorutils.h | 44 + .../connectioneditor/connectionmodel.cpp | 4 - .../connectioneditor/connectionview.cpp | 120 +- .../connectioneditor/connectionview.h | 13 +- .../connectioneditor/connectionviewwidget.cpp | 614 -------- .../connectioneditor/connectionviewwidget.h | 94 -- .../connectioneditor/connectionviewwidget.ui | 265 ---- .../components/connectioneditor/delegates.cpp | 409 ----- .../components/connectioneditor/delegates.h | 78 - .../dynamicpropertiesitem.cpp | 74 + .../connectioneditor/dynamicpropertiesitem.h | 35 + .../dynamicpropertiesmodel.cpp | 1363 ++++------------- .../connectioneditor/dynamicpropertiesmodel.h | 148 +- .../materialeditor/materialeditorview.cpp | 12 +- .../dynamicpropertiesproxymodel.cpp | 54 +- .../textureeditor/textureeditorview.cpp | 12 +- 24 files changed, 1330 insertions(+), 3389 deletions(-) create mode 100644 src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/delegates.cpp delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/delegates.h create mode 100644 src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 68ace4b0ecf..d2252bba95b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -33,7 +33,6 @@ ListView { onCurrentIndexChanged: { root.currentIndex = root.model.currentIndex - dialog.backend.currentRow = root.currentIndex } // Number of columns @@ -79,7 +78,6 @@ ListView { onClicked: { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 90b37aec7fa..bf1ed237cb5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -33,7 +33,6 @@ ListView { onCurrentIndexChanged: { root.currentIndex = root.model.currentIndex - dialog.backend.currentRow = root.currentIndex } // Number of columns @@ -81,7 +80,6 @@ ListView { function onClicked() { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index d3f6cd599f3..09d630ade83 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -485,7 +485,6 @@ add_qtc_plugin(QmlDesigner editorproxy.cpp editorproxy.h EXPLICIT_MOC components/propertyeditor/propertyeditorvalue.h - components/connectioneditor/connectionviewwidget.h qmldesignerplugin.h EXTRA_TRANSLATIONS "${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner" @@ -940,14 +939,15 @@ extend_qtc_plugin(QmlDesigner addnewbackenddialog.cpp addnewbackenddialog.h addnewbackenddialog.ui backendmodel.cpp backendmodel.h bindingmodel.cpp bindingmodel.h + bindingmodelitem.cpp bindingmodelitem.h connectioneditor.qrc connectioneditorevaluator.cpp connectioneditorevaluator.h connectioneditorstatements.cpp connectioneditorstatements.h connectionmodel.cpp connectionmodel.h connectionview.cpp connectionview.h - connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui - delegates.cpp delegates.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h + dynamicpropertiesitem.cpp dynamicpropertiesitem.h + connectioneditorutils.cpp connectioneditorutils.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h propertytreemodel.cpp propertytreemodel.h ) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 4fc3b81c310..69f6b5cc127 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -2,111 +2,27 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "bindingmodel.h" - +#include "bindingmodelitem.h" #include "connectionview.h" +#include "connectioneditorutils.h" +#include #include #include -#include -#include -#include #include -#include +#include +#include #include -#include -#include - namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *parent) - : QStandardItemModel(parent), m_connectionView(parent), - m_delegate(new BindingModelBackendDelegate(this)) + : QStandardItemModel(parent) + , m_connectionView(parent) + , m_delegate(new BindingModelBackendDelegate(this)) { - connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged); -} - -void BindingModel::resetModel() -{ - beginResetModel(); - clear(); - setHorizontalHeaderLabels( - QStringList({tr("Item"), tr("Property"), tr("Source Item"), tr("Source Property")})); - - if (connectionView()->isAttached()) { - for (const ModelNode &modelNode : connectionView()->selectedModelNodes()) - addModelNode(modelNode); - } - - endResetModel(); -} - -void BindingModel::add() -{ - addBindingForCurrentNode(); -} - -void BindingModel::remove(int row) -{ - deleteBindindByRow(row); -} - -int BindingModel::currentIndex() const -{ - return m_currentIndex; -} - -void BindingModel::setCurrentIndex(int i) -{ - if (m_currentIndex == i) - return; - - m_currentIndex = i; - - emit currentIndexChanged(); -} - -void BindingModel::bindingChanged(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - QList selectedNodes = connectionView()->selectedModelNodes(); - if (!selectedNodes.contains(bindingProperty.parentModelNode())) - return; - if (!m_lock) { - int rowNumber = findRowForBinding(bindingProperty); - - if (rowNumber == -1) { - addBindingProperty(bindingProperty); - } else { - updateBindingProperty(rowNumber); - } - } - - m_handleDataChanged = true; -} - -void BindingModel::bindingRemoved(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - QList selectedNodes = connectionView()->selectedModelNodes(); - if (!selectedNodes.contains(bindingProperty.parentModelNode())) - return; - if (!m_lock) { - int rowNumber = findRowForBinding(bindingProperty); - removeRow(rowNumber); - } - - m_handleDataChanged = true; -} - -void BindingModel::selectionChanged([[maybe_unused]] const QList &selectedNodes) -{ - m_handleDataChanged = false; - resetModel(); - m_handleDataChanged = true; + setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } ConnectionView *BindingModel::connectionView() const @@ -114,465 +30,235 @@ ConnectionView *BindingModel::connectionView() const return m_connectionView; } -BindingProperty BindingModel::bindingPropertyForRow(int rowNumber) const -{ - - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.bindingProperty(targetPropertyName.toLatin1()); - - return BindingProperty(); -} - -QStringList BindingModel::possibleTargetProperties(const BindingProperty &bindingProperty) const -{ - const ModelNode modelNode = bindingProperty.parentModelNode(); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node"; - return QStringList(); - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - const auto properties = metaInfo.properties(); - QStringList writableProperties; - writableProperties.reserve(static_cast(properties.size())); - for (const auto &property : properties) { - if (property.isWritable()) - writableProperties.push_back(QString::fromUtf8(property.name())); - } - - return writableProperties; - } - - return QStringList(); -} - -QStringList BindingModel::possibleSourceProperties(const BindingProperty &bindingProperty) const -{ - const QString expression = bindingProperty.expression(); - const QStringList stringlist = expression.split(QLatin1String(".")); - QStringList possibleProperties; - - NodeMetaInfo type; - - if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) - type = metaInfo.property(bindingProperty.name()).propertyType(); - else - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node"; - - const QString &id = stringlist.constFirst(); - - ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode()); - - if (!modelNode.isValid()) { - //if it's not a valid model node, maybe it's a singleton - if (RewriterView* rv = connectionView()->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty() && data.typeName == id) { - NodeMetaInfo metaInfo = connectionView()->model()->metaInfo(data.typeName.toUtf8()); - - if (metaInfo.isValid()) { - for (const auto &property : metaInfo.properties()) { - //without check for now - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - - return possibleProperties; - } - } - } - } - - qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node"; - return QStringList(); - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - for (const VariantProperty &variantProperty : modelNode.variantProperties()) { - if (variantProperty.isDynamic()) - possibleProperties << QString::fromUtf8(variantProperty.name()); - } - - for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) { - if (bindingProperty.isDynamic()) - possibleProperties << QString::fromUtf8((bindingProperty.name())); - } - - if (metaInfo.isValid()) { - for (const auto &property : metaInfo.properties()) { - if (property.propertyType() == type) //### todo proper check - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - } else { - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node"; - } - - return possibleProperties; -} - -void BindingModel::deleteBindindByRow(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); - } - - resetModel(); -} - -static PropertyName unusedProperty(const ModelNode &modelNode) -{ - PropertyName propertyName = "none"; - if (modelNode.metaInfo().isValid()) { - for (const auto &property : modelNode.metaInfo().properties()) { - if (property.isWritable() && !modelNode.hasProperty(propertyName)) - return property.name(); - } - } - - return propertyName; -} - -void BindingModel::addBindingForCurrentNode() -{ - if (connectionView()->selectedModelNodes().size() == 1) { - const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst(); - if (modelNode.isValid()) { - try { - modelNode.bindingProperty(unusedProperty(modelNode)).setExpression(QLatin1String("none.none")); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &BindingModel::handleException); - } - } - } else { - qWarning() << " BindingModel::addBindingForCurrentNode not one node selected"; - } -} - -static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property) -{ - item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole); - item->setData(property.name(), BindingModel::TargetPropertyNameRole); - - const AbstractProperty source = property.resolveToProperty(); - - if (source.isValid()) { - item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole); - item->setData(source.name(), BindingModel::SourcePropertyNameRole); - } -} - -void BindingModel::addBindingProperty(const BindingProperty &property) -{ - QStandardItem *idItem; - QStandardItem *targetPropertyNameItem; - QStandardItem *sourceIdItem; - QStandardItem *sourcePropertyNameItem; - - QString idLabel = property.parentModelNode().id(); - if (idLabel.isEmpty()) - idLabel = property.parentModelNode().simplifiedTypeName(); - idItem = new QStandardItem(idLabel); - updateCustomData(idItem, property); - targetPropertyNameItem = new QStandardItem(QString::fromUtf8(property.name())); - QList items; - - items.append(idItem); - updateDisplayRoles(idItem, property); - items.append(targetPropertyNameItem); - - QString sourceNodeName; - QString sourcePropertyName; - getExpressionStrings(property, &sourceNodeName, &sourcePropertyName); - - sourceIdItem = new QStandardItem(sourceNodeName); - sourcePropertyNameItem = new QStandardItem(sourcePropertyName); - - items.append(sourceIdItem); - items.append(sourcePropertyNameItem); - appendRow(items); -} - -void BindingModel::updateBindingProperty(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - QStandardItem *idItem = item(rowNumber, 0); - if (idItem) - updateDisplayRoles(idItem, bindingProperty); - - QString targetPropertyName = QString::fromUtf8(bindingProperty.name()); - updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName); - QString sourceNodeName; - QString sourcePropertyName; - getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); - updateDisplayRole(rowNumber, SourceModelNodeRow, sourceNodeName); - updateDisplayRole(rowNumber, SourcePropertyNameRow, sourcePropertyName); - } -} - -void BindingModel::addModelNode(const ModelNode &modelNode) -{ - const QList bindingProperties = modelNode.bindingProperties(); - for (const BindingProperty &bindingProperty : bindingProperties) { - addBindingProperty(bindingProperty); - } -} - -void BindingModel::updateExpression(int row) -{ - const QString sourceNode = data(index(row, SourceModelNodeRow)).toString().trimmed(); - const QString sourceProperty = data(index(row, SourcePropertyNameRow)).toString().trimmed(); - - QString expression; - if (sourceProperty.isEmpty()) { - expression = sourceNode; - } else { - expression = sourceNode + QLatin1String(".") + sourceProperty; - } - - connectionView()->executeInTransaction("BindingModel::updateExpression", [this, row, expression](){ - BindingProperty bindingProperty = bindingPropertyForRow(row); - bindingProperty.setExpression(expression.trimmed()); - }); -} - -void BindingModel::updatePropertyName(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toUtf8(); - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); - ModelNode targetNode = bindingProperty.parentModelNode(); - - if (!newName.isEmpty()) { - RewriterTransaction transaction = - connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updatePropertyName")); - try { - if (bindingProperty.isDynamic()) { - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression); - } else { - targetNode.bindingProperty(newName).setExpression(expression); - } - targetNode.removeProperty(bindingProperty.name()); - transaction.commit(); //committing in the try block - } catch (Exception &e) { //better save then sorry - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &BindingModel::handleException); - } - - QStandardItem* idItem = item(rowNumber, 0); - BindingProperty newBindingProperty = targetNode.bindingProperty(newName); - updateCustomData(idItem, newBindingProperty); - - } else { - qWarning() << "BindingModel::updatePropertyName invalid property name"; - } -} - -ModelNode BindingModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const -{ - ModelNode modelNode; - - if (id != QLatin1String("parent")) { - modelNode = connectionView()->modelNodeForId(id); - } else { - if (targetNode.hasParentProperty()) { - modelNode = targetNode.parentProperty().parentModelNode(); - } - } - return modelNode; -} - -void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty) -{ - item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1); - item->setData(bindingProperty.name(), Qt::UserRole + 2); - updateDisplayRoles(item, bindingProperty); -} - -int BindingModel::findRowForBinding(const BindingProperty &bindingProperty) -{ - for (int i=0; i < rowCount(); i++) { - if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty)) - return i; - } - //not found - return -1; -} - -bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty) -{ - //TODO reimplement using existing helper functions - - //### todo we assume no expressions yet - - const QString expression = bindingProperty.expression(); - - if (true) { - const QStringList stringList = expression.split(QLatin1String(".")); - - *sourceNode = stringList.constFirst(); - - QString propertyName; - - for (int i = 1; i < stringList.size(); i++) { - propertyName += stringList.at(i); - if (i != stringList.size() - 1) - propertyName += QLatin1String("."); - } - *sourceProperty = propertyName; - } - return true; -} - -void BindingModel::updateDisplayRole(int row, int columns, const QString &string) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex).toString() != string) - setData(modelIndex, string); -} - -void BindingModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - if (!m_handleDataChanged) - return; - - if (topLeft != bottomRight) { - qWarning() << "BindingModel::handleDataChanged multi edit?"; - return; - } - - m_lock = true; - - int currentColumn = topLeft.column(); - int currentRow = topLeft.row(); - - switch (currentColumn) { - case TargetModelNodeRow: { - //updating user data - } break; - case TargetPropertyNameRow: { - updatePropertyName(currentRow); - } break; - case SourceModelNodeRow: { - updateExpression(currentRow); - } break; - case SourcePropertyNameRow: { - updateExpression(currentRow); - } break; - - default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn; - } - - m_lock = false; -} - -void BindingModel::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - resetModel(); -} - -QHash BindingModel::roleNames() const -{ - static QHash roleNames{{TargetNameRole, "target"}, - {TargetPropertyNameRole, "targetProperty"}, - {SourceNameRole, "source"}, - {SourcePropertyNameRole, "sourceProperty"}}; - - return roleNames; -} - BindingModelBackendDelegate *BindingModel::delegate() const { return m_delegate; } -BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent) +int BindingModel::currentIndex() const +{ + return m_currentIndex; +} + +BindingProperty BindingModel::currentProperty() const +{ + return propertyForRow(m_currentIndex); +} + +BindingProperty BindingModel::propertyForRow(int row) const +{ + if (!m_connectionView) + return {}; + + if (!m_connectionView->isAttached()) + return {}; + + if (auto *item = itemForRow(row)) { + int internalId = item->internalId(); + if (ModelNode node = m_connectionView->modelNodeForInternalId(internalId); node.isValid()) + return node.bindingProperty(item->targetPropertyName()); + } + + return {}; +} + +static PropertyName unusedProperty(const ModelNode &modelNode) +{ + if (modelNode.metaInfo().isValid()) { + for (const auto &property : modelNode.metaInfo().properties()) { + if (property.isWritable() && !modelNode.hasProperty(property.name())) + return property.name(); + } + } + return "none"; +} + +void BindingModel::add() +{ + if (const QList nodes = connectionView()->selectedModelNodes(); nodes.size() == 1) { + const ModelNode modelNode = nodes.constFirst(); + if (modelNode.isValid()) { + try { + PropertyName name = unusedProperty(modelNode); + modelNode.bindingProperty(name).setExpression(QLatin1String("none.none")); + } catch (RewritingException &e) { + showErrorMessage(e.description()); + reset(); + } + } + } else { + qWarning() << __FUNCTION__ << " Requires exactly one selected node"; + } +} + +void BindingModel::remove(int row) +{ + if (BindingProperty property = propertyForRow(row); property.isValid()) { + ModelNode node = property.parentModelNode(); + node.removeProperty(property.name()); + } + + reset(); +} + +void BindingModel::reset(const QList &nodes) +{ + if (!connectionView()) + return; + + if (!connectionView()->isAttached()) + return; + + AbstractProperty current = currentProperty(); + + clear(); + + if (!nodes.isEmpty()) { + for (const ModelNode &modelNode : nodes) + addModelNode(modelNode); + } else { + for (const ModelNode &modelNode : connectionView()->selectedModelNodes()) + addModelNode(modelNode); + } + + setCurrentProperty(current); +} + +void BindingModel::setCurrentIndex(int i) +{ + if (m_currentIndex != i) { + m_currentIndex = i; + emit currentIndexChanged(); + } + m_delegate->update(currentProperty(), m_connectionView); +} + +void BindingModel::setCurrentProperty(const AbstractProperty &property) +{ + if (auto index = rowForProperty(property)) + setCurrentIndex(*index); +} + +void BindingModel::updateItem(const BindingProperty &property) +{ + if (auto *item = itemForProperty(property)) + item->updateProperty(property); + else + appendRow(new BindingModelItem(property)); +} + +void BindingModel::removeItem(const AbstractProperty &property) +{ + + AbstractProperty current = currentProperty(); + if (auto index = rowForProperty(property)) + static_cast(removeRow(*index)); + + setCurrentProperty(current); + emit currentIndexChanged(); +} + +void BindingModel::commitExpression(int row, const QString &expression) +{ + QTC_ASSERT(connectionView(), return); + + BindingProperty bindingProperty = propertyForRow(row); + if (!bindingProperty.isValid()) + return; + + connectionView()->executeInTransaction(__FUNCTION__, [&bindingProperty, expression]() { + bindingProperty.setExpression(expression.trimmed()); + }); +} + +QHash BindingModel::roleNames() const +{ + return BindingModelItem::roleNames(); +} + +std::optional BindingModel::rowForProperty(const AbstractProperty &property) const +{ + PropertyName name = property.name(); + int internalId = property.parentModelNode().internalId(); + + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->targetPropertyName() == name && item->internalId() == internalId) + return i; + } + } + return std::nullopt; +} + +BindingModelItem *BindingModel::itemForRow(int row) const +{ + if (QModelIndex idx = index(row, 0); idx.isValid()) + return dynamic_cast(itemFromIndex(idx)); + return nullptr; +} + +BindingModelItem *BindingModel::itemForProperty(const AbstractProperty &property) const +{ + if (auto row = rowForProperty(property)) + return itemForRow(*row); + return nullptr; +} + +void BindingModel::addModelNode(const ModelNode &node) +{ + if (!node.isValid()) + return; + + const QList bindingProperties = node.bindingProperties(); + for (const BindingProperty &property : bindingProperties) + appendRow(new BindingModelItem(property)); +} + +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) + : QObject(parent) + , m_targetNode() + , m_property() + , m_sourceNode() + , m_sourceNodeProperty() { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() { - handleSourceNodeChanged(); + expressionChanged(); }); connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { - handleSourcePropertyChanged(); + expressionChanged(); }); } -int BindingModelBackendDelegate::currentRow() const +void BindingModelBackendDelegate::update(const BindingProperty &property, AbstractView *view) { - return m_currentRow; -} - -void BindingModelBackendDelegate::setCurrentRow(int i) -{ - // See BindingDelegate::createEditor - - if (m_currentRow == i) + if (!property.isValid()) return; - m_currentRow = i; + auto addName = [](QStringList&& list, const QString& name) { + if (!list.contains(name)) + list.prepend(name); + return std::move(list); + }; - //setup + auto [sourceNodeName, sourcePropertyName] = splitExpression(property.expression()); - BindingModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - - QString idLabel = bindingProperty.parentModelNode().id(); - if (idLabel.isEmpty()) - idLabel = bindingProperty.parentModelNode().simplifiedTypeName(); - - m_targetNode = idLabel; - - emit targetNodeChanged(); - - m_property.setModel(model->possibleTargetProperties(bindingProperty)); - m_property.setCurrentText(QString::fromUtf8(bindingProperty.name())); - - QStringList sourceNodes; - - for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) - sourceNodes.append(modelNode.id()); - } - - std::sort(sourceNodes.begin(), sourceNodes.end()); - - QString sourceNodeName; - QString sourcePropertyName; - model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); - - if (!sourceNodes.contains(sourceNodeName)) - sourceNodes.append(sourceNodeName); - - m_sourceNode.setModel(sourceNodes); + QString targetName = QString::fromUtf8(property.name()); + m_targetNode = idOrTypeName(property.parentModelNode()); + auto modelNodes = addName(availableModelNodes(view), sourceNodeName); + m_sourceNode.setModel(modelNodes); m_sourceNode.setCurrentText(sourceNodeName); - m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty)); + auto sourceproperties = addName(availableSourceProperties(property, view), sourcePropertyName); + m_sourceNodeProperty.setModel(sourceproperties); m_sourceNodeProperty.setCurrentText(sourcePropertyName); -} -void BindingModelBackendDelegate::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - //reset + auto targetProperties = addName(availableTargetProperties(property), targetName); + m_property.setModel(targetProperties); + m_property.setCurrentText(targetName); + + emit targetNodeChanged(); } QString BindingModelBackendDelegate::targetNode() const @@ -595,54 +281,22 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() return &m_sourceNodeProperty; } -void BindingModelBackendDelegate::handleSourceNodeChanged() +void BindingModelBackendDelegate::expressionChanged() const { BindingModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView(), return ); + QTC_ASSERT(model, return); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); QString expression; - if (sourceProperty.isEmpty()) { + if (sourceProperty.isEmpty()) expression = sourceNode; - } else { + else expression = sourceNode + QLatin1String(".") + sourceProperty; - } - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - model->connectionView()->executeInTransaction("BindingModel::updateExpression", - [&bindingProperty, expression]() { - bindingProperty.setExpression( - expression.trimmed()); - }); -} - -void BindingModelBackendDelegate::handleSourcePropertyChanged() -{ - BindingModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView(), return ); - - const QString sourceNode = m_sourceNode.currentText(); - const QString sourceProperty = m_sourceNodeProperty.currentText(); - - QString expression; - if (sourceProperty.isEmpty()) { - expression = sourceNode; - } else { - expression = sourceNode + QLatin1String(".") + sourceProperty; - } - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - model->connectionView()->executeInTransaction("BindingModel::updateExpression", - [&bindingProperty, expression]() { - bindingProperty.setExpression( - expression.trimmed()); - }); + int row = model->currentIndex(); + model->commitExpression(row, expression); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 1f469876852..e167f2b5afb 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -3,132 +3,98 @@ #pragma once -#include +#include #include -#include - +#include #include +#include #include namespace QmlDesigner { -class ConnectionView; class BindingModelBackendDelegate; +class BindingModelItem; +class ConnectionView; class BindingModel : public QStandardItemModel { Q_OBJECT +signals: + void currentIndexChanged(); + +public: Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT) public: - enum ColumnRoles { - TargetModelNodeRow = 0, - TargetPropertyNameRow = 1, - SourceModelNodeRow = 2, - SourcePropertyNameRow = 3 - }; - - enum UserRoles { - InternalIdRole = Qt::UserRole + 2, - TargetNameRole, - TargetPropertyNameRole, - SourceNameRole, - SourcePropertyNameRole - }; - BindingModel(ConnectionView *parent = nullptr); - void bindingChanged(const BindingProperty &bindingProperty); - void bindingRemoved(const BindingProperty &bindingProperty); - void selectionChanged(const QList &selectedNodes); ConnectionView *connectionView() const; - BindingProperty bindingPropertyForRow(int rowNumber) const; - QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const; - QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const; - void deleteBindindByRow(int rowNumber); - void addBindingForCurrentNode(); - void resetModel(); + BindingModelBackendDelegate *delegate() const; + + int currentIndex() const; + BindingProperty currentProperty() const; + BindingProperty propertyForRow(int row) const; Q_INVOKABLE void add(); Q_INVOKABLE void remove(int row); - int currentIndex() const; + void reset(const QList &selectedNodes = {}); void setCurrentIndex(int i); - bool getExpressionStrings(const BindingProperty &bindingProperty, - QString *sourceNode, - QString *sourceProperty); + void setCurrentProperty(const AbstractProperty &property); -signals: - void currentIndexChanged(); + void updateItem(const BindingProperty &property); + void removeItem(const AbstractProperty &property); + + void commitExpression(int row, const QString &expression); protected: - void addBindingProperty(const BindingProperty &property); - void updateBindingProperty(int rowNumber); - void addModelNode(const ModelNode &modelNode); - void updateExpression(int row); - void updatePropertyName(int rowNumber); - ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; - void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty); - int findRowForBinding(const BindingProperty &bindingProperty); - void updateDisplayRole(int row, int columns, const QString &string); - QHash roleNames() const override; - BindingModelBackendDelegate *delegate() const; private: - void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); - void handleException(); + std::optional rowForProperty(const AbstractProperty &property) const; + BindingModelItem *itemForRow(int row) const; + BindingModelItem *itemForProperty(const AbstractProperty &property) const; + + void addModelNode(const ModelNode &modelNode); private: - ConnectionView *m_connectionView; - bool m_lock = false; - bool m_handleDataChanged = false; - QString m_exceptionError; - int m_currentIndex = 0; + ConnectionView *m_connectionView = nullptr; BindingModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; }; class BindingModelBackendDelegate : public QObject { Q_OBJECT - Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) - Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) +signals: + void targetNodeChanged(); + public: BindingModelBackendDelegate(BindingModel *parent = nullptr); -signals: - void currentRowChanged(); - //void nameChanged(); - void targetNodeChanged(); + void update(const BindingProperty &property, AbstractView *view); private: - int currentRow() const; - void setCurrentRow(int i); - void handleException(); QString targetNode() const; + void expressionChanged() const; StudioQmlComboBoxBackend *property(); StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceProperty(); - void handleSourceNodeChanged(); - void handleSourcePropertyChanged(); - + QString m_targetNode; StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_sourceNode; StudioQmlComboBoxBackend m_sourceNodeProperty; - QString m_exceptionError; - int m_currentRow = -1; - QString m_targetNode; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp new file mode 100644 index 00000000000..9e9fd92f528 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "bindingmodelitem.h" +#include "connectioneditorutils.h" + +#include +#include + +namespace QmlDesigner { + +QHash BindingModelItem::roleNames() +{ + return {{TargetNameRole, "target"}, + {TargetPropertyNameRole, "targetProperty"}, + {SourceNameRole, "source"}, + {SourcePropertyNameRole, "sourceProperty"}}; +} + +QStringList BindingModelItem::headerLabels() +{ + return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Source Item"), Tr::tr("Source Property")}; +} + +BindingModelItem::BindingModelItem(const BindingProperty &property) + : QStandardItem(idOrTypeName(property.parentModelNode())) +{ + updateProperty(property); +} + +int BindingModelItem::internalId() const +{ + return data(InternalIdRole).toInt(); +} + +PropertyName BindingModelItem::targetPropertyName() const +{ + return data(TargetPropertyNameRole).toString().toUtf8(); +} + +void BindingModelItem::updateProperty(const BindingProperty &property) +{ + setData(property.parentModelNode().internalId(), InternalIdRole); + setData(idOrTypeName(property.parentModelNode()), TargetNameRole); + setData(property.name(), TargetPropertyNameRole); + + // TODO: Make this safe when the new codemodel allows it. + if (auto expression = property.expression(); !expression.isEmpty()) { + auto [nodeName, propertyName] = splitExpression(expression); + setData(nodeName, SourceNameRole); + setData(propertyName, SourcePropertyNameRole); + } +} + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h new file mode 100644 index 00000000000..2e52905611c --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +namespace QmlDesigner { + +class BindingModelItem : public QStandardItem +{ +public: + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + TargetPropertyNameRole, + SourceNameRole, + SourcePropertyNameRole + }; + + static QHash roleNames(); + static QStringList headerLabels(); + + BindingModelItem(const BindingProperty &property); + + int internalId() const; + PropertyName targetPropertyName() const; + + void updateProperty(const BindingProperty &property); +}; + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp new file mode 100644 index 00000000000..c4ce5c23fe4 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -0,0 +1,392 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "connectioneditorutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +void callLater(const std::function &fun) +{ + QTimer::singleShot(0, fun); +} + +void showErrorMessage(const QString &text) +{ + callLater([text]() { QMessageBox::warning(nullptr, Tr::tr("Error"), text); }); +} + +QString idOrTypeName(const ModelNode &modelNode) +{ + QString idLabel = modelNode.id(); + if (idLabel.isEmpty()) + idLabel = modelNode.simplifiedTypeName(); + return idLabel; +} + +PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode) +{ + PropertyName name = suggestion; + if (!modelNode.isValid() || !modelNode.metaInfo().isValid()) + return name; + + int i = 0; + while (true) { + if (!modelNode.hasProperty(name) && !modelNode.metaInfo().hasProperty(name)) + return name; + name = suggestion + QString::number(i++).toLatin1(); + } + return {}; +} + +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property) +{ + // Note: Uses old mechanism to create the NodeMetaInfo and supports + // only types we care about in the connection editor. + // TODO: Support all possible AbstractProperty types and move to the + // AbstractProperty class. + if (property.dynamicTypeName() == "bool") + return property.model()->boolMetaInfo(); + else if (property.dynamicTypeName() == "int") + return property.model()->metaInfo("QML.int"); + else if (property.dynamicTypeName() == "real") + return property.model()->metaInfo("QML.real"); + else if (property.dynamicTypeName() == "color") + return property.model()->metaInfo("QML.color"); + else if (property.dynamicTypeName() == "string") + return property.model()->metaInfo("QML.string"); + else if (property.dynamicTypeName() == "url") + return property.model()->metaInfo("QML.url"); + else if (property.dynamicTypeName() == "variant") + return property.model()->metaInfo("QML.variant"); + else + qWarning() << __FUNCTION__ << " type " << property.dynamicTypeName() << "not found"; + return { }; +} + +QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName) +{ + QVariant returnValue = variant; + + if (typeName == "int") { + bool ok; + returnValue = variant.toInt(&ok); + if (!ok) + returnValue = 0; + } else if (typeName == "real") { + bool ok; + returnValue = variant.toReal(&ok); + if (!ok) + returnValue = 0.0; + + } else if (typeName == "string") { + returnValue = variant.toString(); + + } else if (typeName == "bool") { + returnValue = variant.toBool(); + } else if (typeName == "url") { + returnValue = variant.toUrl(); + } else if (typeName == "color") { + if (QColor::isValidColor(variant.toString())) + returnValue = variant.toString(); + else + returnValue = QColor(Qt::black); + } else if (typeName == "vector2d") { + returnValue = "Qt.vector2d(0, 0)"; + } else if (typeName == "vector3d") { + returnValue = "Qt.vector3d(0, 0, 0)"; + } else if (typeName == "vector4d") { + returnValue = "Qt.vector4d(0, 0, 0 ,0)"; + } else if (typeName == "TextureInput") { + returnValue = "null"; + } else if (typeName == "alias") { + returnValue = "null"; + } else if (typeName == "Item") { + returnValue = "null"; + } + + return returnValue; +} + +template +void convertPropertyType(const T &property, const QVariant &value) +{ + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + if (!node.isValid()) + return; + + PropertyName name = property.name(); + TypeName type = property.dynamicTypeName(); + node.removeProperty(name); + + if constexpr (std::is_same_v) { + BindingProperty newProperty = node.bindingProperty(name); + if (newProperty.isValid()) + newProperty.setDynamicTypeNameAndExpression(type, value.toString()); + } else if constexpr (std::is_same_v) { + VariantProperty newProperty = node.variantProperty(name); + if (newProperty.isValid()) + newProperty.setDynamicTypeNameAndValue(type, value); + } +} + +void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value) +{ + convertPropertyType(property, value); +} + +void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value) +{ + convertPropertyType(property, value); +} + +bool isBindingExpression(const QVariant& value) +{ + if (value.metaType().id() != QMetaType::QString) + return false; + + QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"); + QRegularExpressionMatch match = regexp.match(value.toString()); + return match.hasMatch(); +} + +bool isDynamicVariantPropertyType(const TypeName &type) +{ + // "variant" is considered value type as it is initialized as one. + // This may need to change if we provide any kind of proper editor for it. + static const QSet valueTypes{"int", "real", "color", "string", "bool", "url", "variant"}; + return valueTypes.contains(type); +} + +QVariant defaultValueForType(const TypeName &type) +{ + QVariant value; + if (type == "int") + value = 0; + else if (type == "real") + value = 0.0; + else if (type == "color") + value = QColor(255, 255, 255); + else if (type == "string") + value = "This is a string"; + else if (type == "bool") + value = false; + else if (type == "url") + value = ""; + else if (type == "variant") + value = ""; + + return value; +} + +QString defaultExpressionForType(const TypeName &type) +{ + QString expression; + if (type == "alias") + expression = "null"; + else if (type == "TextureInput") + expression = "null"; + else if (type == "vector2d") + expression = "Qt.vector2d(0, 0)"; + else if (type == "vector3d") + expression = "Qt.vector3d(0, 0, 0)"; + else if (type == "vector4d") + expression = "Qt.vector4d(0, 0, 0 ,0)"; + + return expression; +} + +QStringList availableModelNodes(AbstractView *view) +{ + QStringList sourceNodes; + for (const ModelNode &modelNode : view->allModelNodes()) { + if (!modelNode.id().isEmpty()) + sourceNodes.append(modelNode.id()); + } + std::sort(sourceNodes.begin(), sourceNodes.end()); + return sourceNodes; +} + +QStringList dynamicPropertyNamesFromNode(const ModelNode& node) +{ + QStringList dynamicProperties; + for (const VariantProperty &variantProperty : node.variantProperties()) { + if (variantProperty.isDynamic()) + dynamicProperties << QString::fromUtf8(variantProperty.name()); + } + + for (const BindingProperty &bindingProperty : node.bindingProperties()) { + if (bindingProperty.isDynamic()) + dynamicProperties << QString::fromUtf8((bindingProperty.name())); + } + return dynamicProperties; +} + +QStringList availableTargetProperties(const BindingProperty &bindingProperty) +{ + const ModelNode modelNode = bindingProperty.parentModelNode(); + if (!modelNode.isValid()) { + qWarning() << __FUNCTION__ << " invalid model node"; + return {}; + } + + NodeMetaInfo metaInfo = modelNode.metaInfo(); + if (metaInfo.isValid()) { + const auto properties = metaInfo.properties(); + QStringList writableProperties; + writableProperties.reserve(static_cast(properties.size())); + for (const auto &property : properties) { + if (property.isWritable()) + writableProperties.push_back(QString::fromUtf8(property.name())); + } + + return dynamicPropertyNamesFromNode(modelNode) + writableProperties; + } + + return dynamicPropertyNamesFromNode(modelNode); +} + +ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const ModelNode &targetNode) +{ + if (id != QLatin1String("parent")) + return view->modelNodeForId(id); + + if (targetNode.hasParentProperty()) + return targetNode.parentProperty().parentModelNode(); + + return {}; +} + +bool metaInfoIsCompatible(const NodeMetaInfo& sourceType, const PropertyMetaInfo& metaInfo) +{ + if (sourceType.isVariant()) + return true; + + NodeMetaInfo targetType = metaInfo.propertyType(); + if (sourceType.isBool() && targetType.isBool()) + return true; + + if (sourceType == targetType) + return true; + + if (sourceType.isNumber() && targetType.isNumber()) + return true; + + if (sourceType.isString() && targetType.isString()) + return true; + + if (sourceType.isUrl() && targetType.isUrl()) + return true; + + if (sourceType.isColor() && targetType.isColor()) + return true; + + return false; +} + +QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view) +{ + const QString expression = bindingProperty.expression(); + const QStringList stringlist = expression.split(QLatin1String(".")); + QStringList possibleProperties; + + const QString &id = stringlist.constFirst(); + ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode()); + if (!modelNode.isValid()) { + //if it's not a valid model node, maybe it's a singleton + if (RewriterView *rv = view->rewriterView()) { + for (const QmlTypeData &data : rv->getQMLTypes()) { + if (!data.typeName.isEmpty() && data.typeName == id) { + NodeMetaInfo metaInfo = view->model()->metaInfo(data.typeName.toUtf8()); + + if (metaInfo.isValid()) { + for (const auto &property : metaInfo.properties()) { + //without check for now + possibleProperties.push_back(QString::fromUtf8(property.name())); + } + + return possibleProperties; + } + } + } + } + qWarning() << __FUNCTION__ << " invalid model node"; + return QStringList(); + } + + possibleProperties = possibleProperties + dynamicPropertyNamesFromNode(modelNode); + + NodeMetaInfo type; + if (bindingProperty.isDynamic()) { + type = dynamicTypeMetaInfo(bindingProperty); + } else if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { + type = metaInfo.property(bindingProperty.name()).propertyType(); + } else + qWarning() << __FUNCTION__ << " no meta info for target node"; + + NodeMetaInfo metaInfo = modelNode.metaInfo(); + if (metaInfo.isValid()) { + for (const auto &property : metaInfo.properties()) { + if (metaInfoIsCompatible(type, property) ) + possibleProperties.push_back(QString::fromUtf8(property.name())); + } + } else { + qWarning() << __FUNCTION__ << " no meta info for source node"; + } + + return possibleProperties; +} + +QList dynamicPropertiesFromNode(const ModelNode &node) +{ + auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); }; + auto byName = [](const AbstractProperty &a, const AbstractProperty &b) { + return a.name() < b.name(); + }; + + QList dynamicProperties = Utils::filtered(node.properties(), isDynamic); + Utils::sort(dynamicProperties, byName); + return dynamicProperties; +} + +std::pair splitExpression(const QString &expression) +{ + // ### Todo from original code (getExpressionStrings): + // We assume no expressions yet + const QStringList stringList = expression.split(QLatin1String(".")); + + QString sourceNode = stringList.constFirst(); + QString propertyName; + for (int i = 1; i < stringList.size(); ++i) { + propertyName += stringList.at(i); + if (i != stringList.size() - 1) + propertyName += QLatin1String("."); + } + if (propertyName.isEmpty()) + std::swap(sourceNode, propertyName); + + return {sourceNode, propertyName}; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h new file mode 100644 index 00000000000..8a1cafa3e54 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "modelfwd.h" +#include "propertymetainfo.h" + +#include +#include +#include + +namespace QmlDesigner { + +class AbstractView; +class AbstractProperty; +class BindingProperty; +class ModelNode; +class VariantProperty; + +void callLater(const std::function &fun); +void showErrorMessage(const QString &text); + +QString idOrTypeName(const ModelNode &modelNode); +PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode); + +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property); +QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName); +void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value); +void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value); + +bool isBindingExpression(const QVariant& value); +bool isDynamicVariantPropertyType(const TypeName &type); +QVariant defaultValueForType(const TypeName &type); +QString defaultExpressionForType(const TypeName &type); + +QStringList availableModelNodes(AbstractView *view); +QStringList availableTargetProperties(const BindingProperty &bindingProperty); +QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view); +QList dynamicPropertiesFromNode(const ModelNode &node); + +std::pair splitExpression(const QString &expression); + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f5e42bbc99b..08e662953e9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -80,10 +80,6 @@ void ConnectionModel::resetModel() for (const ModelNode &modelNode : connectionView()->allModelNodes()) addModelNode(modelNode); } - - const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0); - connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80); - endResetModel(); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 78625efd388..d33e67d1164 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "connectionview.h" -#include "connectionviewwidget.h" #include "backendmodel.h" #include "bindingmodel.h" @@ -70,9 +69,13 @@ public: "ConnectionsEditorEditorBackend", 1); map->setProperties( - {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}, - {"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}, - {"dynamicPropertiesModel", + {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}}); + + map->setProperties( + {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}}); + + map->setProperties( + {{"dynamicPropertiesModel", QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); qmlRegisterType("ConnectionsEditorEditorBackend", @@ -98,7 +101,6 @@ public: // init the first load of the QML UI elements reloadQmlSource(); } - ~ConnectionViewQuickWidget() = default; static QString qmlSourcesPath() @@ -136,17 +138,13 @@ private: }; ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()), - m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)), - m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)), - m_backendModel(new BackendModel(this)), - m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) -{ - connectionViewWidget()->setBindingModel(m_bindingModel); - connectionViewWidget()->setConnectionModel(m_connectionModel); - connectionViewWidget()->setDynamicPropertiesModel(m_dynamicPropertiesModel); - connectionViewWidget()->setBackendModel(m_backendModel); -} + : AbstractView{externalDependencies} + , m_connectionModel(new ConnectionModel(this)) + , m_bindingModel(new BindingModel(this)) + , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)) + , m_backendModel(new BackendModel(this)) + , m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) +{} ConnectionView::~ConnectionView() { @@ -156,25 +154,22 @@ ConnectionView::~ConnectionView() void ConnectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); - bindingModel()->selectionChanged(QList()); + bindingModel()->reset(); dynamicPropertiesModel()->reset(); connectionModel()->resetModel(); - connectionViewWidget()->resetItemViews(); backendModel()->resetModel(); } void ConnectionView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - bindingModel()->selectionChanged(QList()); + bindingModel()->reset(); dynamicPropertiesModel()->reset(); connectionModel()->resetModel(); - connectionViewWidget()->resetItemViews(); } void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) { -//bindings connectionModel()->resetModel(); } @@ -194,8 +189,8 @@ void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstra void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/) { connectionModel()->resetModel(); - bindingModel()->resetModel(); - dynamicPropertiesModel()->resetModel(); + bindingModel()->reset(); + dynamicPropertiesModel()->reset(); } void ConnectionView::propertiesRemoved(const QList &propertyList) @@ -212,10 +207,10 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList &p { for (const AbstractProperty &property : propertyList) { if (property.isBindingProperty()) { - bindingModel()->bindingRemoved(property.toBindingProperty()); - dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty()); + bindingModel()->removeItem(property); + dynamicPropertiesModel()->removeItem(property); } else if (property.isVariantProperty()) { - dynamicPropertiesModel()->variantRemoved(property.toVariantProperty()); + dynamicPropertiesModel()->removeItem(property); } else if (property.isSignalHandlerProperty()) { connectionModel()->removeRowFromTable(property.toSignalHandlerProperty()); } @@ -227,7 +222,7 @@ void ConnectionView::variantPropertiesChanged(const QList &prop { for (const VariantProperty &variantProperty : propertyList) { if (variantProperty.isDynamic()) - dynamicPropertiesModel()->variantPropertyChanged(variantProperty); + dynamicPropertiesModel()->updateItem(variantProperty); if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode()) backendModel()->resetModel(); @@ -241,9 +236,9 @@ void ConnectionView::bindingPropertiesChanged(const QList &prop AbstractView::PropertyChangeFlags /*propertyChange*/) { for (const BindingProperty &bindingProperty : propertyList) { - bindingModel()->bindingChanged(bindingProperty); + bindingModel()->updateItem(bindingProperty); if (bindingProperty.isDynamic()) - dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty); + dynamicPropertiesModel()->updateItem(bindingProperty); if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode()) backendModel()->resetModel(); @@ -263,39 +258,8 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVector & selectedNodeList, const QList & /*lastSelectedNodeList*/) { - bindingModel()->selectionChanged(selectedNodeList); + bindingModel()->reset(selectedNodeList); dynamicPropertiesModel()->reset(); - connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex()); - connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex()); - - if (connectionViewWidget()->currentTab() == ConnectionViewWidget::BindingTab - || connectionViewWidget()->currentTab() == ConnectionViewWidget::DynamicPropertiesTab) - emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.size() == 1); -} - -void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node, - AuxiliaryDataKeyView key, - const QVariant &data) -{ - // Check if the auxiliary data is actually the locked property or if it is unlocked - if (key != lockedProperty || !data.toBool()) - return; - - QItemSelectionModel *selectionModel = connectionTableView()->selectionModel(); - if (!selectionModel->hasSelection()) - return; - - QModelIndex modelIndex = selectionModel->currentIndex(); - if (!modelIndex.isValid() || !model()) - return; - - const int internalId = connectionModel()->data(connectionModel()->index(modelIndex.row(), - ConnectionModel::TargetModelNodeRow), - ConnectionModel::UserRoles::InternalIdRole).toInt(); - ModelNode modelNode = modelNodeForInternalId(internalId); - - if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) - selectionModel->clearSelection(); } void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) @@ -310,14 +274,7 @@ void ConnectionView::currentStateChanged(const ModelNode &) WidgetInfo ConnectionView::widgetInfo() { - /* Enable new connection editor here */ - const bool newEditor = true; - - QWidget *widget = m_connectionViewWidget.data(); - if (newEditor) - widget = m_connectionViewQuickWidget.data(); - - return createWidgetInfo(widget, + return createWidgetInfo(m_connectionViewQuickWidget.data(), QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, @@ -334,31 +291,6 @@ bool ConnectionView::isWidgetEnabled() return widgetInfo().widget->isEnabled(); } -QTableView *ConnectionView::connectionTableView() const -{ - return connectionViewWidget()->connectionTableView(); -} - -QTableView *ConnectionView::bindingTableView() const -{ - return connectionViewWidget()->bindingTableView(); -} - -QTableView *ConnectionView::dynamicPropertiesTableView() const -{ - return connectionViewWidget()->dynamicPropertiesTableView(); -} - -QTableView *ConnectionView::backendView() const -{ - return connectionViewWidget()->backendView(); -} - -ConnectionViewWidget *ConnectionView::connectionViewWidget() const -{ - return m_connectionViewWidget.data(); -} - ConnectionModel *ConnectionView::connectionModel() const { return m_connectionModel; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 507637b4b91..b865bf51cc4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -51,9 +51,6 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; - void auxiliaryDataChanged(const ModelNode &node, - AuxiliaryDataKeyView key, - const QVariant &data) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override; @@ -63,14 +60,8 @@ public: bool hasWidget() const override; bool isWidgetEnabled(); - QTableView *connectionTableView() const; - QTableView *bindingTableView() const; - QTableView *dynamicPropertiesTableView() const; - QTableView *backendView() const; - DynamicPropertiesModel *dynamicPropertiesModel() const; - ConnectionViewWidget *connectionViewWidget() const; ConnectionModel *connectionModel() const; BindingModel *bindingModel() const; BackendModel *backendModel() const; @@ -83,9 +74,7 @@ public: signals: void currentIndexChanged(); -private: //variables - QPointer m_connectionViewWidget; - +private: ConnectionModel *m_connectionModel; BindingModel *m_bindingModel; DynamicPropertiesModel *m_dynamicPropertiesModel; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp deleted file mode 100644 index 655cc1a3914..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "connectionviewwidget.h" -#include "connectionview.h" -#include "ui_connectionviewwidget.h" - -#include "delegates.h" -#include "backendmodel.h" -#include "bindingmodel.h" -#include "connectionmodel.h" -#include "dynamicpropertiesmodel.h" -#include "theme.h" -#include "signalhandlerproperty.h" - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace QmlDesigner { - -ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : - QFrame(parent), - ui(new Ui::ConnectionViewWidget) -{ - m_connectionEditor = new QmlDesigner::ActionEditor(this); - m_bindingEditor = new QmlDesigner::BindingEditor(this); - m_dynamicEditor = new QmlDesigner::BindingEditor(this); - - editorForConnection(); - editorForBinding(); - editorForDynamic(); - - - setWindowTitle(tr("Connections", "Title of connections window")); - ui->setupUi(this); - - QStyle *style = QStyleFactory::create("fusion"); - ui->stackedWidget->setStyle(style); - - //ui->tabWidget->tabBar()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - ui->tabBar->setUsesScrollButtons(true); - ui->tabBar->setElideMode(Qt::ElideRight); - - ui->tabBar->addTab(tr("Connections", "Title of connection tab")); - ui->tabBar->addTab(tr("Bindings", "Title of connection tab")); - ui->tabBar->addTab(tr("Properties", "Title of dynamic properties tab")); - - const Qt::Alignment headerAlignment = Qt::AlignLeft | Qt::AlignVCenter; - ui->connectionView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->bindingView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->dynamicPropertiesView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->backendView->horizontalHeader()->setDefaultAlignment(headerAlignment); - - const QList buttons = createToolBarWidgets(); - - ui->toolBar->setFixedHeight(41); - for (auto toolButton : buttons) - ui->toolBar->addWidget(toolButton); - - if (!QmlProjectManager::QmlProject::isQtDesignStudio()) - ui->tabBar->addTab(tr("Backends", "Title of dynamic properties view")); - - ui->tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - - QByteArray sheet = Utils::FileReader::fetchQrc(":/connectionview/stylesheet.css"); - setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); - - connect(ui->tabBar, &QTabBar::currentChanged, - ui->stackedWidget, &QStackedWidget::setCurrentIndex); - - connect(ui->tabBar, &QTabBar::currentChanged, - this, &ConnectionViewWidget::handleTabChanged); - - ui->stackedWidget->setCurrentIndex(0); - - ui->stackedWidget->parentWidget()->hide(); -} - -ConnectionViewWidget::~ConnectionViewWidget() -{ - delete m_connectionEditor; - delete m_bindingEditor; - delete m_dynamicEditor; - delete ui; -} - -void ConnectionViewWidget::setBindingModel(BindingModel *model) -{ - ui->bindingView->setModel(model); - ui->bindingView->verticalHeader()->hide(); - ui->bindingView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->bindingView->setItemDelegate(new BindingDelegate); - connect(ui->bindingView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::bindingTableViewSelectionChanged); -} - -void ConnectionViewWidget::setConnectionModel(ConnectionModel *model) -{ - ui->connectionView->setModel(model); - ui->connectionView->verticalHeader()->hide(); - ui->connectionView->horizontalHeader()->setDefaultSectionSize(160); - ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->connectionView->setItemDelegate(new ConnectionDelegate); - - connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::connectionTableViewSelectionChanged); -} - -void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) -{ - auto tablePos = [&](QTableView *targetView) { - // adjusting qpoint to the qtableview entrances: - QPoint posInTable(targetView->mapFromGlobal(mapToGlobal(event->pos()))); - posInTable.ry() -= targetView->horizontalHeader()->height(); - return posInTable; - }; - - switch (currentTab()) { - case ConnectionTab: - if (ui->connectionView != nullptr) { - QTableView *targetView = ui->connectionView; - // making sure that we have source column in our hands: - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(ConnectionModel::SourceRow); - if (!index.isValid()) - return; - - QMenu menu(this); - - menu.addAction(tr("Open Connection Editor"), this, [&]() { - auto *connectionModel = qobject_cast(targetView->model()); - const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row()); - const ModelNode node = property.parentModelNode(); - - const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString() - + "." + property.name(); - - m_connectionEditor->showWidget(); - m_connectionEditor->setConnectionValue(index.data().toString()); - m_connectionEditor->setModelIndex(index); - m_connectionEditor->setModelNode(node); - m_connectionEditor->prepareConnections(); - m_connectionEditor->updateWindowName(targetName); - }); - - QMap data; - data["ModelNode"] = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data(); - data["Signal"] = index.siblingAtColumn(ConnectionModel::TargetPropertyNameRow).data(); - DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->designerActionManager(); - const auto actions = designerActionManager.actionsForTargetView( - ActionInterface::TargetView::ConnectionEditor); - - for (const auto &actionInterface : actions) { - auto *action = actionInterface->action(); - action->setData(data); - menu.addAction(action); - } - - menu.exec(event->globalPos()); - } - break; - - case BindingTab: - if (ui->bindingView != nullptr) { - QTableView *targetView = bindingTableView(); - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(BindingModel::SourcePropertyNameRow); - if (!index.isValid()) - return; - - QMenu menu(this); - - menu.addAction(tr("Open Binding Editor"), this, [&]() { - BindingModel *bindingModel = qobject_cast(targetView->model()); - const BindingProperty property = bindingModel->bindingPropertyForRow(index.row()); - - if (!property.isValid() || !property.isBindingProperty()) - return; - - const ModelNode node = property.parentModelNode(); - auto model = node.model(); - const auto type = property.isDynamic() - ? model->metaInfo(property.dynamicTypeName()) - : node.metaInfo().property(property.name()).propertyType(); - - const QString targetName = node.displayName() + "." + property.name(); - - m_bindingEditor->showWidget(); - m_bindingEditor->setBindingValue(property.expression()); - m_bindingEditor->setModelNode(node); - m_bindingEditor->setBackendValueType(type); - m_bindingEditor->setTargetName(targetName); - m_bindingEditor->prepareBindings(); - m_bindingEditor->updateWindowName(); - - m_bindingIndex = index; - }); - menu.exec(event->globalPos()); - } - break; - - case DynamicPropertiesTab: - if (ui->dynamicPropertiesView != nullptr) { - QTableView *targetView = dynamicPropertiesTableView(); - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(DynamicPropertiesModel::PropertyValueRow); - if (!index.isValid()) - return; - - DynamicPropertiesModel *propertiesModel = qobject_cast(targetView->model()); - QMenu menu(this); - - menu.addAction(tr("Open Binding Editor"), this, [&]() { - AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row()); - if (!abstractProperty.isValid()) - return; - - const ModelNode node = abstractProperty.parentModelNode(); - QString newExpression; - - if (abstractProperty.isBindingProperty()) - newExpression = abstractProperty.toBindingProperty().expression(); - else if (abstractProperty.isVariantProperty()) - newExpression = abstractProperty.toVariantProperty().value().toString(); - else - return; - - const QString targetName = node.displayName() + "." + abstractProperty.name(); - auto model = node.model(); - m_dynamicEditor->showWidget(); - m_dynamicEditor->setBindingValue(newExpression); - m_dynamicEditor->setModelNode(node); - m_dynamicEditor->setBackendValueType( - model->metaInfo(abstractProperty.dynamicTypeName())); - m_dynamicEditor->setTargetName(targetName); - m_dynamicEditor->prepareBindings(); - m_dynamicEditor->updateWindowName(); - - m_dynamicIndex = index; - }); - - menu.addAction(tr("Reset Property"), this, [&]() { - propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name()); - }); - - menu.exec(event->globalPos()); - } - break; - default: - break; - - } - -} - -void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model) -{ - ui->dynamicPropertiesView->setModel(model); - ui->dynamicPropertiesView->verticalHeader()->hide(); - ui->dynamicPropertiesView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->dynamicPropertiesView->setItemDelegate(new DynamicPropertiesDelegate); - connect(ui->dynamicPropertiesView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged); -} - -void ConnectionViewWidget::setBackendModel(BackendModel *model) -{ - ui->backendView->setModel(model); - ui->backendView->verticalHeader()->hide(); - ui->backendView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->backendView->setItemDelegate(new BackendDelegate); - model->resetModel(); - connect(ui->backendView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::backendTableViewSelectionChanged); -} - -QList ConnectionViewWidget::createToolBarWidgets() -{ - QList buttons; - - buttons << new QToolButton(); - buttons.constLast()->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); - buttons.constLast()->setToolTip(tr("Add binding or connection.")); - connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::addButtonClicked); - connect(this, &ConnectionViewWidget::setEnabledAddButton, buttons.constLast(), &QWidget::setEnabled); - - buttons << new QToolButton(); - buttons.constLast()->setIcon(Utils::Icons::MINUS_TOOLBAR.icon()); - buttons.constLast()->setToolTip(tr("Remove selected binding or connection.")); - connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::removeButtonClicked); - connect(this, &ConnectionViewWidget::setEnabledRemoveButton, buttons.constLast(), &QWidget::setEnabled); - - QAction *deleteShortcut = new QAction(this); - this->addAction(deleteShortcut); - deleteShortcut->setShortcuts({QKeySequence::Delete, QKeySequence::Backspace}); - deleteShortcut->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(deleteShortcut, &QAction::triggered, this, &ConnectionViewWidget::removeButtonClicked); - - return buttons; -} - -ConnectionViewWidget::TabStatus ConnectionViewWidget::currentTab() const -{ - switch (ui->stackedWidget->currentIndex()) { - case 0: return ConnectionTab; - case 1: return BindingTab; - case 2: return DynamicPropertiesTab; - case 3: return BackendTab; - default: return InvalidTab; - } -} - -void ConnectionViewWidget::resetItemViews() -{ - if (currentTab() == ConnectionTab) { - ui->connectionView->selectionModel()->clear(); - - } else if (currentTab() == BindingTab) { - ui->bindingView->selectionModel()->clear(); - - } else if (currentTab() == DynamicPropertiesTab) { - ui->dynamicPropertiesView->selectionModel()->clear(); - } else if (currentTab() == BackendTab) { - ui->backendView->selectionModel()->clear(); - } - invalidateButtonStatus(); -} - -void ConnectionViewWidget::invalidateButtonStatus() -{ - if (currentTab() == ConnectionTab) { - emit setEnabledRemoveButton(ui->connectionView->selectionModel()->hasSelection()); - emit setEnabledAddButton(true); - } else if (currentTab() == BindingTab) { - emit setEnabledRemoveButton(ui->bindingView->selectionModel()->hasSelection()); - auto bindingModel = qobject_cast(ui->bindingView->model()); - emit setEnabledAddButton(bindingModel->connectionView()->model() - && bindingModel->connectionView()->selectedModelNodes().size() == 1); - - } else if (currentTab() == DynamicPropertiesTab) { - emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection()); - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - emit setEnabledAddButton(dynamicPropertiesModel->view()->model() - && dynamicPropertiesModel->selectedNodes().size() == 1); - } else if (currentTab() == BackendTab) { - emit setEnabledAddButton(true); - emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection()); - } -} - -QTableView *ConnectionViewWidget::connectionTableView() const -{ - return ui->connectionView; -} - -QTableView *ConnectionViewWidget::bindingTableView() const -{ - return ui->bindingView; -} - -QTableView *ConnectionViewWidget::dynamicPropertiesTableView() const -{ - return ui->dynamicPropertiesView; -} - -QTableView *ConnectionViewWidget::backendView() const -{ - return ui->backendView; -} - -void ConnectionViewWidget::handleTabChanged(int) -{ - invalidateButtonStatus(); -} - -void ConnectionViewWidget::removeButtonClicked() -{ - if (currentTab() == ConnectionTab) { - if (ui->connectionView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->connectionView->selectionModel()->selectedRows().constFirst().row(); - auto connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel) { - connectionModel->deleteConnectionByRow(currentRow); - } - } else if (currentTab() == BindingTab) { - if (ui->bindingView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->bindingView->selectionModel()->selectedRows().constFirst().row(); - auto bindingModel = qobject_cast(ui->bindingView->model()); - if (bindingModel) { - bindingModel->deleteBindindByRow(currentRow); - } - } else if (currentTab() == DynamicPropertiesTab) { - if (ui->dynamicPropertiesView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->dynamicPropertiesView->selectionModel()->selectedRows().constFirst().row(); - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - if (dynamicPropertiesModel) - dynamicPropertiesModel->deleteDynamicPropertyByRow(currentRow); - } else if (currentTab() == BackendTab) { - int currentRow = ui->backendView->selectionModel()->selectedRows().constFirst().row(); - auto backendModel = qobject_cast(ui->backendView->model()); - if (backendModel) - backendModel->deletePropertyByRow(currentRow); - } - - invalidateButtonStatus(); -} - -void ConnectionViewWidget::addButtonClicked() -{ - - if (currentTab() == ConnectionTab) { - auto connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel) { - connectionModel->addConnection(); - } - } else if (currentTab() == BindingTab) { - auto bindingModel = qobject_cast(ui->bindingView->model()); - if (bindingModel) { - bindingModel->addBindingForCurrentNode(); - } - - } else if (currentTab() == DynamicPropertiesTab) { - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - if (dynamicPropertiesModel) - dynamicPropertiesModel->addDynamicPropertyForCurrentNode(); - } else if (currentTab() == BackendTab) { - auto backendModel = qobject_cast(ui->backendView->model()); - if (backendModel) - backendModel->addNewBackend(); - } - - invalidateButtonStatus(); -} - -void ConnectionViewWidget::editorForConnection() -{ - QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::accepted, - [&]() { - if (m_connectionEditor->hasModelIndex()) { - ConnectionModel *connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel->connectionView()->isWidgetEnabled() - && (connectionModel->rowCount() > m_connectionEditor->modelIndex().row())) { - connectionModel->connectionView() - ->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() { - SignalHandlerProperty signalHandler - = connectionModel->signalHandlerPropertyForRow( - m_connectionEditor->modelIndex().row()); - signalHandler.setSource(m_connectionEditor->connectionValue()); - }); - } - m_connectionEditor->resetModelIndex(); - } - - m_connectionEditor->hideWidget(); - }); - QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::rejected, - [&]() { - m_connectionEditor->resetModelIndex(); - m_connectionEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::editorForBinding() -{ - QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::accepted, - [&]() { - BindingModel *bindingModel = qobject_cast(bindingTableView()->model()); - QString newValue = m_bindingEditor->bindingValue().trimmed(); - - if (m_bindingIndex.isValid()) { - if (bindingModel->connectionView()->isWidgetEnabled() - && (bindingModel->rowCount() > m_bindingIndex.row())) { - bindingModel->connectionView()->executeInTransaction( - "ConnectionView::setBindingProperty", [this, bindingModel, newValue]() { - BindingProperty property = bindingModel->bindingPropertyForRow( - m_bindingIndex.row()); - - if (property.isValid()) { - if (property.isBindingProperty()) { - if (property.isDynamic()) { - property - .setDynamicTypeNameAndExpression(property.dynamicTypeName(), - newValue); - } else { - property.setExpression(newValue); - } - } - } - }); - } - } - - m_bindingIndex = QModelIndex(); - m_bindingEditor->hideWidget(); - }); - QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::rejected, - [&]() { - m_bindingIndex = QModelIndex(); //invalidating index - m_bindingEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::editorForDynamic() -{ - QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::accepted, - [&]() { - DynamicPropertiesModel *propertiesModel = qobject_cast(dynamicPropertiesTableView()->model()); - QString newValue = m_dynamicEditor->bindingValue().trimmed(); - - if (m_dynamicIndex.isValid()) { - if (qobject_cast(propertiesModel->view())->isWidgetEnabled() - && (propertiesModel->rowCount() > m_dynamicIndex.row())) { - propertiesModel->view()->executeInTransaction( - "ConnectionView::setBinding", [this, propertiesModel, newValue]() { - AbstractProperty abProp = propertiesModel->abstractPropertyForRow( - m_dynamicIndex.row()); - - if (abProp.isValid()) { - if (abProp.isBindingProperty()) { - BindingProperty property = abProp.toBindingProperty(); - property.setDynamicTypeNameAndExpression(property.dynamicTypeName(), - newValue); - } - - //if it's a variant property, then we remove it and replace with binding - else if (abProp.isVariantProperty()) { - VariantProperty property = abProp.toVariantProperty(); - PropertyName name = property.name(); - TypeName type = property.dynamicTypeName(); - - BindingProperty newProperty = propertiesModel - ->replaceVariantWithBinding(name); - if (newProperty.isValid()) { - newProperty.setDynamicTypeNameAndExpression(type, newValue); - } - } - } - }); - } - } - - m_dynamicIndex = QModelIndex(); - m_dynamicEditor->hideWidget(); - }); - QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::rejected, - [&]() { - m_dynamicIndex = QModelIndex(); //invalidating index - m_dynamicEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == BindingTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == ConnectionTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == DynamicPropertiesTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*revious*/) -{ - if (currentTab() == BackendTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } - -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h deleted file mode 100644 index f71641bd8bc..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -QT_BEGIN_NAMESPACE -class QShortcut; -class QToolButton; -class QTableView; -class QListView; -QT_END_NAMESPACE - -namespace QmlDesigner { - -namespace Ui { class ConnectionViewWidget; } - -class ActionEditor; -class BindingEditor; - -class BindingModel; -class ConnectionModel; -class DynamicPropertiesModel; -class BackendModel; - -class ConnectionViewWidget : public QFrame -{ - Q_OBJECT - -public: - - enum TabStatus { - ConnectionTab, - BindingTab, - DynamicPropertiesTab, - BackendTab, - InvalidTab - }; - - explicit ConnectionViewWidget(QWidget *parent = nullptr); - ~ConnectionViewWidget() override; - - void setBindingModel(BindingModel *model); - void setConnectionModel(ConnectionModel *model); - void setDynamicPropertiesModel(DynamicPropertiesModel *model); - void setBackendModel(BackendModel *model); - - QList createToolBarWidgets(); - - TabStatus currentTab() const; - - void resetItemViews(); - void invalidateButtonStatus(); - - QTableView *connectionTableView() const; - QTableView *bindingTableView() const; - QTableView *dynamicPropertiesTableView() const; - QTableView *backendView() const; - - void bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void backendTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - -signals: - void setEnabledAddButton(bool enabled); - void setEnabledRemoveButton(bool enabled); - -protected: - void contextMenuEvent(QContextMenuEvent *event) override; - -private: - void handleTabChanged(int i); - void removeButtonClicked(); - void addButtonClicked(); - - //methods to prepare editors - void editorForConnection(); - void editorForBinding(); - void editorForDynamic(); - -private: - Ui::ConnectionViewWidget *ui; - QmlDesigner::ActionEditor *m_connectionEditor; //editor for connections in connection view - QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view - QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view - - QModelIndex m_bindingIndex; - QModelIndex m_dynamicIndex; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui deleted file mode 100644 index 40939468c5e..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui +++ /dev/null @@ -1,265 +0,0 @@ - - - QmlDesigner::ConnectionViewWidget - - - - 0 - 0 - 994 - 611 - - - - Connections - - - - 0 - - - 1 - - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - 0 - - - - - 0 - 2 - - - - - 16777215 - 2 - - - - - - - - 3 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - - - - stackedWidget - tabBar - widgetSpacer - toolBar - - - - QTabBar - QWidget -
qtabbar.h
- 1 -
-
- - -
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp deleted file mode 100644 index e2c30b0a037..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "delegates.h" - -#include "backendmodel.h" -#include "connectionmodel.h" -#include "bindingmodel.h" -#include "dynamicpropertiesmodel.h" -#include "connectionview.h" -#include "nodemetainfo.h" - -#include - -#include - -#include -#include -#include - -namespace QmlDesigner { - -QStringList prependOnForSignalHandler(const QStringList &signalNames) -{ - QStringList signalHandlerNames; - for (const QString &signalName : signalNames) { - QString signalHandlerName = signalName; - if (!signalHandlerName.isEmpty()) { - QChar firstChar = signalHandlerName.at(0).toUpper(); - signalHandlerName[0] = firstChar; - signalHandlerName.prepend(QLatin1String("on")); - signalHandlerNames.append(signalHandlerName); - } - } - return signalHandlerNames; -} - -PropertiesComboBox::PropertiesComboBox(QWidget *parent) : QComboBox(parent) -{ - setEditable(true); - setValidator(new QRegularExpressionValidator(QRegularExpression(QLatin1String("[a-z|A-Z|0-9|._-]*")), this)); -} - -QString PropertiesComboBox::text() const -{ - return currentText(); -} - -void PropertiesComboBox::setText(const QString &text) -{ - setEditText(text); -} - -void PropertiesComboBox::disableValidator() -{ - setValidator(nullptr); -} - -ConnectionComboBox::ConnectionComboBox(QWidget *parent) : PropertiesComboBox(parent) -{ -} - -QString ConnectionComboBox::text() const -{ - int index = findText(currentText()); - if (index > -1) { - QVariant variantData = itemData(index); - if (variantData.isValid()) - return variantData.toString(); - } - - return currentText(); -} - -ConnectionEditorDelegate::ConnectionEditorDelegate(QWidget *parent) - : QStyledItemDelegate(parent) -{ -} - -void ConnectionEditorDelegate::paint(QPainter *painter, - const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - opt.state &= ~QStyle::State_HasFocus; - QStyledItemDelegate::paint(painter, opt, index); -} - -BindingDelegate::BindingDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ - static QItemEditorFactory *factory = nullptr; - if (factory == nullptr) { - factory = new QItemEditorFactory; - QItemEditorCreatorBase *creator - = new QItemEditorCreator("text"); - factory->registerEditor(QVariant::String, creator); - } - - setItemEditorFactory(factory); -} - -QWidget *BindingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto model = qobject_cast(index.model()); - if (!model) { - qWarning() << "BindingDelegate::createEditor no model"; - return widget; - } - if (!model->connectionView()) { - qWarning() << "BindingDelegate::createEditor no connection view"; - return widget; - } - - model->connectionView()->allModelNodes(); - - auto bindingComboBox = qobject_cast(widget); - if (!bindingComboBox) { - qWarning() << "BindingDelegate::createEditor no bindingComboBox"; - return widget; - } - - BindingProperty bindingProperty = model->bindingPropertyForRow(index.row()); - - switch (index.column()) { - case BindingModel::TargetModelNodeRow: - return nullptr; //no editor - case BindingModel::TargetPropertyNameRow: { - bindingComboBox->addItems(model->possibleTargetProperties(bindingProperty)); - } break; - case BindingModel::SourceModelNodeRow: { - //common items - for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) { - bindingComboBox->addItem(modelNode.id()); - } - } - //singletons: - if (RewriterView* rv = model->connectionView()->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty()) { - bindingComboBox->addItem(data.typeName); - } - } - } - //parent: - if (!bindingProperty.parentModelNode().isRootNode()) - bindingComboBox->addItem(QLatin1String("parent")); - } break; - case BindingModel::SourcePropertyNameRow: { - bindingComboBox->addItems(model->possibleSourceProperties(bindingProperty)); - bindingComboBox->disableValidator(); - } break; - default: qWarning() << "BindingDelegate::createEditor column" << index.column(); - } - - connect(bindingComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(bindingComboBox); - }); - - return widget; -} - -DynamicPropertiesDelegate::DynamicPropertiesDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ -// static QItemEditorFactory *factory = 0; -// if (factory == 0) { -// factory = new QItemEditorFactory; -// QItemEditorCreatorBase *creator -// = new QItemEditorCreator("text"); -// factory->registerEditor(QVariant::String, creator); -// } - -// setItemEditorFactory(factory); -} - -QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto model = qobject_cast(index.model()); - if (!model) { - qWarning() << "BindingDelegate::createEditor no model"; - return widget; - } - - if (!model->view()) { - qWarning() << "BindingDelegate::createEditor no connection view"; - return widget; - } - model->view()->allModelNodes(); - - switch (index.column()) { - case DynamicPropertiesModel::TargetModelNodeRow: { - return nullptr; //no editor - }; - case DynamicPropertiesModel::PropertyNameRow: { - return QStyledItemDelegate::createEditor(parent, option, index); - }; - case DynamicPropertiesModel::PropertyTypeRow: { - - auto dynamicPropertiesComboBox = new PropertiesComboBox(parent); - connect(dynamicPropertiesComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(dynamicPropertiesComboBox); - }); - - dynamicPropertiesComboBox->addItem(QLatin1String("alias")); - dynamicPropertiesComboBox->addItem(QLatin1String("Item")); - dynamicPropertiesComboBox->addItem(QLatin1String("real")); - dynamicPropertiesComboBox->addItem(QLatin1String("int")); - dynamicPropertiesComboBox->addItem(QLatin1String("string")); - dynamicPropertiesComboBox->addItem(QLatin1String("bool")); - dynamicPropertiesComboBox->addItem(QLatin1String("url")); - dynamicPropertiesComboBox->addItem(QLatin1String("color")); - dynamicPropertiesComboBox->addItem(QLatin1String("variant")); - dynamicPropertiesComboBox->addItem(QLatin1String("TextureInput")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector2d")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector3d")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector4d")); - return dynamicPropertiesComboBox; - }; - case DynamicPropertiesModel::PropertyValueRow: { - return QStyledItemDelegate::createEditor(parent, option, index); - }; - default: qWarning() << "BindingDelegate::createEditor column" << index.column(); - } - - return nullptr; -} - -ConnectionDelegate::ConnectionDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ - static QItemEditorFactory *factory = nullptr; - if (factory == nullptr) { - factory = new QItemEditorFactory; - QItemEditorCreatorBase *creator - = new QItemEditorCreator("text"); - factory->registerEditor(QVariant::String, creator); - } - - setItemEditorFactory(factory); -} - -static QString nameForAction(const QString &input) -{ - QStringList list = input.split('.'); - return list.first(); -} - -QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto connectionModel = qobject_cast(index.model()); - - auto connectionComboBox = qobject_cast(widget); - - if (!connectionModel) { - qWarning() << "ConnectionDelegate::createEditor no model"; - return widget; - } - - if (!connectionModel->connectionView()) { - qWarning() << "ConnectionDelegate::createEditor no connection view"; - return widget; - } - - if (!connectionComboBox) { - qWarning() << "ConnectionDelegate::createEditor no bindingComboBox"; - return widget; - } - - switch (index.column()) { - case ConnectionModel::TargetModelNodeRow: { - - auto addMetaInfoProperties = [&](const NodeMetaInfo& itemMetaInfo, QString itemName){ - if (itemMetaInfo.isValid()) { - for (const auto &property : itemMetaInfo.properties()) { - NodeMetaInfo propertyType = property.propertyType(); - if (propertyType.isValid() && propertyType.isFileComponent()) { - if (!property.isEnumType() && !property.isPrivate() - && !property.isListProperty() && !property.isPointer()) { - if (propertyType.isQtObject()) { - connectionComboBox->addItem(itemName + "." + property.name()); - } - } - } - } - } - }; - - for (const ModelNode &modelNode : connectionModel->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) { - connectionComboBox->addItem(modelNode.id()); - - for (const BindingProperty &property : modelNode.bindingProperties()) { - if (property.isValid()) { - if (property.isAlias()) { - connectionComboBox->addItem(modelNode.id() - + "." - + QString::fromUtf8(property.name())); - } - } - } - - //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()); - addMetaInfoProperties(metaInfo, data.typeName); - } - } - } - } break; - case ConnectionModel::TargetPropertyNameRow: { - connectionComboBox->addItems(prependOnForSignalHandler(connectionModel->getSignalsForRow(index.row()))); - } break; - case ConnectionModel::SourceRow: { - ModelNode rootModelNode = connectionModel->connectionView()->rootModelNode(); - if (QmlItemNode::isValidQmlItemNode(rootModelNode) && !rootModelNode.id().isEmpty()) { - - QString itemText = tr("Change to default state"); - QString source = QString::fromLatin1("%1.state = \"\"").arg(rootModelNode.id()); - connectionComboBox->addItem(itemText, source); - connectionComboBox->disableValidator(); - - for (const QmlModelState &state : QmlItemNode(rootModelNode).states().allStates()) { - QString itemText = tr("Change state to %1").arg(state.name()); - QString source = QString::fromLatin1("%1.state = \"%2\"") - .arg(rootModelNode.id()) - .arg(state.name()); - connectionComboBox->addItem(itemText, source); - } - - QStringList trigger = connectionModel->getflowActionTriggerForRow(index.row()); - for (const QString &action : trigger) { - connectionComboBox->addItem(tr("Activate FlowAction %1").arg(nameForAction(action)), action); - } - } - connectionComboBox->disableValidator(); - } break; - - default: qWarning() << "ConnectionDelegate::createEditor column" << index.column(); - } - - connect(connectionComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(connectionComboBox); - }); - - return widget; -} - -BackendDelegate::BackendDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ -} - -QWidget *BackendDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - const auto model = qobject_cast(index.model()); - - model->connectionView()->allModelNodes(); - - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - QTC_ASSERT(model, return widget); - QTC_ASSERT(model->connectionView(), return widget); - - switch (index.column()) { - case BackendModel::TypeNameColumn: { - auto backendComboBox = new PropertiesComboBox(parent); - backendComboBox->addItems(model->possibleCppTypes()); - connect(backendComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(backendComboBox); - }); - return backendComboBox; - }; - case BackendModel::PropertyNameColumn: { - return widget; - }; - case BackendModel::IsSingletonColumn: { - return nullptr; //no editor - }; - case BackendModel::IsLocalColumn: { - return nullptr; //no editor - }; - default: qWarning() << "BackendDelegate::createEditor column" << index.column(); - } - - return widget; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.h b/src/plugins/qmldesigner/components/connectioneditor/delegates.h deleted file mode 100644 index 55f886b98a4..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include -#include - -namespace QmlDesigner { - -class PropertiesComboBox : public QComboBox -{ - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText USER true) -public: - PropertiesComboBox(QWidget *parent = nullptr); - - virtual QString text() const; - void setText(const QString &text); - void disableValidator(); -}; - -class ConnectionComboBox : public PropertiesComboBox -{ - Q_OBJECT -public: - ConnectionComboBox(QWidget *parent = nullptr); - QString text() const override; -}; - -class ConnectionEditorDelegate : public QStyledItemDelegate -{ -public: - ConnectionEditorDelegate(QWidget *parent = nullptr); - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; -}; - -class BindingDelegate : public ConnectionEditorDelegate -{ -public: - BindingDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -class DynamicPropertiesDelegate : public ConnectionEditorDelegate -{ -public: - DynamicPropertiesDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - - -class ConnectionDelegate : public ConnectionEditorDelegate -{ - Q_OBJECT -public: - ConnectionDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -class BackendDelegate : public ConnectionEditorDelegate -{ - Q_OBJECT -public: - BackendDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp new file mode 100644 index 00000000000..ae23fab076d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "dynamicpropertiesitem.h" +#include "connectioneditorutils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +QHash DynamicPropertiesItem::roleNames() +{ + return {{TargetNameRole, "target"}, + {PropertyNameRole, "name"}, + {PropertyTypeRole, "type"}, + {PropertyValueRole, "value"}}; +} + +QStringList DynamicPropertiesItem::headerLabels() +{ + return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Property Type"), Tr::tr("Property Value")}; +} + +DynamicPropertiesItem::DynamicPropertiesItem(const AbstractProperty &property) + : QStandardItem(idOrTypeName(property.parentModelNode())) +{ + updateProperty(property); +} + +int DynamicPropertiesItem::internalId() const +{ + return data(InternalIdRole).toInt(); +} + +PropertyName DynamicPropertiesItem::propertyName() const +{ + return data(PropertyNameRole).toString().toUtf8(); +} + +std::optional parentIfNotDefaultState(const AbstractProperty &property) +{ + const QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode()); + if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) + return objectNode; + return std::nullopt; +} + +void DynamicPropertiesItem::updateProperty(const AbstractProperty &property) +{ + setData(property.parentModelNode().internalId(), InternalIdRole); + setData(idOrTypeName(property.parentModelNode()), TargetNameRole); + setData(property.name(), PropertyNameRole); + setData(property.dynamicTypeName(), PropertyTypeRole); + + if (property.isVariantProperty()) { + if (std::optional nodeInState = parentIfNotDefaultState(property)) + setData(nodeInState->modelValue(property.name()), PropertyValueRole); + else + setData(property.toVariantProperty().value(), PropertyValueRole); + } else if (property.isBindingProperty()) { + if (std::optional nodeInState = parentIfNotDefaultState(property)) + setData(nodeInState->expression(property.name()), PropertyValueRole); + else + setData(property.toBindingProperty().expression(), PropertyValueRole); + } +} + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h new file mode 100644 index 00000000000..9d95a8d9e4d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class AbstractProperty; + +class DynamicPropertiesItem : public QStandardItem +{ +public: + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + PropertyNameRole, + PropertyTypeRole, + PropertyValueRole + }; + + static QHash roleNames(); + static QStringList headerLabels(); + + DynamicPropertiesItem(const AbstractProperty &property); + + int internalId() const; + PropertyName propertyName() const; + + void updateProperty(const AbstractProperty &property); +}; + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 8557ebf5cae..084afb8e100 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -2,164 +2,44 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "dynamicpropertiesmodel.h" +#include "dynamicpropertiesitem.h" +#include "connectioneditorutils.h" -#include "bindingproperty.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "qmlchangeset.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qmlobjectnode.h" -#include "qmltimeline.h" -#include "rewritertransaction.h" -#include "rewritingexception.h" -#include "variantproperty.h" - +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include -#include -#include - -namespace { - -bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProp1, - const QmlDesigner::VariantProperty &variantProp2) -{ - if (variantProp1.parentModelNode() != variantProp2.parentModelNode()) - return false; - if (variantProp1.name() != variantProp2.name()) - return false; - return true; -} - -QString idOrTypeNameForNode(const QmlDesigner::ModelNode &modelNode) -{ - QString idLabel = modelNode.id(); - if (idLabel.isEmpty()) - idLabel = modelNode.simplifiedTypeName(); - - return idLabel; -} - -QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::TypeName &typeName) -{ - QVariant returnValue = variant; - - if (typeName == "int") { - bool ok; - returnValue = variant.toInt(&ok); - if (!ok) - returnValue = 0; - } else if (typeName == "real") { - bool ok; - returnValue = variant.toReal(&ok); - if (!ok) - returnValue = 0.0; - - } else if (typeName == "string") { - returnValue = variant.toString(); - - } else if (typeName == "bool") { - returnValue = variant.toBool(); - } else if (typeName == "url") { - returnValue = variant.toUrl(); - } else if (typeName == "color") { - if (QColor::isValidColor(variant.toString())) { - returnValue = variant.toString(); - } else { - returnValue = QColor(Qt::black); - } - } else if (typeName == "vector2d") { - returnValue = "Qt.vector2d(0, 0)"; - } else if (typeName == "vector3d") { - returnValue = "Qt.vector3d(0, 0, 0)"; - } else if (typeName == "vector4d") { - returnValue = "Qt.vector4d(0, 0, 0 ,0)"; - } else if (typeName == "TextureInput") { - returnValue = "null"; - } else if (typeName == "alias") { - returnValue = "null"; - } else if (typeName == "Item") { - returnValue = "null"; - } - - return returnValue; -} - -} // namespace +#include namespace QmlDesigner { -PropertyName DynamicPropertiesModel::unusedProperty(const ModelNode &modelNode) +DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *parent) + : QStandardItemModel(parent) + , m_view(parent) + , m_delegate(new DynamicPropertiesModelBackendDelegate(this)) + , m_explicitSelection(exSelection) { - PropertyName propertyName = "property"; - int i = 0; - if (modelNode.isValid() && modelNode.metaInfo().isValid()) { - while (true) { - const PropertyName currentPropertyName = propertyName + QString::number(i++).toLatin1(); - if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName)) - return currentPropertyName; - } - } - - return propertyName; + setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); } -bool DynamicPropertiesModel::isValueType(const TypeName &type) +AbstractView *DynamicPropertiesModel::view() const { - // "variant" is considered value type as it is initialized as one. - // This may need to change if we provide any kind of proper editor for it. - static const QSet valueTypes {"int", "real", "color", "string", "bool", "url", "variant"}; - return valueTypes.contains(type); + return m_view; } -QVariant DynamicPropertiesModel::defaultValueForType(const TypeName &type) +DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const { - QVariant value; - if (type == "int") - value = 0; - else if (type == "real") - value = 0.0; - else if (type == "color") - value = QColor(255, 255, 255); - else if (type == "string") - value = "This is a string"; - else if (type == "bool") - value = false; - else if (type == "url") - value = ""; - else if (type == "variant") - value = ""; - - return value; -} - -QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type) -{ - QString expression; - if (type == "alias") - expression = "null"; - else if (type == "TextureInput") - expression = "null"; - else if (type == "vector2d") - expression = "Qt.vector2d(0, 0)"; - else if (type == "vector3d") - expression = "Qt.vector3d(0, 0, 0)"; - else if (type == "vector4d") - expression = "Qt.vector4d(0, 0, 0 ,0)"; - - return expression; -} - -void DynamicPropertiesModel::add() -{ - addDynamicPropertyForCurrentNode(); -} - -void DynamicPropertiesModel::remove(int row) -{ - deleteDynamicPropertyByRow(row); + return m_delegate; } int DynamicPropertiesModel::currentIndex() const @@ -167,112 +47,293 @@ int DynamicPropertiesModel::currentIndex() const return m_currentIndex; } -void DynamicPropertiesModel::setCurrentIndex(int i) +AbstractProperty DynamicPropertiesModel::currentProperty() const { - if (m_currentIndex == i) - return; - - m_currentIndex = i; - - emit currentIndexChanged(); + return propertyForRow(m_currentIndex); } -DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent) - : QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection), - m_delegate(new DynamicPropertiesModelBackendDelegate(this)) +void DynamicPropertiesModel::add() { - connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged); + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); + + if (const QList nodes = selectedNodes(); nodes.size() == 1) { + const ModelNode modelNode = nodes.constFirst(); + if (!modelNode.isValid()) + return; + + try { + PropertyName newName = uniquePropertyName("property", modelNode); + VariantProperty newProperty = modelNode.variantProperty(newName); + newProperty.setDynamicTypeNameAndValue("string", "This is a string"); + } catch (RewritingException &e) { + showErrorMessage(e.description()); + } + } else { + qWarning() << "DynamicPropertiesModel::add not one node selected"; + } } -void DynamicPropertiesModel::resetModel() +void DynamicPropertiesModel::remove(int row) { - beginResetModel(); - const int backIndex = m_currentIndex; + m_view->executeInTransaction(__FUNCTION__, [this, row]() { + if (DynamicPropertiesItem *item = itemForRow(row)) { + PropertyName name = item->propertyName(); + if (ModelNode node = modelNodeForItem(item); node.isValid()) { + node.removeProperty(name); + + QmlObjectNode objectNode = QmlObjectNode(node); + const auto stateOperations = objectNode.allAffectingStatesOperations(); + for (const QmlModelStateOperation &stateOperation : stateOperations) { + if (stateOperation.modelNode().hasProperty(name)) + stateOperation.modelNode().removeProperty(name); + } + for (auto &timelineNode : objectNode.allTimelines()) { + QmlTimeline timeline(timelineNode); + timeline.removeKeyframesForTargetAndProperty(node, name); + } + } + } + }); + reset(); +} + +void DynamicPropertiesModel::reset(const QList &modelNodes) +{ + AbstractProperty current = currentProperty(); + clear(); - setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}); + + if (!modelNodes.isEmpty()) { + for (const ModelNode &modelNode : modelNodes) + addModelNode(modelNode); + return; + } if (m_view->isAttached()) { - const auto nodes = selectedNodes(); - for (const ModelNode &modelNode : nodes) + const QList selected = selectedNodes(); + for (const ModelNode &modelNode : selected) addModelNode(modelNode); } - emit currentIndexChanged(); - endResetModel(); - m_currentIndex = backIndex; + setCurrentProperty(current); } - -// Method creates dynamic BindingProperty with the same name and type as old VariantProperty -// Value copying is optional -BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue) +void DynamicPropertiesModel::setCurrentIndex(int i) { - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - if (modelNode.hasVariantProperty(name)) { - try { - VariantProperty vprop = modelNode.variantProperty(name); - TypeName oldType = vprop.dynamicTypeName(); - QVariant oldValue = vprop.value(); - - modelNode.removeProperty(name); - - BindingProperty bprop = modelNode.bindingProperty(name); - if (bprop.isValid()) { - if (copyValue) - bprop.setDynamicTypeNameAndExpression(oldType, oldValue.toString()); - return bprop; - } - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } - } - } else { - qWarning() << "DynamicPropertiesModel::replaceVariantWithBinding: no selected nodes"; + if (m_currentIndex != i) { + m_currentIndex = i; + emit currentIndexChanged(); } + // Property properties may have changed. + m_delegate->update(currentProperty()); +} +void DynamicPropertiesModel::setCurrentProperty(const AbstractProperty &property) +{ + if (!property.isValid()) + return; + + if (auto index = findRow(property.parentModelNode().internalId(), property.name())) + setCurrentIndex(*index); +} + +void DynamicPropertiesModel::setCurrent(int internalId, const PropertyName &name) +{ + if (internalId < 0) + return; + + if (auto index = findRow(internalId, name)) + setCurrentIndex(*index); +} + +void DynamicPropertiesModel::updateItem(const AbstractProperty &property) +{ + if (!property.isDynamic()) + return; + + if (auto *item = itemForProperty(property)) + item->updateProperty(property); + else + addProperty(property); +} + +void DynamicPropertiesModel::removeItem(const AbstractProperty &property) +{ + if (!property.isValid()) + return; + + AbstractProperty current = currentProperty(); + + if (auto index = findRow(property.parentModelNode().internalId(), property.name())) + static_cast(removeRow(*index)); + + setCurrentProperty(current); + emit currentIndexChanged(); +} + +QHash DynamicPropertiesModel::roleNames() const +{ + return DynamicPropertiesItem::roleNames(); +} + +AbstractProperty DynamicPropertiesModel::propertyForRow(int row) const +{ + if (!m_view) + return {}; + + if (!m_view->isAttached()) + return {}; + + if (auto *item = itemForRow(row)) { + int internalId = item->internalId(); + if (ModelNode node = m_view->modelNodeForInternalId(internalId); node.isValid()) + return node.property(item->propertyName()); + } return {}; } -// Finds selected property, and changes it to empty value (QVariant()) -// If it's a BindingProperty, then replaces it with empty VariantProperty -void DynamicPropertiesModel::resetProperty(const PropertyName &name) +std::optional DynamicPropertiesModel::findRow(int nodeId, const PropertyName &name) const { - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - if (modelNode.hasProperty(name)) { - try { - AbstractProperty abProp = modelNode.property(name); + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->propertyName() == name && item->internalId() == nodeId) + return i; + } + } + return std::nullopt; +} - if (abProp.isVariantProperty()) { - VariantProperty property = abProp.toVariantProperty(); - QVariant newValue = convertVariantForTypeName({}, property.dynamicTypeName()); - property.setDynamicTypeNameAndValue(property.dynamicTypeName(), - newValue); - } else if (abProp.isBindingProperty()) { - BindingProperty property = abProp.toBindingProperty(); - TypeName oldType = property.dynamicTypeName(); +DynamicPropertiesItem *DynamicPropertiesModel::itemForRow(int row) const +{ + if (QModelIndex idx = index(row, 0); idx.isValid()) + return dynamic_cast(itemFromIndex(idx)); + return nullptr; +} - // removing old property, to create the new one with the same name - modelNode.removeProperty(name); +DynamicPropertiesItem *DynamicPropertiesModel::itemForProperty(const AbstractProperty &property) const +{ + if (!property.isValid()) + return nullptr; - VariantProperty newProperty = modelNode.variantProperty(name); - QVariant newValue = convertVariantForTypeName({}, oldType); - newProperty.setDynamicTypeNameAndValue(oldType, newValue); - } + if (auto row = findRow(property.parentModelNode().internalId(), property.name())) + return itemForRow(*row); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } + return nullptr; +} + +ModelNode DynamicPropertiesModel::modelNodeForItem(DynamicPropertiesItem *item) +{ + if (!m_view->isAttached()) + return {}; + + return m_view->modelNodeForInternalId(item->internalId()); +} + +void DynamicPropertiesModel::addModelNode(const ModelNode &node) +{ + if (!node.isValid()) + return; + + for (const AbstractProperty &property : dynamicPropertiesFromNode(node)) + addProperty(property); +} + +void DynamicPropertiesModel::addProperty(const AbstractProperty &property) +{ + const PropertyName name = property.name(); + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->propertyName() > name) { + insertRow(i, new DynamicPropertiesItem(property)); + return; } } - } else { - qWarning() << "DynamicPropertiesModel::resetProperty: no selected nodes"; + } + appendRow(new DynamicPropertiesItem(property)); +} + +void DynamicPropertiesModel::commitPropertyType(int row, const TypeName &type) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); + try { + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + const QString expression = binding.expression(); + binding.parentModelNode().removeProperty(binding.name()); + binding.setDynamicTypeNameAndExpression(type, expression); + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + QVariant val = typeConvertVariant(variant.value(), type); + variant.parentModelNode().removeProperty(variant.name()); + variant.setDynamicTypeNameAndValue(type, val); + } + transaction.commit(); + + } catch (Exception &e) { + showErrorMessage(e.description()); + } +} + +void DynamicPropertiesModel::commitPropertyName(int row, const PropertyName &name) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + m_view->executeInTransaction(__FUNCTION__, [binding, name, &node]() { + const QString expression = binding.expression(); + const TypeName type = binding.dynamicTypeName(); + node.removeProperty(binding.name()); + node.bindingProperty(name).setDynamicTypeNameAndExpression(type, expression); + }); + + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + m_view->executeInTransaction(__FUNCTION__, [variant, name, &node]() { + const QVariant value = variant.value(); + const TypeName type = variant.dynamicTypeName(); + node.removeProperty(variant.name()); + node.variantProperty(name).setDynamicTypeNameAndValue(type, value); + }); + } +} + +void DynamicPropertiesModel::commitPropertyValue(int row, const QVariant &value) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); + try { + bool isBindingValue = isBindingExpression(value); + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + if (!isBindingValue) { + convertBindingToVariantProperty(binding, value); + } else { + const QString expression = value.toString(); + const TypeName typeName = binding.dynamicTypeName(); + binding.setDynamicTypeNameAndExpression(typeName, expression); + } + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + if (isBindingValue) + convertVariantToBindingProperty(variant, value); + else + variant.setDynamicTypeNameAndValue(variant.dynamicTypeName(), value); + } + transaction.commit(); + } catch (Exception &e) { + showErrorMessage(e.description()); } } @@ -285,676 +346,13 @@ void DynamicPropertiesModel::dispatchPropertyChanges(const AbstractProperty &abs const PropertyName propertyName = abstractProperty.name(); const AbstractProperty targetProperty = target.variantProperty(propertyName); if (target.hasProperty(propertyName) && targetProperty.isDynamic()) - abstractPropertyChanged(targetProperty); + updateItem(targetProperty); } } } -void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty) -{ - if (!bindingProperty.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(bindingProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForBindingProperty(bindingProperty); - - if (rowNumber == -1) - addBindingProperty(bindingProperty); - else - updateBindingProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::abstractPropertyChanged(const AbstractProperty &property) -{ - if (!property.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(property.parentModelNode())) - return; - - int rowNumber = findRowForProperty(property); - if (rowNumber > -1) { - if (property.isVariantProperty()) - updateVariantProperty(rowNumber); - else - updateBindingProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty) -{ - if (!variantProperty.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(variantProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForVariantProperty(variantProperty); - - if (rowNumber == -1) - addVariantProperty(variantProperty); - else - updateVariantProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(bindingProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForBindingProperty(bindingProperty); - removeRow(rowNumber); - } - - emit currentIndexChanged(); - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProperty) -{ - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(variantProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForVariantProperty(variantProperty); - removeRow(rowNumber); - } - - emit currentIndexChanged(); - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::reset() -{ - m_handleDataChanged = false; - resetModel(); - m_handleDataChanged = true; - emit currentIndexChanged(); -} - -void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) -{ - QTC_ASSERT(m_explicitSelection, return); - - if (!node.isValid()) - return; - - m_selectedNodes.clear(); - m_selectedNodes.append(node); - reset(); -} - -AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2) - .toString(); - - if (!m_view->isAttached()) - return AbstractProperty(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.property(targetPropertyName.toUtf8()); - - return {}; -} - -BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.bindingProperty(targetPropertyName.toUtf8()); - - return {}; -} - -VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.variantProperty(targetPropertyName.toUtf8()); - - return {}; -} - -QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const -{ - const ModelNode modelNode = bindingProperty.parentModelNode(); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node"; - return QStringList(); - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - QStringList possibleProperties; - const PropertyMetaInfos props = metaInfo.properties(); - for (const auto &property : props) { - if (property.isWritable()) - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - - return possibleProperties; - } - - return {}; -} - -void DynamicPropertiesModel::addDynamicPropertyForCurrentNode() -{ - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); - - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - try { - modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", "This is a string"); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } - } else { - qWarning() << " BindingModel::addBindingForCurrentNode not one node selected"; - } -} - -QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProperty &bindingProperty) const -{ - const QString expression = bindingProperty.expression(); - const QStringList stringlist = expression.split(QLatin1String(".")); - - NodeMetaInfo type; - - if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) - type = metaInfo.property(bindingProperty.name()).propertyType(); - else - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node"; - - const QString &id = stringlist.constFirst(); - - ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode()); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node"; - return QStringList(); - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - QStringList possibleProperties; - const PropertyMetaInfos props = metaInfo.properties(); - for (const auto &property : props) { - if (property.propertyType() == type) // TODO: proper check - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - return possibleProperties; - } else { - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node"; - } - - return {}; -} - -void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber) -{ - m_view->executeInTransaction(__FUNCTION__, [this, rowNumber]() { - const AbstractProperty property = abstractPropertyForRow(rowNumber); - const PropertyName propertyName = property.name(); - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - if (bindingProperty.isValid()) { - bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); - } else { - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - if (variantProperty.isValid()) - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - } - - if (property.isValid()) { - QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode()); - const auto stateOperations = objectNode.allAffectingStatesOperations(); - for (const QmlModelStateOperation &stateOperation : stateOperations) { - if (stateOperation.modelNode().hasProperty(propertyName)) - stateOperation.modelNode().removeProperty(propertyName); - } - - const QList timelineNodes = objectNode.allTimelines(); - for (auto &timelineNode : timelineNodes) { - QmlTimeline timeline(timelineNode); - timeline.removeKeyframesForTargetAndProperty(objectNode.modelNode(), - propertyName); - } - } - }); - - resetModel(); -} - -void DynamicPropertiesModel::addProperty(const QVariant &propertyValue, - const QString &propertyType, - const AbstractProperty &abstractProperty) -{ - QList items; - - QStandardItem *idItem; - QStandardItem *propertyNameItem; - QStandardItem *propertyTypeItem; - QStandardItem *propertyValueItem; - - idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode())); - updateCustomData(idItem, abstractProperty); - - const QString propName = QString::fromUtf8(abstractProperty.name()); - propertyNameItem = new QStandardItem(propName); - - items.append(idItem); - items.append(propertyNameItem); - - propertyTypeItem = new QStandardItem(propertyType); - items.append(propertyTypeItem); - - propertyValueItem = new QStandardItem(); - propertyValueItem->setData(propertyValue, Qt::DisplayRole); - items.append(propertyValueItem); - - for (int i = 0; i < rowCount(); ++i) { - if (data(index(i, PropertyNameRow)).toString() > propName) { - insertRow(i, items); - return; - } - } - - appendRow(items); -} - -void DynamicPropertiesModel::addBindingProperty(const BindingProperty &property) -{ - QVariant value = property.expression(); - QString type = QString::fromLatin1(property.dynamicTypeName()); - addProperty(value, type, property); -} - -void DynamicPropertiesModel::addVariantProperty(const VariantProperty &property) -{ - QVariant value = property.value(); - QString type = QString::fromLatin1(property.dynamicTypeName()); - addProperty(value, type, property); -} - -void DynamicPropertiesModel::updateBindingProperty(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - updateCustomData(rowNumber, bindingProperty); - - QString propertyName = QString::fromUtf8(bindingProperty.name()); - updateDisplayRole(rowNumber, PropertyNameRow, propertyName); - QString value = bindingProperty.expression(); - QString type = QString::fromUtf8(bindingProperty.dynamicTypeName()); - updateDisplayRole(rowNumber, PropertyTypeRow, type); - updateDisplayRole(rowNumber, PropertyValueRow, value); - - const QmlObjectNode objectNode = QmlObjectNode(bindingProperty.parentModelNode()); - if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) - value = objectNode.expression(bindingProperty.name()); - - updateDisplayRole(rowNumber, PropertyValueRow, value); - } -} - -void DynamicPropertiesModel::updateVariantProperty(int rowNumber) -{ - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isValid()) { - updateCustomData(rowNumber, variantProperty); - QString propertyName = QString::fromUtf8(variantProperty.name()); - updateDisplayRole(rowNumber, PropertyNameRow, propertyName); - QVariant value = variantProperty.value(); - QString type = QString::fromUtf8(variantProperty.dynamicTypeName()); - updateDisplayRole(rowNumber, PropertyTypeRow, type); - const QmlObjectNode objectNode = QmlObjectNode(variantProperty.parentModelNode()); - if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) - value = objectNode.modelValue(variantProperty.name()); - - updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value); - } -} - -void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode) -{ - if (!modelNode.isValid()) - return; - - const QList properties = modelNode.properties(); - QList dynamicProperties = Utils::filtered(properties, [](const AbstractProperty &p) { - return p.isDynamic(); - }); - - Utils::sort(dynamicProperties, [](const AbstractProperty &a, const AbstractProperty &b) { - return a.name() < b.name(); - }); - - for (const AbstractProperty &property : std::as_const(dynamicProperties)) { - if (property.isBindingProperty()) - addBindingProperty(property.toBindingProperty()); - else if (property.isVariantProperty()) - addVariantProperty(property.toVariantProperty()); - } -} - -void DynamicPropertiesModel::updateValue(int row) -{ - BindingProperty bindingProperty = bindingPropertyForRow(row); - - if (bindingProperty.isBindingProperty()) { - const QString expression = data(index(row, PropertyValueRow)).toString(); - - RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); - try { - bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - return; - } - - VariantProperty variantProperty = variantPropertyForRow(row); - - if (variantProperty.isVariantProperty()) { - const QVariant value = data(index(row, PropertyValueRow)); - - RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); - try { - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } -} - -void DynamicPropertiesModel::updatePropertyName(int rowNumber) -{ - const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toUtf8(); - QTC_ASSERT(!newName.isEmpty(), return); - - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - ModelNode targetNode = bindingProperty.parentModelNode(); - - if (bindingProperty.isBindingProperty()) { - m_view->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() { - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); - - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression); - targetNode.removeProperty(bindingProperty.name()); - }); - - updateCustomData(rowNumber, targetNode.bindingProperty(newName)); - return; - } - - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); - ModelNode targetNode = variantProperty.parentModelNode(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value); - targetNode.removeProperty(variantProperty.name()); - }); - - updateCustomData(rowNumber, targetNode.variantProperty(newName)); - } -} - -void DynamicPropertiesModel::updatePropertyType(int rowNumber) -{ - const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1(); - QTC_ASSERT(!newType.isEmpty(), return); - - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isBindingProperty()) { - const QString expression = bindingProperty.expression(); - const PropertyName propertyName = bindingProperty.name(); - ModelNode targetNode = bindingProperty.parentModelNode(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.removeProperty(bindingProperty.name()); - targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression); - }); - - updateCustomData(rowNumber, targetNode.bindingProperty(propertyName)); - return; - } - - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - ModelNode targetNode = variantProperty.parentModelNode(); - const PropertyName propertyName = variantProperty.name(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.removeProperty(variantProperty.name()); - if (!isValueType(newType)) { - targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression( - newType, convertVariantForTypeName({}, newType).toString()); - } else { - targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue( - newType, convertVariantForTypeName(value, newType)); - } - }); - - updateCustomData(rowNumber, targetNode.variantProperty(propertyName)); - - if (variantProperty.isVariantProperty()) - updateVariantProperty(rowNumber); - else if (bindingProperty.isBindingProperty()) - updateBindingProperty(rowNumber); - } -} - -ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const -{ - if (id != QLatin1String("parent")) - return m_view->modelNodeForId(id); - - if (targetNode.hasParentProperty()) - return targetNode.parentProperty().parentModelNode(); - - return {}; -} - -void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property) -{ - item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1); - item->setData(property.name(), Qt::UserRole + 2); - - item->setData(property.parentModelNode().id(), TargetNameRole); - item->setData(property.name(), PropertyNameRole); - item->setData(property.parentModelNode().id(), TargetNameRole); - item->setData(property.dynamicTypeName(), PropertyTypeRole); - - if (property.isVariantProperty()) - item->setData(property.toVariantProperty().value(), PropertyValueRole); - if (property.isBindingProperty()) - item->setData(property.toBindingProperty().expression(), PropertyValueRole); -} - -void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property) -{ - QStandardItem* idItem = item(row, 0); - updateCustomData(idItem, property); -} - -int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty)) - return i; - } - - return -1; // not found -} - -int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if (compareVariantProperties(variantPropertyForRow(i), variantProperty)) - return i; - } - - return -1; // not found -} - -int DynamicPropertiesModel::findRowForProperty(const AbstractProperty &abstractProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if ((abstractPropertyForRow(i).name() == abstractProperty.name())) - return i; - } - - return -1; // not found -} - -bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, - QString *sourceProperty) -{ - // TODO: we assume no expressions yet - - const QString expression = bindingProperty.expression(); - - if (true) { - const QStringList expressionParts = expression.split('.'); - - *sourceNode = expressionParts.constFirst(); - - QString propertyName; - - for (int i = 1; i < expressionParts.size(); ++i) { - propertyName += expressionParts.at(i); - if (i != expressionParts.size() - 1) - propertyName += QLatin1String("."); - } - *sourceProperty = propertyName; - } - - return true; -} - -void DynamicPropertiesModel::updateDisplayRole(int row, int columns, const QString &string) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex).toString() != string) - setData(modelIndex, string); -} - -void DynamicPropertiesModel::updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex) != variant) - setData(modelIndex, variant); -} - - -void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - if (!m_handleDataChanged) - return; - - if (topLeft != bottomRight) { - qWarning() << __FUNCTION__ << ": multi edit?"; - return; - } - - m_lock = true; - - int currentColumn = topLeft.column(); - int currentRow = topLeft.row(); - - switch (currentColumn) { - case TargetModelNodeRow: { - // updating user data - } break; - case PropertyNameRow: { - updatePropertyName(currentRow); - } break; - case PropertyTypeRow: { - updatePropertyType(currentRow); - } break; - case PropertyValueRow: { - updateValue(currentRow); - } break; - - default: qWarning() << __FUNCTION__ << " column" << currentColumn; - } - - m_lock = false; -} - -void DynamicPropertiesModel::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - resetModel(); -} - const QList DynamicPropertiesModel::selectedNodes() const { - // If selected nodes are explicitly set, return those. - // Otherwise return actual selected nodes of the model. if (m_explicitSelection) return m_selectedNodes; @@ -969,52 +367,34 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const return m_view->singleSelectedModelNode(); } -QHash DynamicPropertiesModel::roleNames() const +void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) { - static QHash roleNames{{TargetNameRole, "target"}, - {PropertyNameRole, "name"}, - {PropertyTypeRole, "type"}, - {PropertyValueRole, "value"}}; + QTC_ASSERT(m_explicitSelection, return); - return roleNames; + if (!node.isValid()) + return; + + m_selectedNodes.clear(); + m_selectedNodes.append(node); + reset(); } -DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const -{ - return m_delegate; -} - -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate( - DynamicPropertiesModel *parent) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent) : QObject(parent) + , m_internalNodeId(std::nullopt) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); - connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this]() { handleTypeChanged(); }); connect(&m_name, &StudioQmlTextBackend::activated, this, [this]() { handleNameChanged(); }); connect(&m_value, &StudioQmlTextBackend::activated, this, [this]() { handleValueChanged(); }); } -int DynamicPropertiesModelBackendDelegate::currentRow() const +void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &property) { - return m_currentRow; -} - -void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) -{ - if (m_currentRow == i) + if (!property.isValid()) return; - m_currentRow = i; - - //setup - - DynamicPropertiesModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - - AbstractProperty property = model->abstractPropertyForRow(i); - + m_internalNodeId = property.parentModelNode().internalId(); m_type.setCurrentText(QString::fromUtf8(property.dynamicTypeName())); m_name.setText(QString::fromUtf8(property.name())); @@ -1024,153 +404,62 @@ void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) m_value.setText(property.toBindingProperty().expression()); m_targetNode = property.parentModelNode().id(); - emit targetNodeChanged(); } void DynamicPropertiesModelBackendDelegate::handleTypeChanged() { - //void DynamicPropertiesModel::updatePropertyType(int rowNumber) - const TypeName type = m_type.currentText().toUtf8(); - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); + const PropertyName name = m_name.text().toUtf8(); - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + int current = model->currentIndex(); + const TypeName type = m_type.currentText().toUtf8(); + model->commitPropertyType(current, type); - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - - try { - if (bindingProperty.isBindingProperty() || type == "var") { //var is always a binding - const QString expression = bindingProperty.expression(); - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - bindingProperty.setDynamicTypeNameAndExpression(type, expression); - } else if (variantProperty.isVariantProperty()) { - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - variantProperty.setDynamicTypeNameAndValue(type, variantValue()); - } - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } + // The order might have changed! + model->setCurrent(m_internalNodeId.value_or(-1), name); } void DynamicPropertiesModelBackendDelegate::handleNameChanged() { - //see DynamicPropertiesModel::updatePropertyName - - const PropertyName newName = m_name.text().toUtf8(); - QTC_ASSERT(!newName.isEmpty(), return ); - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); + const PropertyName name = m_name.text().toUtf8(); + QTC_ASSERT(!name.isEmpty(), return); - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + int current = model->currentIndex(); + model->commitPropertyName(current, name); - ModelNode targetNode = bindingProperty.parentModelNode(); + // The order might have changed! + model->setCurrent(m_internalNodeId.value_or(-1), name); +} - if (bindingProperty.isBindingProperty()) { - model->view()->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() { - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); +// TODO: Maybe replace with utils typeConvertVariant? +QVariant valueFromText(const QString &value, const QString &type) +{ + if (isBindingExpression(value)) + return value; - targetNode.removeProperty(bindingProperty.name()); + if (type == "real" || type == "int") + return value.toFloat(); - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, - expression); - }); + if (type == "bool") + return value == "true"; - return; - } - - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); - ModelNode targetNode = variantProperty.parentModelNode(); - - model->view()->executeInTransaction(__FUNCTION__, [=]() { - targetNode.removeProperty(variantProperty.name()); - - targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, - value); - }); - } - - AbstractProperty property = targetNode.property(newName); - - //order might have changed because of name change we have to select the correct row - int newRow = model->findRowForProperty(property); - model->setCurrentIndex(newRow); - setCurrentRow(newRow); + return value; } void DynamicPropertiesModelBackendDelegate::handleValueChanged() { - //see void DynamicPropertiesModel::updateValue(int row) - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - - if (bindingProperty.isBindingProperty()) { - const QString expression = m_value.text(); - - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - try { - bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), - expression); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } - return; - } - - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - if (variantProperty.isVariantProperty()) { - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - try { - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), - variantValue()); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } - } -} - -void DynamicPropertiesModelBackendDelegate::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - //reset -} - -QVariant DynamicPropertiesModelBackendDelegate::variantValue() const -{ - //improve - const QString type = m_type.currentText(); - if (type == "real" || type == "int") - return m_value.text().toFloat(); - - if (type == "bool") - return m_value.text() == "true"; - - return m_value.text(); + int current = model->currentIndex(); + QVariant value = valueFromText(m_value.text(), m_type.currentText()); + model->commitPropertyValue(current, value); } QString DynamicPropertiesModelBackendDelegate::targetNode() const diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index 766b4e7b87c..8ae4abf6d59 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -3,19 +3,17 @@ #pragma once -#include - -#include +#include "dynamicpropertiesitem.h" +#include "nodeinstanceglobal.h" +#include "studioquickwidget.h" #include namespace QmlDesigner { -class AbstractProperty; class AbstractView; -class BindingProperty; +class AbstractProperty; class ModelNode; -class VariantProperty; class DynamicPropertiesModelBackendDelegate; @@ -23,112 +21,67 @@ class DynamicPropertiesModel : public QStandardItemModel { Q_OBJECT +signals: + void currentIndexChanged(); + public: - enum ColumnRoles { - TargetModelNodeRow = 0, - PropertyNameRow = 1, - PropertyTypeRow = 2, - PropertyValueRow = 3 - }; - - enum UserRoles { - InternalIdRole = Qt::UserRole + 2, - TargetNameRole, - PropertyNameRole, - PropertyTypeRole, - PropertyValueRole - }; - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT) DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); - void bindingPropertyChanged(const BindingProperty &bindingProperty); - void abstractPropertyChanged(const AbstractProperty &bindingProperty); - void variantPropertyChanged(const VariantProperty &variantProperty); - void bindingRemoved(const BindingProperty &bindingProperty); - void variantRemoved(const VariantProperty &variantProperty); - void reset(); - void setSelectedNode(const ModelNode &node); - const QList selectedNodes() const; - const ModelNode singleSelectedNode() const; + AbstractView *view() const; + DynamicPropertiesModelBackendDelegate *delegate() const; - AbstractView *view() const { return m_view; } - AbstractProperty abstractPropertyForRow(int rowNumber) const; - BindingProperty bindingPropertyForRow(int rowNumber) const; - VariantProperty variantPropertyForRow(int rowNumber) const; - QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const; - QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const; - void deleteDynamicPropertyByRow(int rowNumber); - - void updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant); - void addDynamicPropertyForCurrentNode(); - void resetModel(); - - BindingProperty replaceVariantWithBinding(const PropertyName &name, bool copyValue = false); - void resetProperty(const PropertyName &name); - - void dispatchPropertyChanges(const AbstractProperty &abstractProperty); - - PropertyName unusedProperty(const ModelNode &modelNode); - - static bool isValueType(const TypeName &type); - static QVariant defaultValueForType(const TypeName &type); - static QString defaultExpressionForType(const TypeName &type); + int currentIndex() const; + AbstractProperty currentProperty() const; + AbstractProperty propertyForRow(int row) const; Q_INVOKABLE void add(); Q_INVOKABLE void remove(int row); - int currentIndex() const; + void reset(const QList &modelNodes = {}); void setCurrentIndex(int i); + void setCurrentProperty(const AbstractProperty &property); + void setCurrent(int internalId, const PropertyName &name); - int findRowForProperty(const AbstractProperty &abstractProperty) const; + void updateItem(const AbstractProperty &property); + void removeItem(const AbstractProperty &property); -signals: - void currentIndexChanged(); + void commitPropertyType(int row, const TypeName &type); + void commitPropertyName(int row, const PropertyName &name); + void commitPropertyValue(int row, const QVariant &value); + + void dispatchPropertyChanges(const AbstractProperty &abstractProperty); protected: - void addProperty(const QVariant &propertyValue, - const QString &propertyType, - const AbstractProperty &abstractProperty); - void addBindingProperty(const BindingProperty &property); - void addVariantProperty(const VariantProperty &property); - void updateBindingProperty(int rowNumber); - void updateVariantProperty(int rowNumber); - void addModelNode(const ModelNode &modelNode); - void updateValue(int row); - void updatePropertyName(int rowNumber); - void updatePropertyType(int rowNumber); - ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; - void updateCustomData(QStandardItem *item, const AbstractProperty &property); - void updateCustomData(int row, const AbstractProperty &property); - int findRowForBindingProperty(const BindingProperty &bindingProperty) const; - int findRowForVariantProperty(const VariantProperty &variantProperty) const; - - bool getExpressionStrings(const BindingProperty &bindingProperty, - QString *sourceNode, - QString *sourceProperty); - - void updateDisplayRole(int row, int columns, const QString &string); - QHash roleNames() const override; - DynamicPropertiesModelBackendDelegate *delegate() const; +private: + std::optional findRow(int nodeId, const PropertyName &name) const; + DynamicPropertiesItem *itemForRow(int row) const; + DynamicPropertiesItem *itemForProperty(const AbstractProperty &property) const; + ModelNode modelNodeForItem(DynamicPropertiesItem *item); + + void addModelNode(const ModelNode &node); + void addProperty(const AbstractProperty &property); + +public: + // TODO: Remove. This is a model for properties. Not nodes. + // Use reset with a list of nodes instead if all properties + // from a set of given nodes should be added. + const QList selectedNodes() const; + void setSelectedNode(const ModelNode &node); + const ModelNode singleSelectedNode() const; private: - void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void handleException(); - AbstractView *m_view = nullptr; - bool m_lock = false; - bool m_handleDataChanged = false; - QString m_exceptionError; - QList m_selectedNodes; - bool m_explicitSelection = false; - int m_currentIndex = 0; - DynamicPropertiesModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; + + // TODO: Remove. + QList m_selectedNodes = {}; + bool m_explicitSelection = false; }; class DynamicPropertiesModelBackendDelegate : public QObject @@ -137,40 +90,33 @@ class DynamicPropertiesModelBackendDelegate : public QObject Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT) - Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT) Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) - //Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) public: DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr); + void update(const AbstractProperty &property); + signals: - void currentRowChanged(); void nameChanged(); void valueChanged(); void targetNodeChanged(); private: - int currentRow() const; - void setCurrentRow(int i); void handleTypeChanged(); void handleNameChanged(); void handleValueChanged(); - void handleException(); - QVariant variantValue() const; - QString targetNode() const; StudioQmlComboBoxBackend *type(); - StudioQmlTextBackend *name(); StudioQmlTextBackend *value(); + QString targetNode() const; + std::optional m_internalNodeId; StudioQmlComboBoxBackend m_type; StudioQmlTextBackend m_name; StudioQmlTextBackend m_value; - int m_currentRow = -1; - QString m_exceptionError; QString m_targetNode; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 3192269b3ca..ac3a92c5d44 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -834,7 +834,7 @@ void MaterialEditorView::variantPropertiesChanged(const QList & ModelNode node(property.parentModelNode()); if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->variantPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (m_selectedMaterial.property(property.name()).isBindingProperty()) setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); else @@ -869,7 +869,7 @@ void MaterialEditorView::bindingPropertiesChanged(const QList & if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->bindingPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty()) setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); else @@ -897,12 +897,8 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node, void MaterialEditorView::propertiesAboutToBeRemoved(const QList &propertyList) { - for (const auto &property : propertyList) { - if (property.isBindingProperty()) - m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty()); - else if (property.isVariantProperty()) - m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty()); - } + for (const auto &property : propertyList) + m_dynamicPropertiesModel->removeItem(property); } // request render image for the selected material node diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 046f95ed738..45f89ae3392 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -27,6 +27,7 @@ #include "bindingproperty.h" #include "propertyeditorvalue.h" +#include "connectioneditorutils.h" #include @@ -96,7 +97,7 @@ QHash DynamicPropertiesProxyModel::roleNames() const QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const { if (index.isValid() && index.row() < rowCount()) { - AbstractProperty property = m_model->abstractPropertyForRow(index.row()); + AbstractProperty property = m_model->propertyForRow(index.row()); QTC_ASSERT(property.isValid(), return QVariant()); @@ -142,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const { DynamicPropertiesModel *propsModel = dynamicPropertiesModel(); - return QString::fromUtf8(propsModel->unusedProperty(propsModel->singleSelectedNode())); + return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode())); } void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type) @@ -162,12 +163,12 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr return; } try { - if (DynamicPropertiesModel::isValueType(typeName)) { - QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); + if (isDynamicVariantPropertyType(typeName)) { + QVariant value = defaultValueForType(typeName); VariantProperty variantProp = modelNode.variantProperty(name.toUtf8()); variantProp.setDynamicTypeNameAndValue(typeName, value); } else { - QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); + QString expression = defaultExpressionForType(typeName); BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8()); bindingProp.setDynamicTypeNameAndExpression(typeName, expression); @@ -261,7 +262,7 @@ PropertyEditorValue *DynamicPropertyRow::backendValue() const void DynamicPropertyRow::remove() { - m_model->dynamicPropertiesModel()->deleteDynamicPropertyByRow(m_row); + m_model->dynamicPropertiesModel()->remove(m_row); } PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue() @@ -283,7 +284,7 @@ void DynamicPropertyRow::setupBackendValue() if (!m_model) return; - AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row); + AbstractProperty property = m_model->dynamicPropertiesModel()->propertyForRow(m_row); if (!property.isValid()) return; @@ -324,9 +325,9 @@ void DynamicPropertyRow::commitValue(const QVariant &value) return; auto propertiesModel = m_model->dynamicPropertiesModel(); - VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); - if (!DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName())) + if (!isDynamicVariantPropertyType(property.dynamicTypeName())) return; m_lock = true; @@ -335,16 +336,21 @@ void DynamicPropertyRow::commitValue(const QVariant &value) auto view = propertiesModel->view(); RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__); try { - QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); - if (view->currentState().isBaseState() - && !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) { - if (variantProperty.value() != value) - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); - } else { - QTC_CHECK(objectNode.isValid()); - PropertyName name = variantProperty.name(); - if (objectNode.isValid() && objectNode.modelValue(name) != value) - objectNode.setVariantProperty(name, value); + if (property.isBindingProperty()) { + convertBindingToVariantProperty(property.toBindingProperty(), value); + } else if (property.isVariantProperty()) { + VariantProperty variantProperty = property.toVariantProperty(); + QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); + if (view->currentState().isBaseState() + && !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) { + if (variantProperty.value() != value) + variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); + } else { + QTC_CHECK(objectNode.isValid()); + PropertyName name = variantProperty.name(); + if (objectNode.isValid() && objectNode.modelValue(name) != value) + objectNode.setVariantProperty(name, value); + } } transaction.commit(); // committing in the try block } catch (Exception &e) { @@ -358,7 +364,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression) return; auto propertiesModel = m_model->dynamicPropertiesModel(); - AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name()); @@ -413,15 +419,15 @@ void DynamicPropertyRow::resetValue() auto propertiesModel = m_model->dynamicPropertiesModel(); auto view = propertiesModel->view(); - AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); TypeName typeName = property.dynamicTypeName(); if (view->currentState().isBaseState()) { - if (DynamicPropertiesModel::isValueType(typeName)) { - QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); + if (isDynamicVariantPropertyType(typeName)) { + QVariant value = defaultValueForType(typeName); commitValue(value); } else { - QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); + QString expression = defaultExpressionForType(typeName); commitExpression(expression); } } else { diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 7c6ba5fc497..42d909400c3 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -588,7 +588,7 @@ void TextureEditorView::variantPropertiesChanged(const QList &p ModelNode node(property.parentModelNode()); if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->variantPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (m_selectedTexture.property(property.name()).isBindingProperty()) setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); else @@ -612,7 +612,7 @@ void TextureEditorView::bindingPropertiesChanged(const QList &p if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->bindingPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty()) setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); else @@ -642,12 +642,8 @@ void TextureEditorView::auxiliaryDataChanged(const ModelNode &node, void TextureEditorView::propertiesAboutToBeRemoved(const QList &propertyList) { - for (const auto &property : propertyList) { - if (property.isBindingProperty()) - m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty()); - else if (property.isVariantProperty()) - m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty()); - } + for (const auto &property : propertyList) + m_dynamicPropertiesModel->removeItem(property); } void TextureEditorView::nodeReparented(const ModelNode &node,