From d238c503992dd56f16c7820c362906a1460f91db Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 29 Sep 2022 16:05:48 +0300 Subject: [PATCH] QmlDesigner: Store current value at material property copy Now we store the current value of copied properties at copy time instead of just storing a reference to copied property. This ensures we paste the correct value. When copying all properties, properties set by base state, current state, and active timeline are copied. Fixes: QDS-7804 Change-Id: Id6315dde96b30304fde007a87da578faaab43233 Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../materialbrowser/materialbrowsermodel.cpp | 53 +++++++++++++++++-- .../materialbrowser/materialbrowsermodel.h | 17 ++++-- .../materialbrowser/materialbrowserview.cpp | 36 +++++++++---- 3 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index dd828de352e..e9af23eb804 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -29,7 +29,8 @@ #include #include #include -#include "variantproperty.h" +#include +#include #include "utils/qtcassert.h" namespace QmlDesigner { @@ -349,6 +350,9 @@ void MaterialBrowserModel::duplicateMaterial(int idx) void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §ion) { m_copiedMaterial = m_materialList.at(idx); + + QTC_ASSERT(m_copiedMaterial.isValid(), return); + QString matType = QString::fromLatin1(m_copiedMaterial.type()); if (matType.startsWith("QtQuick3D.")) @@ -356,27 +360,66 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io setCopiedMaterialType(matType); m_allPropsCopied = section == "All"; + QmlObjectNode mat(m_copiedMaterial); + + QSet validProps; + PropertyNameList copiedProps; + + // Base state properties are always valid + const auto baseProps = m_copiedMaterial.propertyNames(); + for (const auto &baseProp : baseProps) + validProps.insert(baseProp); + + if (!mat.isInBaseState()) { + QmlPropertyChanges changes = mat.propertyChangeForCurrentState(); + if (changes.isValid()) { + const QList changedProps = changes.targetProperties(); + for (const auto &changedProp : changedProps) + validProps.insert(changedProp.name()); + } + } + + if (mat.timelineIsActive()) { + const QList keyframeGroups + = mat.currentTimeline().keyframeGroupsForTarget(m_copiedMaterial); + for (const auto &kfg : keyframeGroups) + validProps.insert(kfg.propertyName()); + } if (m_allPropsCopied || m_propertyGroupsObj.empty()) { - m_copiedMaterialProps = m_copiedMaterial.properties(); + copiedProps = validProps.values(); } else { QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); if (propsSpecObj.contains(section)) { // should always be true - m_copiedMaterialProps.clear(); const QJsonArray propNames = propsSpecObj.value(section).toArray(); // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before for (const auto &propName : propNames) - m_copiedMaterialProps.append(m_copiedMaterial.property(propName.toString().toLatin1())); + copiedProps.append(propName.toString().toLatin1()); if (section == "Base") { // add QtQuick3D.Material base props as well QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject(); const QJsonArray propNames = propsMatObj.value("Base").toArray(); // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before for (const auto &propName : propNames) - m_copiedMaterialProps.append(m_copiedMaterial.property(propName.toString().toLatin1())); + copiedProps.append(propName.toString().toLatin1()); } } } + + m_copiedMaterialProps.clear(); + for (const auto &propName : copiedProps) { + PropertyCopyData data; + data.name = propName; + data.isValid = m_allPropsCopied || validProps.contains(propName); + data.isBinding = mat.hasBindingProperty(propName); + if (data.isValid) { + if (data.isBinding) + data.value = mat.expression(propName); + else + data.value = mat.modelValue(propName); + } + m_copiedMaterialProps.append(data); + } } void MaterialBrowserModel::pasteMaterialProperties(int idx) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 704cdcb644a..c054d07527b 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -94,6 +94,14 @@ public: Q_INVOKABLE void openMaterialEditor(); Q_INVOKABLE bool isCopiedMaterialValid() const; + struct PropertyCopyData + { + PropertyName name; + QVariant value; + bool isBinding = false; + bool isValid = false; + }; + signals: void isEmptyChanged(); void hasQuick3DImportChanged(); @@ -106,9 +114,10 @@ signals: void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false); void addNewMaterialTriggered(); void duplicateMaterialTriggered(const QmlDesigner::ModelNode &material); - void pasteMaterialPropertiesTriggered(const QmlDesigner::ModelNode &material, - const QList &props, - bool all); + void pasteMaterialPropertiesTriggered( + const QmlDesigner::ModelNode &material, + const QList &props, + bool all); private: bool isMaterialVisible(int idx) const; @@ -120,7 +129,7 @@ private: QStringList m_principledMaterialSections; QStringList m_customMaterialSections; ModelNode m_copiedMaterial; - QList m_copiedMaterialProps; + QList m_copiedMaterialProps; QHash m_materialIndexHash; // internalId -> index QJsonObject m_propertyGroupsObj; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 13f7ad211d5..90ef925ac43 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -96,29 +96,43 @@ WidgetInfo MaterialBrowserView::widgetInfo() }); connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this, - [&] (const ModelNode &material, const QList &props, bool all) { + [&] (const ModelNode &material, + const QList &propDatas, + bool all) { QmlObjectNode mat(material); executeInTransaction(__FUNCTION__, [&] { if (all) { // all material properties copied // remove current properties - const PropertyNameList propNames = material.propertyNames(); - for (const PropertyName &propName : propNames) { + PropertyNameList propNames; + if (mat.isInBaseState()) { + propNames = material.propertyNames(); + } else { + QmlPropertyChanges changes = mat.propertyChangeForCurrentState(); + if (changes.isValid()) { + const QList changedProps = changes.targetProperties(); + for (const auto &changedProp : changedProps) + propNames.append(changedProp.name()); + } + } + for (const PropertyName &propName : qAsConst(propNames)) { if (propName != "objectName") mat.removeProperty(propName); } } // apply pasted properties - for (const AbstractProperty &prop : props) { - if (prop.name() == "objectName" || !prop.isValid()) + for (const QmlDesigner::MaterialBrowserModel::PropertyCopyData &propData : propDatas) { + if (propData.name == "objectName") continue; - if (prop.isVariantProperty()) - mat.setVariantProperty(prop.name(), prop.toVariantProperty().value()); - else if (prop.isBindingProperty()) - mat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); - else if (!all) - mat.removeProperty(prop.name()); + if (propData.isValid) { + if (propData.isBinding) + mat.setBindingProperty(propData.name, propData.value.toString()); + else + mat.setVariantProperty(propData.name, propData.value); + } else { + mat.removeProperty(propData.name); + } } }); });