forked from qt-creator/qt-creator
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:
@@ -411,6 +411,7 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
aligndistribute.cpp aligndistribute.h
|
aligndistribute.cpp aligndistribute.h
|
||||||
assetimageprovider.cpp assetimageprovider.h
|
assetimageprovider.cpp assetimageprovider.h
|
||||||
colorpalettebackend.cpp colorpalettebackend.h
|
colorpalettebackend.cpp colorpalettebackend.h
|
||||||
|
compatibleproperties.cpp compatibleproperties.h
|
||||||
designerpropertymap.cpp designerpropertymap.h
|
designerpropertymap.cpp designerpropertymap.h
|
||||||
fileresourcesmodel.cpp fileresourcesmodel.h
|
fileresourcesmodel.cpp fileresourcesmodel.h
|
||||||
itemfiltermodel.cpp itemfiltermodel.h
|
itemfiltermodel.cpp itemfiltermodel.h
|
||||||
|
@@ -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 ©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
|
@@ -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
|
@@ -4,6 +4,7 @@
|
|||||||
#include "propertyeditorcontextobject.h"
|
#include "propertyeditorcontextobject.h"
|
||||||
|
|
||||||
#include "abstractview.h"
|
#include "abstractview.h"
|
||||||
|
#include "compatibleproperties.h"
|
||||||
#include "easingcurvedialog.h"
|
#include "easingcurvedialog.h"
|
||||||
#include "nodemetainfo.h"
|
#include "nodemetainfo.h"
|
||||||
#include "propertyeditorutils.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 */
|
* If we add more code here we have to forward the property editor view */
|
||||||
RewriterView *rewriterView = m_model->rewriterView();
|
RewriterView *rewriterView = m_model->rewriterView();
|
||||||
|
|
||||||
QTC_ASSERT(!rewriterView->selectedModelNodes().isEmpty(), return);
|
QTC_ASSERT(!m_editorNodes.isEmpty(), return);
|
||||||
|
|
||||||
auto changeNodeTypeName = [&](ModelNode &selectedNode) {
|
auto changeNodeTypeName = [&](ModelNode &selectedNode) {
|
||||||
// Check if the requested type is the same as already set
|
// 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());
|
incompatibleProperties.append(property.name().toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompatibleProperties compatibleProps(selectedNode.metaInfo(), metaInfo);
|
||||||
|
compatibleProps.createCompatibilityMap(incompatibleProperties);
|
||||||
|
|
||||||
Utils::sort(incompatibleProperties);
|
Utils::sort(incompatibleProperties);
|
||||||
|
|
||||||
// Create a dialog showing incompatible properties and signals
|
// Create a dialog showing incompatible properties and signals
|
||||||
@@ -274,6 +278,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName)
|
|||||||
selectedNode.removeProperty(p);
|
selectedNode.removeProperty(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compatibleProps.copyMappedProperties(selectedNode);
|
||||||
#ifdef QDS_USE_PROJECTSTORAGE
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
if (selectedNode.isRootNode())
|
if (selectedNode.isRootNode())
|
||||||
rewriterView->changeRootNodeType(typeName.toUtf8(), -1, -1);
|
rewriterView->changeRootNodeType(typeName.toUtf8(), -1, -1);
|
||||||
@@ -289,6 +294,7 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName)
|
|||||||
metaInfo.majorVersion(),
|
metaInfo.majorVersion(),
|
||||||
metaInfo.minorVersion());
|
metaInfo.minorVersion());
|
||||||
#endif
|
#endif
|
||||||
|
compatibleProps.applyCompatibleProperties(selectedNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Reference in New Issue
Block a user