diff --git a/src/plugins/qmldesigner/components/componentcore/createtexture.cpp b/src/plugins/qmldesigner/components/componentcore/createtexture.cpp index 2d86c77bb4b..f595e0e47e7 100644 --- a/src/plugins/qmldesigner/components/componentcore/createtexture.cpp +++ b/src/plugins/qmldesigner/components/componentcore/createtexture.cpp @@ -9,12 +9,14 @@ #include #include #include +#include #include #include #include #include -#include +#include #include +#include #include @@ -124,6 +126,90 @@ ModelNode CreateTexture::execute(const QString &filePath, AddTextureMode mode, i return texture; } +/*! + * \brief Duplicates a texture node. + * The duplicate keeps a copy of all of the source node properties. + * It also generates a unique id, and also a name based on its id. + * \param texture The source texture + * \return Duplicate texture + */ +ModelNode CreateTexture::execute(const ModelNode &texture) +{ + QTC_ASSERT(texture.isValid(), return {}); + + if (!m_view || !m_view->model()) + return {}; + + TypeName matType = texture.type(); + QmlObjectNode sourceTexture(texture); + ModelNode duplicateTextureNode; + QList dynamicProps; + + m_view->executeInTransaction(__FUNCTION__, [&] { + ModelNode matLib = Utils3D::materialLibraryNode(m_view); + if (!matLib.isValid()) + return; + + // create the duplicate texture +#ifdef QDS_USE_PROJECTSTORAGE + QmlObjectNode duplicateTex = m_view->createModelNode(matType); +#else + NodeMetaInfo metaInfo = m_view->model()->metaInfo(matType); + QmlObjectNode duplicateTex = m_view->createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif + duplicateTextureNode = duplicateTex.modelNode(); + + // sync properties. Only the base state is duplicated. + const QList props = texture.properties(); + for (const AbstractProperty &prop : props) { + if (prop.name() == "objectName" || prop.name() == "data") + continue; + + if (prop.isVariantProperty()) { + if (prop.isDynamic()) + dynamicProps.append(prop); + else + duplicateTex.setVariantProperty(prop.name(), prop.toVariantProperty().value()); + } else if (prop.isBindingProperty()) { + if (prop.isDynamic()) { + dynamicProps.append(prop); + } else { + duplicateTex.setBindingProperty(prop.name(), + prop.toBindingProperty().expression()); + } + } + } + + const QString preferredId = m_view->model()->generateNewId(sourceTexture.id(), "Texture"); + duplicateTextureNode.setIdWithoutRefactoring(preferredId); + duplicateTextureNode.ensureIdExists(); + duplicateTex.setVariantProperty("objectName", nameFromId(duplicateTex.id(), "Texture"_L1)); + + matLib.defaultNodeListProperty().reparentHere(duplicateTex); + }); + + // For some reason, creating dynamic properties in the same transaction doesn't work, so + // let's do it in separate transaction. + // TODO: Fix the issue and merge transactions (QDS-8094) + if (!dynamicProps.isEmpty()) { + m_view->executeInTransaction(__FUNCTION__, [&] { + for (const AbstractProperty &prop : std::as_const(dynamicProps)) { + if (prop.isVariantProperty()) { + VariantProperty variantProp = duplicateTextureNode.variantProperty(prop.name()); + variantProp.setDynamicTypeNameAndValue(prop.dynamicTypeName(), + prop.toVariantProperty().value()); + } else if (prop.isBindingProperty()) { + BindingProperty bindingProp = duplicateTextureNode.bindingProperty(prop.name()); + bindingProp.setDynamicTypeNameAndExpression(prop.dynamicTypeName(), + prop.toBindingProperty().expression()); + } + } + }); + } + + return duplicateTextureNode; +} + bool CreateTexture::addFileToProject(const QString &filePath) { AddFilesResult result = ModelNodeOperations::addImageToProject( diff --git a/src/plugins/qmldesigner/components/componentcore/createtexture.h b/src/plugins/qmldesigner/components/componentcore/createtexture.h index 0f276cdbcd2..450a7f7a0db 100644 --- a/src/plugins/qmldesigner/components/componentcore/createtexture.h +++ b/src/plugins/qmldesigner/components/componentcore/createtexture.h @@ -27,6 +27,7 @@ public: ModelNode execute(const QString &filePath, AddTextureMode mode = AddTextureMode::Texture, int sceneId = -1); + ModelNode execute(const ModelNode &texture); ModelNode resolveSceneEnv(int sceneId); void assignTextureAsLightProbe(const ModelNode &texture, int sceneId); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 531ae8850b1..94c150626d6 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -845,74 +845,7 @@ void TextureEditorView::importsChanged([[maybe_unused]] const Imports &addedImpo void TextureEditorView::duplicateTexture(const ModelNode &texture) { QTC_ASSERT(texture.isValid(), return); - - if (!model()) - return; - - TypeName matType = texture.type(); - QmlObjectNode sourceTexture(texture); - ModelNode duplicateTextureNode; - QList dynamicProps; - - executeInTransaction(__FUNCTION__, [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; - - // create the duplicate texture -#ifdef QDS_USE_PROJECTSTORAGE - QmlObjectNode duplicateTex = createModelNode(matType); -#else - NodeMetaInfo metaInfo = model()->metaInfo(matType); - QmlObjectNode duplicateTex = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); -#endif - duplicateTextureNode = duplicateTex .modelNode(); - duplicateTextureNode.ensureIdExists(); - - // sync properties. Only the base state is duplicated. - const QList props = texture.properties(); - for (const AbstractProperty &prop : props) { - if (prop.name() == "objectName" || prop.name() == "data") - continue; - - if (prop.isVariantProperty()) { - if (prop.isDynamic()) { - dynamicProps.append(prop); - } else { - duplicateTextureNode.variantProperty(prop.name()) - .setValue(prop.toVariantProperty().value()); - } - } else if (prop.isBindingProperty()) { - if (prop.isDynamic()) { - dynamicProps.append(prop); - } else { - duplicateTextureNode.bindingProperty(prop.name()) - .setExpression(prop.toBindingProperty().expression()); - } - } - } - - matLib.defaultNodeListProperty().reparentHere(duplicateTex); - }); - - // For some reason, creating dynamic properties in the same transaction doesn't work, so - // let's do it in separate transaction. - // TODO: Fix the issue and merge transactions (QDS-8094) - if (!dynamicProps.isEmpty()) { - executeInTransaction(__FUNCTION__, [&] { - for (const AbstractProperty &prop : std::as_const(dynamicProps)) { - if (prop.isVariantProperty()) { - duplicateTextureNode.variantProperty(prop.name()) - .setDynamicTypeNameAndValue(prop.dynamicTypeName(), - prop.toVariantProperty().value()); - } else if (prop.isBindingProperty()) { - duplicateTextureNode.bindingProperty(prop.name()) - .setDynamicTypeNameAndExpression(prop.dynamicTypeName(), - prop.toBindingProperty().expression()); - } - } - }); - } + m_createTexture->execute(texture); } void TextureEditorView::customNotification([[maybe_unused]] const AbstractView *view, diff --git a/src/plugins/qmldesigner/libs/designercore/designercoreutils/uniquename.cpp b/src/plugins/qmldesigner/libs/designercore/designercoreutils/uniquename.cpp index d372b6cfb92..7a6e889be47 100644 --- a/src/plugins/qmldesigner/libs/designercore/designercoreutils/uniquename.cpp +++ b/src/plugins/qmldesigner/libs/designercore/designercoreutils/uniquename.cpp @@ -82,7 +82,7 @@ QString generate(const QString &name, std::function predi return name; // match prefix and number (including zero padding) parts - static QRegularExpression rgx("(\\D*?)(\\d+)$"); + static const QRegularExpression rgx(R"((\D\w*?)(\d+)$)"); QRegularExpressionMatch match = rgx.match(name); QString prefix; diff --git a/tests/unit/tests/unittests/designercoreutils/uniquename-test.cpp b/tests/unit/tests/unittests/designercoreutils/uniquename-test.cpp index 5967936713e..5a42888b116 100644 --- a/tests/unit/tests/unittests/designercoreutils/uniquename-test.cpp +++ b/tests/unit/tests/unittests/designercoreutils/uniquename-test.cpp @@ -90,6 +90,16 @@ TEST(UniqueName, generateId_stable_captilzation) ASSERT_THAT(uniqueId, "aCaMeLCAsE"); } +TEST(UniqueName, generateId_contains_number_in_prefix_and_suffix) +{ + QString id = "_4KScartchySurface6"; + auto pred = [&](const QString &str) -> bool { return str == id; }; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "_4KScartchySurface7"); +} + TEST(UniqueName, generateId_begins_with_non_latin) { QString id = "😂_saneId";