From c5f2f7984cbf35c394a338c4969d251d6014e0bf Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 11 Mar 2025 16:46:22 +0200 Subject: [PATCH] QmlDesigner: Handle multiselection for Materials and textures * Material Preview is removed for multiselected nodes * Name field shows `multiselection` * Showing material type in multiselection is handled * Setting a type for multiselected materials is handled * Renaming a material/texture changes the id as well Task-number: QDS-14895 Change-Id: I61b6ebf8bf320f5a9e27e318f66d6823d9e91272 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../QtQuick3D/Material/Preview.qml | 1 - .../QtQuick3D/Material/TopSection.qml | 5 +- .../QtQuick3D/MaterialPane.qml | 37 ++++++++----- .../propertyeditorcontextobject.cpp | 45 ++++++++++------ .../propertyeditorqmlbackend.cpp | 52 +++++++++++-------- .../propertyeditor/propertyeditorqmlbackend.h | 4 ++ .../propertyeditor/propertyeditorview.cpp | 12 +++++ .../propertyeditor/qmlmaterialnodeproxy.cpp | 35 ++++++++++++- .../propertyeditor/qmlmodelnodeproxy.cpp | 11 ++++ .../propertyeditor/qmlmodelnodeproxy.h | 2 + 10 files changed, 150 insertions(+), 54 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/Preview.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/Preview.qml index 8dd04372b06..5dcedfa48d0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/Preview.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/Preview.qml @@ -44,7 +44,6 @@ Rectangle { image.source = "image://nodeInstance/preview" } - Connections { target: root.backend diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/TopSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/TopSection.qml index b52befa0a0f..cc0e8a015b0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/TopSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Material/TopSection.qml @@ -15,13 +15,15 @@ StudioControls.SplitView { property Component previewComponent: null width: parent.width - implicitHeight: showImage ? previewLoader.implicitHeight + nameSection.implicitHeight : nameSection.implicitHeight + implicitHeight: showImage ? previewLoader.activeHeight + nameSection.implicitHeight : nameSection.implicitHeight orientation: Qt.Vertical Loader { id: previewLoader + property real activeHeight: previewLoader.active ? implicitHeight : 0 + SplitView.fillWidth: true SplitView.minimumWidth: 152 SplitView.preferredHeight: previewLoader.visible ? Math.min(root.width * 0.75, 400) : 0 @@ -58,6 +60,7 @@ StudioControls.SplitView { placeholderText: qsTr("Material name") showTranslateCheckBox: false showExtendedFunctionButton: false + enabled: !hasMultiSelection Timer { running: true diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/MaterialPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/MaterialPane.qml index b8174ad4be6..db814b1bc7a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/MaterialPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/MaterialPane.qml @@ -6,6 +6,7 @@ import QtQuick import QtQuick.Controls import HelperWidgets 2.0 import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme import "Material" as Material Item { @@ -58,7 +59,7 @@ Item { active: splitView.isHorizontal visible: leftSideView.active && leftSideView.item - sourceComponent: PreviewComponent {} + sourceComponent: hasMultiSelection ? blankPreview : preview } PropertyEditorPane { @@ -77,8 +78,8 @@ Item { Component.onCompleted: topSection.restoreState(settings.topSection) Component.onDestruction: settings.topSection = topSection.saveState() - previewComponent: PreviewComponent {} - showImage: !splitView.isHorizontal + previewComponent: preview + showImage: !hasMultiSelection && !splitView.isHorizontal } DynamicPropertiesSection { @@ -119,19 +120,31 @@ Item { } } - component PreviewComponent : Material.Preview { - id: previewItem + Component { + id: preview - pinned: settings.dockMode - showPinButton: !leftSideView.visible - onPinnedChanged: settings.dockMode = previewItem.pinned + Material.Preview { + id: previewItem - Connections { - target: root + pinned: settings.dockMode + showPinButton: !leftSideView.visible + onPinnedChanged: settings.dockMode = previewItem.pinned - function onRefreshPreview() { - previewItem.refreshPreview() + Connections { + target: root + + function onRefreshPreview() { + previewItem.refreshPreview() + } } } } + + Component { + id: blankPreview + + Rectangle { + color: StudioTheme.Values.themePanelBackground + } + } } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 331f6ad269b..df9c8cc8ece 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -191,18 +191,15 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) QTC_ASSERT(!rewriterView->selectedModelNodes().isEmpty(), return); - try { - auto transaction = RewriterTransaction(rewriterView, "PropertyEditorContextObject:changeTypeName"); - - ModelNode selectedNode = rewriterView->selectedModelNodes().constFirst(); - + auto changeNodeTypeName = [&](ModelNode &selectedNode) { // Check if the requested type is the same as already set if (selectedNode.simplifiedTypeName() == typeName) return; NodeMetaInfo metaInfo = m_model->metaInfo(typeName.toLatin1()); if (!metaInfo.isValid()) { - Core::AsynchronousMessageBox::warning(tr("Invalid Type"), tr("%1 is an invalid type.").arg(typeName)); + Core::AsynchronousMessageBox::warning(tr("Invalid Type"), + tr("%1 is an invalid type.").arg(typeName)); return; } @@ -210,7 +207,9 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) auto propertiesAndSignals = Utils::transform( PropertyEditorUtils::filteredProperties(metaInfo), &PropertyMetaInfo::name); // Add signals to the list - for (const auto &signal : metaInfo.signalNames()) { + + const PropertyNameList &signalNames = metaInfo.signalNames(); + for (const PropertyName &signal : signalNames) { if (signal.isEmpty()) continue; @@ -222,7 +221,8 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) } // Add dynamic properties and respective change signals - for (const auto &property : selectedNode.properties()) { + const QList &nodeProperties = selectedNode.properties(); + for (const AbstractProperty &property : nodeProperties) { if (!property.isDynamic()) continue; @@ -239,7 +239,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) // Compare current properties and signals with the once available for change type QList incompatibleProperties; - for (const auto &property : selectedNode.properties()) { + for (const AbstractProperty &property : nodeProperties) { if (!propertiesAndSignals.contains(property.name())) incompatibleProperties.append(property.name().toByteArray()); } @@ -259,11 +259,11 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) msgBox.setTextFormat(Qt::RichText); msgBox.setIcon(QMessageBox::Question); msgBox.setWindowTitle("Change Type"); - msgBox.setText(QString("Changing the type from %1 to %2 can't be done without removing incompatible properties.

%3") - .arg(selectedNode.simplifiedTypeName()) - .arg(typeName) - .arg(detailedText)); - msgBox.setInformativeText("Do you want to continue by removing incompatible properties?"); + msgBox.setText(QString("Changing the type from %1 to %2 can't be done without removing " + "incompatible properties.

%3") + .arg(selectedNode.simplifiedTypeName(), typeName, detailedText)); + msgBox.setInformativeText( + "Do you want to continue by removing incompatible properties?"); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); @@ -281,10 +281,23 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) selectedNode.changeType(typeName.toUtf8(), -1, -1); #else if (selectedNode.isRootNode()) - rewriterView->changeRootNodeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); + rewriterView->changeRootNodeType(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); else - selectedNode.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); + selectedNode.changeType(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); #endif + }; + + try { + auto transaction = RewriterTransaction(rewriterView, "PropertyEditorContextObject:changeTypeName"); + + ModelNodes selectedNodes = rewriterView->selectedModelNodes(); // TODO: replace it by PropertyEditorView::currentNodes() + for (ModelNode &selectedNode : selectedNodes) + changeNodeTypeName(selectedNode); + transaction.commit(); } catch (const Exception &e) { e.showException(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index f97d9b6dfeb..362efce6cf9 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -535,6 +535,31 @@ void QmlDesigner::PropertyEditorQmlBackend::createPropertyEditorValues(const Qml #endif } +PropertyEditorValue *PropertyEditorQmlBackend::insertValue(const QString &name, + const QVariant &value, + const ModelNode &modelNode) +{ + auto valueObject = qobject_cast( + variantToQObject(m_backendValuesPropertyMap.value(name))); + if (!valueObject) + valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap); + valueObject->setName(name.toLatin1()); + + if (modelNode) + valueObject->setModelNode(modelNode); + + if (value.isValid()) + valueObject->setValue(value); + + QObject::connect(valueObject, + &PropertyEditorValue::valueChanged, + &backendValuesPropertyMap(), + &DesignerPropertyMap::valueChanged); + m_backendValuesPropertyMap.insert(name, QVariant::fromValue(valueObject)); + + return valueObject; +} + void PropertyEditorQmlBackend::updateInstanceImage() { m_view->instanceImageProvider()->invalidate(); @@ -564,29 +589,12 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q m_backendMaterialNode.setup(qmlObjectNode); m_backendTextureNode.setup(qmlObjectNode); - // className - auto valueObject = qobject_cast(variantToQObject( - m_backendValuesPropertyMap.value(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY))); - if (!valueObject) - valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap); - valueObject->setName(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY); - valueObject->setModelNode(qmlObjectNode.modelNode()); - valueObject->setValue(m_backendModelNode.simplifiedTypeName()); - QObject::connect(valueObject, - &PropertyEditorValue::valueChanged, - &backendValuesPropertyMap(), - &DesignerPropertyMap::valueChanged); - m_backendValuesPropertyMap.insert(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY, - QVariant::fromValue(valueObject)); + insertValue(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY, + m_backendModelNode.simplifiedTypeName(), + qmlObjectNode.modelNode()); - // id - valueObject = qobject_cast(variantToQObject(m_backendValuesPropertyMap.value(QLatin1String("id")))); - if (!valueObject) - valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap); - valueObject->setName("id"); - valueObject->setValue(m_backendModelNode.nodeId()); - QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged); - m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject)); + insertValue("id"_L1, m_backendModelNode.nodeId()); + insertValue("objectName"_L1, m_backendModelNode.nodeObjectName()); QmlItemNode itemNode(qmlObjectNode.modelNode()); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index e58822b076e..d4304b3d8ab 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -113,6 +113,10 @@ private: const NodeMetaInfo &type); void createPropertyEditorValues(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); + PropertyEditorValue *insertValue(const QString &name, + const QVariant &value = {}, + const ModelNode &modelNode = {}); + static QUrl fileToUrl(const QString &filePath); static QString fileFromUrl(const QUrl &url); #ifndef QDS_USE_PROJECTSTORAGE diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 27d20dcc98f..aa81d3b6d11 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -135,6 +135,18 @@ void PropertyEditorView::changeValue(const QString &name) return; } + if (propertyName == "objectName" && currentNodes().size() == 1) { + if (activeNode().metaInfo().isQtQuick3DMaterial() + || activeNode().metaInfo().isQtQuick3DTexture()) { + PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName( + "objectName"); + const QString &newObjectName = value->value().toString(); + QmlObjectNode objectNode(activeNode()); + objectNode.setNameAndId(newObjectName, QString::fromLatin1(activeNode().type())); + return; + } + } + PropertyName underscoreName(propertyName); underscoreName.replace('.', '_'); PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName)); diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp index 398b8694055..d2011e8e087 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp @@ -16,6 +16,19 @@ namespace QmlDesigner { using namespace Qt::StringLiterals; +static bool allMaterialTypesAre(const ModelNodes &materials, const QString &materialType) +{ + if (materials.isEmpty()) + return false; + + for (const ModelNode &material : materials) { + if (material.simplifiedTypeName() != materialType) + return false; + } + + return true; +} + QmlMaterialNodeProxy::QmlMaterialNodeProxy() : QObject() , m_previewUpdateTimer(this) @@ -62,9 +75,27 @@ void QmlMaterialNodeProxy::updatePossibleTypes() "SpecularGlossyMaterial", }; + if (!materialNode()) { + setPossibleTypes({}); + return; + } + const QString &matType = materialNode().simplifiedTypeName(); - setPossibleTypes(basicTypes.contains(matType) ? basicTypes : QStringList{matType}); - setCurrentType(matType); + const ModelNodes selectedNodes = materialView()->selectedModelNodes(); // TODO: replace it by PropertyEditorView::currentNodes() + bool allAreBasic = Utils::allOf(selectedNodes, [&](const ModelNode &node) { + return basicTypes.contains(node.simplifiedTypeName()); + }); + + if (allAreBasic) { + setPossibleTypes(basicTypes); + setCurrentType(matType); + } else if (allMaterialTypesAre(selectedNodes, matType)) { + setPossibleTypes(QStringList{matType}); + setCurrentType(matType); + } else { + setPossibleTypes(QStringList{"multiselection"}); + setCurrentType("multiselection"); + } } void QmlMaterialNodeProxy::setCurrentType(const QString &type) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index 76dc3c8a22e..1e6fe6a1e3e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -80,6 +80,17 @@ QString QmlModelNodeProxy::nodeId() const return m_qmlObjectNode.id(); } +QString QmlModelNodeProxy::nodeObjectName() const +{ + if (!m_qmlObjectNode.isValid()) + return {}; + + if (multiSelection()) + return tr("multiselection"); + + return m_qmlObjectNode.modelNode().variantProperty("objectName").value().toString(); +} + QString QmlModelNodeProxy::simplifiedTypeName() const { if (!m_qmlObjectNode.isValid()) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h index 09e597e84d5..207594b15b3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h @@ -38,6 +38,8 @@ public: QString nodeId() const; + QString nodeObjectName() const; + QString simplifiedTypeName() const; Q_INVOKABLE QList allChildren(int internalId = -1) const;