QmlDesigner: Set id and name for the duplicate textures

* Also fixes a bug in UniqueName. Case `_4KScartchySurface6` can be a
valid id now.

Fixes: QDS-13682
Change-Id: Idc1ae984a48c44dd531fe5458184fc61d655826d
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2024-09-17 15:45:16 +03:00
parent 5960b320b9
commit 1c8fbc5c3f
5 changed files with 100 additions and 70 deletions

View File

@@ -9,12 +9,14 @@
#include <documentmanager.h> #include <documentmanager.h>
#include <modelnode.h> #include <modelnode.h>
#include <modelnodeoperations.h> #include <modelnodeoperations.h>
#include <modelutils.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <qmlobjectnode.h> #include <qmlobjectnode.h>
#include <variantproperty.h> #include <uniquename.h>
#include <utils3d.h> #include <utils3d.h>
#include <variantproperty.h>
#include <coreplugin/messagebox.h> #include <coreplugin/messagebox.h>
@@ -124,6 +126,90 @@ ModelNode CreateTexture::execute(const QString &filePath, AddTextureMode mode, i
return texture; 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<AbstractProperty> 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<AbstractProperty> 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) bool CreateTexture::addFileToProject(const QString &filePath)
{ {
AddFilesResult result = ModelNodeOperations::addImageToProject( AddFilesResult result = ModelNodeOperations::addImageToProject(

View File

@@ -27,6 +27,7 @@ public:
ModelNode execute(const QString &filePath, ModelNode execute(const QString &filePath,
AddTextureMode mode = AddTextureMode::Texture, AddTextureMode mode = AddTextureMode::Texture,
int sceneId = -1); int sceneId = -1);
ModelNode execute(const ModelNode &texture);
ModelNode resolveSceneEnv(int sceneId); ModelNode resolveSceneEnv(int sceneId);
void assignTextureAsLightProbe(const ModelNode &texture, int sceneId); void assignTextureAsLightProbe(const ModelNode &texture, int sceneId);

View File

@@ -845,74 +845,7 @@ void TextureEditorView::importsChanged([[maybe_unused]] const Imports &addedImpo
void TextureEditorView::duplicateTexture(const ModelNode &texture) void TextureEditorView::duplicateTexture(const ModelNode &texture)
{ {
QTC_ASSERT(texture.isValid(), return); QTC_ASSERT(texture.isValid(), return);
m_createTexture->execute(texture);
if (!model())
return;
TypeName matType = texture.type();
QmlObjectNode sourceTexture(texture);
ModelNode duplicateTextureNode;
QList<AbstractProperty> 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<AbstractProperty> 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());
}
}
});
}
} }
void TextureEditorView::customNotification([[maybe_unused]] const AbstractView *view, void TextureEditorView::customNotification([[maybe_unused]] const AbstractView *view,

View File

@@ -82,7 +82,7 @@ QString generate(const QString &name, std::function<bool(const QString &)> predi
return name; return name;
// match prefix and number (including zero padding) parts // 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); QRegularExpressionMatch match = rgx.match(name);
QString prefix; QString prefix;

View File

@@ -90,6 +90,16 @@ TEST(UniqueName, generateId_stable_captilzation)
ASSERT_THAT(uniqueId, "aCaMeLCAsE"); 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) TEST(UniqueName, generateId_begins_with_non_latin)
{ {
QString id = "😂_saneId"; QString id = "😂_saneId";