PropertyEditor: Copy material compatible properties in type changes

Fixes: QDS-14946
Change-Id: If8f1e15ffd19f99ffdb70ef7650ce5a7c4a07d3a
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Ali Kianian
2025-03-18 16:23:24 +02:00
parent a22aa8d14e
commit c5d4d140a4
4 changed files with 167 additions and 1 deletions

View File

@@ -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

View File

@@ -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 <bindingproperty.h>
#include <qmlobjectnode.h>
#include <qmltimelinekeyframegroup.h>
#include <variantproperty.h>
namespace QmlDesigner {
/*!
* \internal
* \brief Extracts and removes compatible properties from \param properties.
* compatible properties will be stored.
*/
void CompatibleProperties::createCompatibilityMap(QList<PropertyName> &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<MaterialType, PropertyName> baseColors = {
{MaterialType::Principled, "baseColor"},
{MaterialType::Default, "diffuseColor"},
{MaterialType::SpecularGlossy, "albedoColor"},
};
static const QHash<MaterialType, PropertyName> 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<ModelNode> timeLines = QmlObjectNode(node).allTimelines();
for (const auto &pair : m_compatibilityMap.asKeyValueRange()) {
const PropertyName &key = pair.first;
CopyData &copyData = 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 &copyData = 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

View File

@@ -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 <modelfwd.h>
#include <nodemetainfo.h>
#include <QHash>
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<PropertyName> &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<PropertyName, CopyData> m_compatibilityMap;
};
} // namespace QmlDesigner

View File

@@ -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 {