From c5d4d140a4f34fc9bc81845d49835b39312f5352 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 18 Mar 2025 16:23:24 +0200 Subject: [PATCH] PropertyEditor: Copy material compatible properties in type changes Fixes: QDS-14946 Change-Id: If8f1e15ffd19f99ffdb70ef7650ce5a7c4a07d3a Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../propertyeditor/compatibleproperties.cpp | 110 ++++++++++++++++++ .../propertyeditor/compatibleproperties.h | 49 ++++++++ .../propertyeditorcontextobject.cpp | 8 +- 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index e2d3d8303d2..32d6ba212b8 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -411,6 +411,7 @@ extend_qtc_plugin(QmlDesigner aligndistribute.cpp aligndistribute.h assetimageprovider.cpp assetimageprovider.h colorpalettebackend.cpp colorpalettebackend.h + compatibleproperties.cpp compatibleproperties.h designerpropertymap.cpp designerpropertymap.h fileresourcesmodel.cpp fileresourcesmodel.h itemfiltermodel.cpp itemfiltermodel.h diff --git a/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.cpp b/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.cpp new file mode 100644 index 00000000000..5b1bdbe4cab --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compatibleproperties.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +/*! + * \internal + * \brief Extracts and removes compatible properties from \param properties. + * compatible properties will be stored. + */ +void CompatibleProperties::createCompatibilityMap(QList &properties) +{ + m_compatibilityMap.clear(); + if (m_oldInfo == m_newInfo || !m_oldInfo.isQtQuick3DMaterial() || !m_newInfo.isQtQuick3DMaterial()) + return; + + enum class MaterialType { Unknown, Principled, Default, SpecularGlossy }; + auto getMaterialType = [](const NodeMetaInfo &info) { + if (info.isQtQuick3DPrincipledMaterial()) + return MaterialType::Principled; + else if (info.isQtQuick3DDefaultMaterial()) + return MaterialType::Default; + else if (info.isQtQuick3DSpecularGlossyMaterial()) + return MaterialType::SpecularGlossy; + return MaterialType::Unknown; + }; + + static const QHash baseColors = { + {MaterialType::Principled, "baseColor"}, + {MaterialType::Default, "diffuseColor"}, + {MaterialType::SpecularGlossy, "albedoColor"}, + }; + + static const QHash baseMaps = { + {MaterialType::Principled, "baseColorMap"}, + {MaterialType::Default, "diffuseMap"}, + {MaterialType::SpecularGlossy, "albedoMap"}, + }; + + MaterialType sourceMaterialType = getMaterialType(m_oldInfo); + MaterialType destMaterialType = getMaterialType(m_newInfo); + + if (sourceMaterialType == MaterialType::Unknown || destMaterialType == MaterialType::Unknown) + return; + + if (properties.contains(baseColors.value(sourceMaterialType))) { + m_compatibilityMap.insert(baseColors[sourceMaterialType], baseColors[destMaterialType]); + properties.removeOne(baseColors[sourceMaterialType]); + } + + if (properties.contains(baseMaps[sourceMaterialType])) { + m_compatibilityMap.insert(baseMaps[sourceMaterialType], baseMaps[destMaterialType]); + properties.removeOne(baseMaps[sourceMaterialType]); + } +} + +void CompatibleProperties::copyMappedProperties(const ModelNode &node) +{ + if (m_compatibilityMap.isEmpty()) + return; + // Copy mapped properties to new name. Note that this will only copy the base + // property value and adjust the keyframe groups. Any other bindings related + // to the property will be ignored. + const QList timeLines = QmlObjectNode(node).allTimelines(); + for (const auto &pair : m_compatibilityMap.asKeyValueRange()) { + const PropertyName &key = pair.first; + CopyData ©Data = pair.second; + for (const ModelNode &timeLineNode : timeLines) { + QmlTimeline timeLine(timeLineNode); + if (timeLine.hasKeyframeGroup(node, key)) { + QmlTimelineKeyframeGroup group = timeLine.keyframeGroup(node, key); + group.setPropertyName(copyData.name); + } + } + // Property value itself cannot be copied until type has been changed, so store it + AbstractProperty prop = node.property(key); + if (prop.isValid()) { + if (prop.isBindingProperty()) { + copyData.isBinding = true; + copyData.value = prop.toBindingProperty().expression(); + } else { + copyData.value = prop.toVariantProperty().value(); + } + } + node.removeProperty(key); + } +} + +void CompatibleProperties::applyCompatibleProperties(const ModelNode &node) +{ + for (const auto &pair : m_compatibilityMap.asKeyValueRange()) { + const CopyData ©Data = pair.second; + if (copyData.isBinding) { + BindingProperty property = node.bindingProperty(copyData.name); + property.setExpression(copyData.value.toString()); + } else { + VariantProperty property = node.variantProperty(copyData.name); + property.setValue(copyData.value); + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.h b/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.h new file mode 100644 index 00000000000..bc574f58f63 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/compatibleproperties.h @@ -0,0 +1,49 @@ +// Copyright (C) 2025 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 { + +// Currently this compatibility is only checked for the materials +// If there are more compatible types in the future, we can make a proxy model for Compatibility +class CompatibleProperties +{ +public: + CompatibleProperties(const NodeMetaInfo &oldInfo, NodeMetaInfo &newInfo) + : m_oldInfo(oldInfo) + , m_newInfo(newInfo) + {} + + void createCompatibilityMap(QList &properties); + void copyMappedProperties(const ModelNode &node); + void applyCompatibleProperties(const ModelNode &node); + +private: + void *operator new(size_t) = delete; + + struct CopyData + { + CopyData() = default; + + CopyData(PropertyName n) + : name(n) + {} + + PropertyName name; + QVariant value; + bool isBinding = false; + }; + + NodeMetaInfo m_oldInfo; + NodeMetaInfo m_newInfo; + + QHash m_compatibilityMap; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index effb5ace520..1ff7f85c1a3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -4,6 +4,7 @@ #include "propertyeditorcontextobject.h" #include "abstractview.h" +#include "compatibleproperties.h" #include "easingcurvedialog.h" #include "nodemetainfo.h" #include "propertyeditorutils.h" @@ -189,7 +190,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) * If we add more code here we have to forward the property editor view */ RewriterView *rewriterView = m_model->rewriterView(); - QTC_ASSERT(!rewriterView->selectedModelNodes().isEmpty(), return); + QTC_ASSERT(!m_editorNodes.isEmpty(), return); auto changeNodeTypeName = [&](ModelNode &selectedNode) { // Check if the requested type is the same as already set @@ -244,6 +245,9 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) incompatibleProperties.append(property.name().toByteArray()); } + CompatibleProperties compatibleProps(selectedNode.metaInfo(), metaInfo); + compatibleProps.createCompatibilityMap(incompatibleProperties); + Utils::sort(incompatibleProperties); // Create a dialog showing incompatible properties and signals @@ -274,6 +278,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) selectedNode.removeProperty(p); } + compatibleProps.copyMappedProperties(selectedNode); #ifdef QDS_USE_PROJECTSTORAGE if (selectedNode.isRootNode()) rewriterView->changeRootNodeType(typeName.toUtf8(), -1, -1); @@ -289,6 +294,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) metaInfo.majorVersion(), metaInfo.minorVersion()); #endif + compatibleProps.applyCompatibleProperties(selectedNode); }; try {