QmlDesigner: Add assets to navigator in 1 transaction

...so only 1 undo step is created when dragging multiple assets to
navigator.

Change-Id: Ifa812eada5ce56a51e164d80864c3826f9875caf
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2021-06-18 15:52:28 +03:00
parent 1c44dab5af
commit 95a3ce7e25
2 changed files with 119 additions and 125 deletions

View File

@@ -535,7 +535,7 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
if (mimeData->hasFormat("application/vnd.bauhaus.itemlibraryinfo")) { if (mimeData->hasFormat("application/vnd.bauhaus.itemlibraryinfo")) {
handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex); handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex);
} else if (mimeData->hasFormat("application/vnd.bauhaus.libraryresource")) { } else if (mimeData->hasFormat("application/vnd.bauhaus.libraryresource")) {
QStringList assetsPaths = QString::fromUtf8(mimeData->data("application/vnd.bauhaus.libraryresource")).split(","); const QStringList assetsPaths = QString::fromUtf8(mimeData->data("application/vnd.bauhaus.libraryresource")).split(",");
NodeAbstractProperty targetProperty; NodeAbstractProperty targetProperty;
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
@@ -544,24 +544,43 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
if (foundTarget) { if (foundTarget) {
QList<ModelNode> addedNodes; QList<ModelNode> addedNodes;
ModelNode currNode; ModelNode currNode;
for (const QString &assetPath : std::as_const(assetsPaths)) {
auto assetTypeAndData = ItemLibraryWidget::getAssetTypeAndData(assetPath);
QString assetType = assetTypeAndData.first;
QString assetData = QString::fromUtf8(assetTypeAndData.second);
if (assetType == "application/vnd.bauhaus.libraryresource.image")
currNode = handleItemLibraryImageDrop(assetPath, targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.font")
currNode = handleItemLibraryFontDrop(assetData, targetProperty, rowModelIndex); // assetData is fontFamily
else if (assetType == "application/vnd.bauhaus.libraryresource.shader")
currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f", targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.sound")
currNode = handleItemLibrarySoundDrop(assetPath, targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.texture3d")
currNode = handleItemLibraryTexture3dDrop(assetPath, targetProperty, rowModelIndex);
if (currNode.isValid()) QSet<QString> neededImports;
addedNodes.append(currNode); for (const QString &assetPath : assetsPaths) {
} QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first;
if (assetType == "application/vnd.bauhaus.libraryresource.shader")
neededImports.insert("QtQuick3D");
else if (assetType == "application/vnd.bauhaus.libraryresource.sound")
neededImports.insert("QtMultimedia");
if (neededImports.size() == 2)
break;
};
for (const QString &import : std::as_const(neededImports))
addImport(import);
m_view->executeInTransaction("NavigatorTreeModel::dropMimeData", [&] {
for (const QString &assetPath : assetsPaths) {
auto assetTypeAndData = ItemLibraryWidget::getAssetTypeAndData(assetPath);
QString assetType = assetTypeAndData.first;
QString assetData = QString::fromUtf8(assetTypeAndData.second);
if (assetType == "application/vnd.bauhaus.libraryresource.image")
currNode = handleItemLibraryImageDrop(assetPath, targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.font")
currNode = handleItemLibraryFontDrop(assetData, targetProperty, rowModelIndex); // assetData is fontFamily
else if (assetType == "application/vnd.bauhaus.libraryresource.shader")
currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f", targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.sound")
currNode = handleItemLibrarySoundDrop(assetPath, targetProperty, rowModelIndex);
else if (assetType == "application/vnd.bauhaus.libraryresource.texture3d")
currNode = handleItemLibraryTexture3dDrop(assetPath, targetProperty, rowModelIndex);
if (currNode.isValid())
addedNodes.append(currNode);
}
});
if (!addedNodes.isEmpty()) { if (!addedNodes.isEmpty()) {
moveNodesInteractive(targetProperty, addedNodes, rowNumber); moveNodesInteractive(targetProperty, addedNodes, rowNumber);
m_view->setSelectedModelNodes(addedNodes); m_view->setSelectedModelNodes(addedNodes);
@@ -757,14 +776,12 @@ ModelNode NavigatorTreeModel::handleItemLibraryImageDrop(const QString &imagePat
// if dropping an image on an existing image, set the source // if dropping an image on an existing image, set the source
targetNode.variantProperty("source").setValue(imagePathRelative); targetNode.variantProperty("source").setValue(imagePathRelative);
} else { } else {
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryImageDrop", [&] { // create an image
// create an image QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(m_view, imagePath, QPointF(), targetProperty, false);
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(m_view, imagePath, QPointF(), targetProperty, false); if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode()))
if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode())) newModelNode = newItemNode.modelNode();
newModelNode = newItemNode.modelNode(); else
else newItemNode.destroy();
newItemNode.destroy();
});
} }
} }
@@ -781,84 +798,79 @@ ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamil
ModelNode newModelNode; ModelNode newModelNode;
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryFontDrop", [&] { if (targetNode.isSubclassOf("QtQuick.Text")) {
if (targetNode.isSubclassOf("QtQuick.Text")) { // if dropping into an existing Text, update font
// if dropping into an existing Text, update font targetNode.variantProperty("font.family").setValue(fontFamily);
targetNode.variantProperty("font.family").setValue(fontFamily); } else {
} else { // create a Text node
// create a Text node QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont(
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont( m_view, fontFamily, QPointF(), targetProperty, false);
m_view, fontFamily, QPointF(), targetProperty, false); if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode()))
if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode())) newModelNode = newItemNode.modelNode();
newModelNode = newItemNode.modelNode(); else
else newItemNode.destroy();
newItemNode.destroy(); }
}
});
return newModelNode; return newModelNode;
} }
void NavigatorTreeModel::addImport(const QString &importName)
{
Import import = Import::createLibraryImport(importName);
if (!m_view->model()->hasImport(import, true, true)) {
const QList<Import> possImports = m_view->model()->possibleImports();
for (const auto &possImport : possImports) {
if (possImport.url() == import.url()) {
import = possImport;
m_view->model()->changeImports({import}, {});
QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManagerImport(import);
break;
}
}
}
}
ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader, ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader,
NodeAbstractProperty targetProperty, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex) const QModelIndex &rowModelIndex)
{ {
QTC_ASSERT(m_view, return {}); QTC_ASSERT(m_view, return {});
Import import = Import::createLibraryImport(QStringLiteral("QtQuick3D"));
bool addImport = false;
if (!m_view->model()->hasImport(import, true, true)) {
const QList<Import> possImports = m_view->model()->possibleImports();
for (const auto &possImport : possImports) {
if (possImport.url() == import.url()) {
import = possImport;
addImport = true;
m_view->model()->changeImports({import}, {});
break;
}
}
}
ModelNode targetNode(modelNodeForIndex(rowModelIndex)); ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode; ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(shaderPath); const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(shaderPath);
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryShaderDrop", [&] { if (targetNode.isSubclassOf("QtQuick3D.Shader")) {
if (targetNode.isSubclassOf("QtQuick3D.Shader")) { // if dropping into an existing Shader, update
// if dropping into an existing Shader, update targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment"
targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment" : "Shader.Vertex");
: "Shader.Vertex"); targetNode.variantProperty("shader").setValue(relPath);
targetNode.variantProperty("shader").setValue(relPath); } else {
} else { // create a new Shader
// create a new Shader ItemLibraryEntry itemLibraryEntry;
ItemLibraryEntry itemLibraryEntry; itemLibraryEntry.setName("Shader");
itemLibraryEntry.setName("Shader"); itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0);
itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0);
// set shader properties // set shader properties
PropertyName prop = "shader"; PropertyName prop = "shader";
QString type = "QByteArray"; QString type = "QByteArray";
QVariant val = relPath; QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val); itemLibraryEntry.addProperty(prop, type, val);
prop = "stage"; prop = "stage";
type = "enum"; type = "enum";
val = isFragShader ? "Shader.Fragment" : "Shader.Vertex"; val = isFragShader ? "Shader.Fragment" : "Shader.Vertex";
itemLibraryEntry.addProperty(prop, type, val); itemLibraryEntry.addProperty(prop, type, val);
// create a texture // create a texture
newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {}, newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {},
targetProperty, false); targetProperty, false);
// Rename the node based on shader source // Rename the node based on shader source
QFileInfo fi(relPath); QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(), newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(),
"shader")); "shader"));
} }
});
if (addImport)
QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
return newModelNode; return newModelNode;
} }
@@ -869,54 +881,35 @@ ModelNode NavigatorTreeModel::handleItemLibrarySoundDrop(const QString &soundPat
{ {
QTC_ASSERT(m_view, return {}); QTC_ASSERT(m_view, return {});
Import import = Import::createLibraryImport(QStringLiteral("QtMultimedia"));
bool addImport = false;
if (!m_view->model()->hasImport(import, true, true)) {
const QList<Import> possImports = m_view->model()->possibleImports();
for (const auto &possImport : possImports) {
if (possImport.url() == import.url()) {
import = possImport;
addImport = true;
m_view->model()->changeImports({import}, {});
break;
}
}
}
ModelNode targetNode(modelNodeForIndex(rowModelIndex)); ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode; ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(soundPath); const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(soundPath);
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibrarySoundDrop", [&] { if (targetNode.isSubclassOf("QtMultimedia.SoundEffect")) {
if (targetNode.isSubclassOf("QtMultimedia.SoundEffect")) { // if dropping into on an existing SoundEffect, update
// if dropping into on an existing SoundEffect, update targetNode.variantProperty("source").setValue(relPath);
targetNode.variantProperty("source").setValue(relPath); } else {
} else { // create a new SoundEffect
// create a new SoundEffect ItemLibraryEntry itemLibraryEntry;
ItemLibraryEntry itemLibraryEntry; itemLibraryEntry.setName("SoundEffect");
itemLibraryEntry.setName("SoundEffect"); itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0);
itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0);
// set source property // set source property
PropertyName prop = "source"; PropertyName prop = "source";
QString type = "QUrl"; QString type = "QUrl";
QVariant val = relPath; QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val); itemLibraryEntry.addProperty(prop, type, val);
// create a texture // create a texture
newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {}, newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {},
targetProperty, false); targetProperty, false);
// Rename the node based on source // Rename the node based on source
QFileInfo fi(relPath); QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(), newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(),
"soundEffect")); "soundEffect"));
} }
});
if (addImport)
QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
return newModelNode; return newModelNode;
} }

View File

@@ -128,6 +128,7 @@ private:
const QString &imagePath, ModelNode &newNode); const QString &imagePath, ModelNode &newNode);
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath); ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath);
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes); QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
void addImport(const QString &importName);
QPointer<NavigatorView> m_view; QPointer<NavigatorView> m_view;
mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash; mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash;