QmlDesigner: Move the functions to handle assets drops

Moving those functions to ModelNodeOperations allows reuse in
other views like e.g. the TextEditor.

Change-Id: I7eee1c6080b4208ffaab6637f0debf78ec648c8e
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Thomas Hartmann
2023-11-01 14:39:28 +01:00
parent 94e55865f7
commit fc1c720aec
4 changed files with 474 additions and 360 deletions

View File

@@ -12,20 +12,22 @@
#include "addsignalhandlerdialog.h"
#include <bindingproperty.h>
#include <nodelistproperty.h>
#include <nodehints.h>
#include <nodemetainfo.h>
#include <modelnode.h>
#include <qmlitemnode.h>
#include <variantproperty.h>
#include <rewritingexception.h>
#include <rewritertransaction.h>
#include <choosefrompropertylistdialog.h>
#include <documentmanager.h>
#include <qmlanchors.h>
#include <nodelistproperty.h>
#include <nodeproperty.h>
#include <signalhandlerproperty.h>
#include <itemlibraryentry.h>
#include <materialutils.h>
#include <modelnode.h>
#include <nodehints.h>
#include <nodeinstanceview.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <nodeproperty.h>
#include <qmlanchors.h>
#include <qmlitemnode.h>
#include <rewritertransaction.h>
#include <rewritingexception.h>
#include <signalhandlerproperty.h>
#include <variantproperty.h>
#include <componentcore_constants.h>
#include <stylesheetmerger.h>
@@ -1739,6 +1741,409 @@ void jumpToCodeOperation(const SelectionContext &selectionState)
jumpToCode(selectionState.currentSingleSelectedNode());
}
static bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const ModelNode &node)
{
NodeAbstractProperty parentProp = targetProperty.parentProperty();
if (parentProp.isValid()) {
ModelNode targetModel = parentProp.parentModelNode();
parentProp.reparentHere(node);
return true;
}
return false;
}
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath)
{
AbstractView *view = targetProp.view();
QTC_ASSERT(view, return {});
if (targetProp.isValid()) {
// create a texture item lib
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("Texture");
itemLibraryEntry.setType("QtQuick3D.Texture", 1, 0);
// set texture source
PropertyName prop = "source";
QString type = "QUrl";
QVariant val = imagePath;
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
ModelNode newModelNode = QmlItemNode::createQmlObjectNode(view,
itemLibraryEntry,
{},
targetProp,
false);
// Rename the node based on source image
QFileInfo fi(imagePath);
newModelNode.setIdWithoutRefactoring(
view->model()->generateNewId(fi.baseName(), "textureImage"));
return newModelNode;
}
return {};
}
bool dropAsImage3dTexture(const ModelNode &targetNode,
const NodeAbstractProperty &targetProp,
const QString &imagePath,
ModelNode &newNode,
bool &outMoveNodesAfter)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
auto bindToProperty = [&](const PropertyName &propName, bool sibling) {
view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath);
if (newNode.isValid()) {
targetNode.bindingProperty(propName).setExpression(newNode.validId());
// If dropping an image on e.g. TextureInput, create a texture on the same level as
// target, as the target doesn't support Texture children (QTBUG-86219)
if (sibling)
outMoveNodesAfter = !moveNodeToParent(targetProp, newNode);
}
});
};
if (targetNode.metaInfo().isQtQuick3DDefaultMaterial()
|| targetNode.metaInfo().isQtQuick3DPrincipledMaterial()
|| targetNode.metaInfo().isQtQuick3DSpecularGlossyMaterial()) {
// if dropping an image on a material, create a texture instead of image
// Show texture property selection dialog
auto dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode,
view->model()->metaInfo(
"QtQuick3D.Texture"),
Core::ICore::dialogParent());
if (!dialog)
return false;
dialog->exec();
if (dialog->result() == QDialog::Accepted) {
view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath);
if (newNode.isValid()) // Automatically set the texture to selected property
targetNode.bindingProperty(dialog->selectedProperty())
.setExpression(newNode.validId());
});
}
delete dialog;
return true;
} else if (targetNode.metaInfo().isQtQuick3DTextureInput()) {
bindToProperty("texture", true);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DParticles3DSpriteParticle3D()) {
bindToProperty("sprite", false);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DSceneEnvironment()) {
bindToProperty("lightProbe", false);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DTexture()) {
// if dropping an image on an existing texture, set the source
targetNode.variantProperty("source").setValue(imagePath);
return true;
} else if (targetNode.metaInfo().isQtQuick3DModel()) {
QTimer::singleShot(0, view, [targetNode, imagePath, view]() {
if (view && targetNode.isValid()) {
// To MaterialBrowserView. Done async to avoid custom notification in transaction
view->emitCustomNotification("apply_asset_to_model3D",
{targetNode},
{DocumentManager::currentFilePath()
.absolutePath()
.pathAppended(imagePath)
.cleanPath()
.toString()});
}
});
return true;
}
return false;
}
ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const ModelNode &targetNode)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
ModelNode newModelNode;
if ((targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect")
|| !targetNode.metaInfo().isQtQuickItem()) {
return newModelNode;
}
if (ModelNodeOperations::validateEffect(effectPath)) {
bool layerEffect = ModelNodeOperations::useLayerEffect();
newModelNode = QmlItemNode::createQmlItemNodeForEffect(view,
targetNode,
effectPath,
layerEffect);
}
return newModelNode;
}
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode)
{
AbstractView *view = targetModelNode.view();
QTC_ASSERT(view, return );
QmlObjectNode targetNode(targetModelNode);
if (!targetNode.isValid())
return;
qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt();
ModelNode texNode = view->modelNodeForInternalId(internalId);
QTC_ASSERT(texNode.isValid(), return );
if (targetNode.modelNode().metaInfo().isQtQuick3DModel()) {
view->emitCustomNotification("apply_texture_to_model3D", {targetNode, texNode});
} else {
auto *dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode,
texNode,
Core::ICore::dialogParent());
if (dialog) {
bool soloProperty = dialog->isSoloProperty();
if (!soloProperty)
dialog->exec();
if (soloProperty || dialog->result() == QDialog::Accepted)
targetNode.setBindingProperty(dialog->selectedProperty(), texNode.id());
delete dialog;
}
}
}
void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return );
if (!targetNode.metaInfo().isQtQuick3DModel())
return;
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
ModelNode matNode = view->modelNodeForInternalId(internalId);
view->executeInTransaction(__FUNCTION__, [&] {
MaterialUtils::assignMaterialTo3dModel(view, targetNode, matNode);
});
}
ModelNode handleItemLibraryImageDrop(const QString &imagePath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
const QString imagePathRelative
= DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
imagePath); // relative to .ui.qml file
ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode,
targetProperty,
imagePathRelative,
newModelNode,
outMoveNodesAfter)) {
if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) {
// if dropping an image on an existing image, set the source
targetNode.variantProperty("source").setValue(imagePathRelative);
} else {
// create an image
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(view,
imagePath,
QPointF(),
targetProperty,
false);
if (NodeHints::fromModelNode(targetProperty.parentModelNode())
.canBeContainerFor(newItemNode.modelNode())) {
newModelNode = newItemNode.modelNode();
} else {
newItemNode.destroy();
}
}
}
return newModelNode;
}
ModelNode handleItemLibraryFontDrop(const QString &fontFamily,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
ModelNode newModelNode;
if (targetNode.metaInfo().isQtQuickText()) {
// if dropping into an existing Text, update font
targetNode.variantProperty("font.family").setValue(fontFamily);
} else {
// create a Text node
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont(view,
fontFamily,
QPointF(),
targetProperty,
false);
if (NodeHints::fromModelNode(targetProperty.parentModelNode())
.canBeContainerFor(newItemNode.modelNode())) {
newModelNode = newItemNode.modelNode();
} else {
newItemNode.destroy();
}
}
return newModelNode;
}
ModelNode handleItemLibraryShaderDrop(const QString &shaderPath,
bool isFragShader,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
shaderPath);
if (targetNode.metaInfo().isQtQuick3DShader()) {
// if dropping into an existing Shader, update
targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment"
: "Shader.Vertex");
targetNode.variantProperty("shader").setValue(relPath);
} else {
view->executeInTransaction("NavigatorTreeModel::handleItemLibraryShaderDrop", [&] {
// create a new Shader
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("Shader");
itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0);
// set shader properties
PropertyName prop = "shader";
QString type = "QUrl";
QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val);
prop = "stage";
type = "enum";
val = isFragShader ? "Shader.Fragment" : "Shader.Vertex";
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
newModelNode = QmlItemNode::createQmlObjectNode(view,
itemLibraryEntry,
{},
targetProperty,
false);
// Rename the node based on shader source
QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(
view->model()->generateNewId(fi.baseName(), "shader"));
// Passes can't have children, so move shader node under parent
if (targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) {
BindingProperty listProp = targetNode.bindingProperty("shaders");
listProp.addModelNodeToArray(newModelNode);
outMoveNodesAfter = !moveNodeToParent(targetProperty, newModelNode);
}
});
}
return newModelNode;
}
ModelNode handleItemLibrarySoundDrop(const QString &soundPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
soundPath);
if (targetNode.metaInfo().isQtMultimediaSoundEffect()) {
// if dropping into on an existing SoundEffect, update
targetNode.variantProperty("source").setValue(relPath);
} else {
// create a new SoundEffect
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("SoundEffect");
itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0);
// set source property
PropertyName prop = "source";
QString type = "QUrl";
QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
newModelNode = QmlItemNode::createQmlObjectNode(view,
itemLibraryEntry,
{},
targetProperty,
false);
// Rename the node based on source
QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(
view->model()->generateNewId(fi.baseName(), "soundEffect"));
}
return newModelNode;
}
ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter)
{
AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {});
Import import = Import::createLibraryImport(QStringLiteral("QtQuick3D"));
if (!view->model()->hasImport(import, true, true))
return {};
const QString imagePath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
tex3DPath); // relative to qml file
ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode,
targetProperty,
imagePath,
newModelNode,
outMoveNodesAfter)) {
view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] {
// create a standalone Texture3D at drop location
newModelNode = createTextureNode(targetProperty, imagePath);
if (!NodeHints::fromModelNode(targetProperty.parentModelNode())
.canBeContainerFor(newModelNode))
newModelNode.destroy();
});
}
return newModelNode;
}
} // namespace ModelNodeOperations
} //QmlDesigner

View File

@@ -133,6 +133,30 @@ bool validateEffect(const QString &effectPath);
Utils::FilePath getImagesDefaultDirectory();
//Item Library and Assets related drop operations
ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const ModelNode &targetNode);
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode);
void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode);
ModelNode handleItemLibraryImageDrop(const QString &imagePath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter);
ModelNode handleItemLibraryFontDrop(const QString &fontFamily,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode);
ModelNode handleItemLibraryShaderDrop(const QString &shaderPath,
bool isFragShader,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter);
ModelNode handleItemLibrarySoundDrop(const QString &soundPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode);
ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode,
bool &outMoveNodesAfter);
// ModelNodePreviewImageOperations
QVariant previewImageDataForGenericNode(const ModelNode &modelNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);

View File

@@ -554,9 +554,13 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
if (mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) {
handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex);
} else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
handleTextureDrop(mimeData, dropModelIndex);
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
ModelNode targetNode = modelNodeForIndex(rowModelIndex);
ModelNodeOperations::handleTextureDrop(mimeData, targetNode);
} else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) {
handleMaterialDrop(mimeData, dropModelIndex);
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
ModelNode targetNode = modelNodeForIndex(rowModelIndex);
ModelNodeOperations::handleMaterialDrop(mimeData, targetNode);
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
QByteArray filePath = mimeData->data(Constants::MIME_TYPE_BUNDLE_TEXTURE);
ModelNode targetNode(modelNodeForIndex(dropModelIndex));
@@ -607,23 +611,36 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
QString assetType = assetTypeAndData.first;
QString assetData = QString::fromUtf8(assetTypeAndData.second);
if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) {
currNode = handleItemLibraryImageDrop(assetPath, targetProperty,
rowModelIndex, moveNodesAfter);
currNode
= ModelNodeOperations::handleItemLibraryImageDrop(assetPath,
targetProperty,
modelNodeForIndex(
rowModelIndex),
moveNodesAfter);
} else if (assetType == Constants::MIME_TYPE_ASSET_FONT) {
currNode = handleItemLibraryFontDrop(assetData, // assetData is fontFamily
targetProperty, rowModelIndex);
currNode = ModelNodeOperations::handleItemLibraryFontDrop(
assetData, // assetData is fontFamily
targetProperty,
modelNodeForIndex(rowModelIndex));
} else if (assetType == Constants::MIME_TYPE_ASSET_SHADER) {
currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f",
targetProperty, rowModelIndex,
currNode = ModelNodeOperations::handleItemLibraryShaderDrop(
assetPath,
assetData == "f",
targetProperty,
modelNodeForIndex(rowModelIndex),
moveNodesAfter);
} else if (assetType == Constants::MIME_TYPE_ASSET_SOUND) {
currNode = handleItemLibrarySoundDrop(assetPath, targetProperty,
rowModelIndex);
currNode = ModelNodeOperations::handleItemLibrarySoundDrop(
assetPath, targetProperty, modelNodeForIndex(rowModelIndex));
} else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
currNode = handleItemLibraryTexture3dDrop(assetPath, targetProperty,
rowModelIndex, moveNodesAfter);
currNode = ModelNodeOperations::handleItemLibraryTexture3dDrop(
assetPath,
targetProperty,
modelNodeForIndex(rowModelIndex),
moveNodesAfter);
} else if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) {
currNode = handleItemLibraryEffectDrop(assetPath, rowModelIndex);
currNode = ModelNodeOperations::handleItemLibraryEffectDrop(
assetPath, modelNodeForIndex(rowModelIndex));
moveNodesAfter = false;
}
@@ -785,109 +802,6 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
}
}
void NavigatorTreeModel::handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex)
{
QTC_ASSERT(m_view, return);
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
QmlObjectNode targetNode = modelNodeForIndex(rowModelIndex);
if (!targetNode.isValid())
return;
qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt();
ModelNode texNode = m_view->modelNodeForInternalId(internalId);
QTC_ASSERT(texNode.isValid(), return);
if (targetNode.modelNode().metaInfo().isQtQuick3DModel()) {
m_view->emitCustomNotification("apply_texture_to_model3D", {targetNode, texNode});
} else {
auto *dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, texNode, Core::ICore::dialogParent());
if (dialog) {
bool soloProperty = dialog->isSoloProperty();
if (!soloProperty)
dialog->exec();
if (soloProperty || dialog->result() == QDialog::Accepted)
targetNode.setBindingProperty(dialog->selectedProperty(), texNode.id());
delete dialog;
}
}
}
void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex)
{
QTC_ASSERT(m_view, return);
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
ModelNode targetNode = modelNodeForIndex(rowModelIndex);
if (!targetNode.metaInfo().isQtQuick3DModel())
return;
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
ModelNode matNode = m_view->modelNodeForInternalId(internalId);
m_view->executeInTransaction(__FUNCTION__, [&] {
MaterialUtils::assignMaterialTo3dModel(m_view, targetNode, matNode);
});
}
ModelNode NavigatorTreeModel::handleItemLibraryImageDrop(const QString &imagePath,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex,
bool &outMoveNodesAfter)
{
QTC_ASSERT(m_view, return {});
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
const QString imagePathRelative = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(imagePath); // relative to .ui.qml file
ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePathRelative, newModelNode, outMoveNodesAfter)) {
if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) {
// if dropping an image on an existing image, set the source
targetNode.variantProperty("source").setValue(imagePathRelative);
} else {
// create an image
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(m_view, imagePath, QPointF(), targetProperty, false);
if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode()))
newModelNode = newItemNode.modelNode();
else
newItemNode.destroy();
}
}
return newModelNode;
}
ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamily,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex)
{
QTC_ASSERT(m_view, return {});
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode;
if (targetNode.metaInfo().isQtQuickText()) {
// if dropping into an existing Text, update font
targetNode.variantProperty("font.family").setValue(fontFamily);
} else {
// create a Text node
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont(
m_view, fontFamily, QPointF(), targetProperty, false);
if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode()))
newModelNode = newItemNode.modelNode();
else
newItemNode.destroy();
}
return newModelNode;
}
void NavigatorTreeModel::addImport(const QString &importName)
{
if (!ModelUtils::addImportWithCheck(importName, m_view->model()))
@@ -900,227 +814,12 @@ bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractPropert
NodeAbstractProperty parentProp = targetProperty.parentProperty();
if (parentProp.isValid()) {
ModelNode targetModel = parentProp.parentModelNode();
int targetRowNumber = rowCount(indexForModelNode(targetModel));
moveNodesInteractive(parentProp, {node}, targetRowNumber, false);
parentProp.reparentHere(node);
return true;
}
return false;
}
ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex,
bool &outMoveNodesAfter)
{
QTC_ASSERT(m_view, return {});
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(shaderPath);
if (targetNode.metaInfo().isQtQuick3DShader()) {
// if dropping into an existing Shader, update
targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment"
: "Shader.Vertex");
targetNode.variantProperty("shader").setValue(relPath);
} else {
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryShaderDrop", [&] {
// create a new Shader
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("Shader");
itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0);
// set shader properties
PropertyName prop = "shader";
QString type = "QUrl";
QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val);
prop = "stage";
type = "enum";
val = isFragShader ? "Shader.Fragment" : "Shader.Vertex";
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {},
targetProperty, false);
// Rename the node based on shader source
QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(
m_view->model()->generateNewId(fi.baseName(), "shader"));
// Passes can't have children, so move shader node under parent
if (targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) {
BindingProperty listProp = targetNode.bindingProperty("shaders");
listProp.addModelNodeToArray(newModelNode);
outMoveNodesAfter = !moveNodeToParent(targetProperty, newModelNode);
}
});
}
return newModelNode;
}
ModelNode NavigatorTreeModel::handleItemLibrarySoundDrop(const QString &soundPath,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex)
{
QTC_ASSERT(m_view, return {});
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(soundPath);
if (targetNode.metaInfo().isQtMultimediaSoundEffect()) {
// if dropping into on an existing SoundEffect, update
targetNode.variantProperty("source").setValue(relPath);
} else {
// create a new SoundEffect
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("SoundEffect");
itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0);
// set source property
PropertyName prop = "source";
QString type = "QUrl";
QVariant val = relPath;
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {},
targetProperty, false);
// Rename the node based on source
QFileInfo fi(relPath);
newModelNode.setIdWithoutRefactoring(
m_view->model()->generateNewId(fi.baseName(), "soundEffect"));
}
return newModelNode;
}
ModelNode NavigatorTreeModel::handleItemLibraryTexture3dDrop(const QString &tex3DPath,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex,
bool &outMoveNodesAfter)
{
QTC_ASSERT(m_view, return {});
Import import = Import::createLibraryImport(QStringLiteral("QtQuick3D"));
if (!m_view->model()->hasImport(import, true, true))
return {};
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
const QString imagePath = DocumentManager::currentFilePath().toFileInfo().dir()
.relativeFilePath(tex3DPath); // relative to qml file
ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePath, newModelNode, outMoveNodesAfter)) {
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] {
// create a standalone Texture3D at drop location
newModelNode = createTextureNode(targetProperty, imagePath);
if (!NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newModelNode))
newModelNode.destroy();
});
}
return newModelNode;
}
ModelNode NavigatorTreeModel::handleItemLibraryEffectDrop(const QString &effectPath, const QModelIndex &rowModelIndex)
{
QTC_ASSERT(m_view, return {});
ModelNode targetNode(modelNodeForIndex(rowModelIndex));
ModelNode newModelNode;
if ((targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect")
|| !targetNode.metaInfo().isQtQuickItem())
return newModelNode;
if (ModelNodeOperations::validateEffect(effectPath)) {
bool layerEffect = ModelNodeOperations::useLayerEffect();
newModelNode = QmlItemNode::createQmlItemNodeForEffect(m_view, targetNode, effectPath, layerEffect);
}
return newModelNode;
}
bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode,
const NodeAbstractProperty &targetProp,
const QString &imagePath,
ModelNode &newNode,
bool &outMoveNodesAfter)
{
auto bindToProperty = [&](const PropertyName &propName, bool sibling) {
m_view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath);
if (newNode.isValid()) {
targetNode.bindingProperty(propName).setExpression(newNode.validId());
// If dropping an image on e.g. TextureInput, create a texture on the same level as
// target, as the target doesn't support Texture children (QTBUG-86219)
if (sibling)
outMoveNodesAfter = !moveNodeToParent(targetProp, newNode);
}
});
};
if (targetNode.metaInfo().isQtQuick3DDefaultMaterial()
|| targetNode.metaInfo().isQtQuick3DPrincipledMaterial()
|| targetNode.metaInfo().isQtQuick3DSpecularGlossyMaterial()) {
// if dropping an image on a material, create a texture instead of image
// Show texture property selection dialog
auto dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode,
m_view->model()->metaInfo(
"QtQuick3D.Texture"),
Core::ICore::dialogParent());
if (!dialog)
return false;
dialog->exec();
if (dialog->result() == QDialog::Accepted) {
m_view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath);
if (newNode.isValid()) // Automatically set the texture to selected property
targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newNode.validId());
});
}
delete dialog;
return true;
} else if (targetNode.metaInfo().isQtQuick3DTextureInput()) {
bindToProperty("texture", true);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DParticles3DSpriteParticle3D()) {
bindToProperty("sprite", false);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DSceneEnvironment()) {
bindToProperty("lightProbe", false);
return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DTexture()) {
// if dropping an image on an existing texture, set the source
targetNode.variantProperty("source").setValue(imagePath);
return true;
} else if (targetNode.metaInfo().isQtQuick3DModel()) {
QTimer::singleShot(0, this, [this, targetNode, imagePath]() {
if (m_view && targetNode.isValid()) {
// To MaterialBrowserView. Done async to avoid custom notification in transaction
m_view->emitCustomNotification(
"apply_asset_to_model3D", {targetNode},
{DocumentManager::currentFilePath().absolutePath().pathAppended(imagePath).cleanPath().toString()});
}
});
return true;
}
return false;
}
ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targetProp,
const QString &imagePath)
{

View File

@@ -93,21 +93,7 @@ private:
int targetIndex, bool executeInTransaction = true);
void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex);
void handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex);
ModelNode handleItemLibraryImageDrop(const QString &imagePath, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex, bool &outMoveNodesAfter);
ModelNode handleItemLibraryFontDrop(const QString &fontFamily, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex);
ModelNode handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader,
NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex,
bool &outMoveNodesAfter);
ModelNode handleItemLibrarySoundDrop(const QString &soundPath, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex);
ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex, bool &outMoveNodesAfter);
ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const QModelIndex &rowModelIndex);
bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp,
const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter);
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath);