forked from qt-creator/qt-creator
QmlDesigner: Fix on-drag binding of created nodes to properties
Generic matching for dragged item to bindable properties didn't work properly, missing appropriate matches. Now supported cases are hardcoded with the expectation that in the future this will be handled via metainfo hints. Also fixed related issues with dragging images and shaders into navigator. Fixes: QDS-6198 Change-Id: Ide9360ef037997cbb388f4c2db35b79eded67aa6 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -34,35 +34,64 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
|
||||
const NodeMetaInfo &parentInfo,
|
||||
bool breakOnFirst)
|
||||
{
|
||||
TypeName typeName = insertInfo.typeName();
|
||||
TypeName superClass = insertInfo.directSuperClass().typeName();
|
||||
TypeName nodePackage = insertInfo.typeName().replace(insertInfo.simplifiedTypeName(), "");
|
||||
TypeName targetPackage = parentInfo.typeName().replace(parentInfo.simplifiedTypeName(), "");
|
||||
if (!nodePackage.contains(targetPackage))
|
||||
return;
|
||||
// TODO: Metainfo based matching system (QDS-6240)
|
||||
|
||||
// Common base types cause too many rarely valid matches, so they are ignored
|
||||
const QSet<TypeName> ignoredTypes {"<cpp>.QObject",
|
||||
"<cpp>.QQuickItem",
|
||||
"<cpp>.QQuick3DObject",
|
||||
"QtQuick.Item",
|
||||
"QtQuick3D.Object3D",
|
||||
"QtQuick3D.Node",
|
||||
"QtQuick3D.Particles3D.ParticleSystem3D"};
|
||||
const PropertyNameList targetNodeNameList = parentInfo.propertyNames();
|
||||
for (const PropertyName &name : targetNodeNameList) {
|
||||
if (!name.contains('.')) {
|
||||
TypeName propType = parentInfo.propertyTypeName(name).replace("<cpp>.", targetPackage);
|
||||
// Skip properties that are not sub classes of anything
|
||||
if (propType.contains('.')
|
||||
&& !ignoredTypes.contains(propType)
|
||||
&& (typeName == propType || propType == superClass)
|
||||
&& parentInfo.propertyIsWritable(propType)) {
|
||||
propertyList.append(QString::fromLatin1(name));
|
||||
if (breakOnFirst)
|
||||
break;
|
||||
// Fall back to a hardcoded list of supported cases:
|
||||
// Texture
|
||||
// -> DefaultMaterial
|
||||
// -> PrincipledMaterial
|
||||
// -> SpriteParticle3D
|
||||
// -> TextureInput
|
||||
// -> SceneEnvironment
|
||||
// Effect
|
||||
// -> SceneEnvironment
|
||||
// Shader, Command, Buffer
|
||||
// -> Pass
|
||||
// InstanceListEntry
|
||||
// -> InstanceList
|
||||
// Pass
|
||||
// -> Effect
|
||||
|
||||
const TypeName textureType = "QtQuick3D.Texture";
|
||||
if (insertInfo.isSubclassOf(textureType)) {
|
||||
const TypeName textureTypeCpp = "<cpp>.QQuick3DTexture";
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.DefaultMaterial")
|
||||
|| parentInfo.isSubclassOf("QtQuick3D.PrincipledMaterial")) {
|
||||
// All texture properties are valid targets
|
||||
const PropertyNameList targetNodeNameList = parentInfo.propertyNames();
|
||||
for (const PropertyName &name : targetNodeNameList) {
|
||||
TypeName propType = parentInfo.propertyTypeName(name);
|
||||
if (propType == textureType || propType == textureTypeCpp) {
|
||||
propertyList.append(QString::fromLatin1(name));
|
||||
if (breakOnFirst)
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (parentInfo.isSubclassOf("QtQuick3D.Particles3D.SpriteParticle3D")) {
|
||||
propertyList.append("sprite");
|
||||
} else if (parentInfo.isSubclassOf("QtQuick3D.TextureInput")) {
|
||||
propertyList.append("texture");
|
||||
} else if (parentInfo.isSubclassOf("QtQuick3D.SceneEnvironment")) {
|
||||
propertyList.append("lightProbe");
|
||||
}
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.Effect")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.SceneEnvironment"))
|
||||
propertyList.append("effects");
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.Shader")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.Pass"))
|
||||
propertyList.append("shaders");
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.Command")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.Pass"))
|
||||
propertyList.append("commands");
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.Buffer")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.Pass"))
|
||||
propertyList.append("output");
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.InstanceListEntry")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.InstanceList"))
|
||||
propertyList.append("instances");
|
||||
} else if (insertInfo.isSubclassOf("QtQuick3D.Pass")) {
|
||||
if (parentInfo.isSubclassOf("QtQuick3D.Effect"))
|
||||
propertyList.append("passes");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,14 +139,6 @@ TypeName ChooseFromPropertyListDialog::selectedProperty() const
|
||||
ChooseFromPropertyListDialog *ChooseFromPropertyListDialog::createIfNeeded(
|
||||
const ModelNode &targetNode, const ModelNode &newNode, QWidget *parent)
|
||||
{
|
||||
TypeName typeName = newNode.type();
|
||||
|
||||
// Component matches cases where you don't want to insert a plain component,
|
||||
// such as layer.effect. Also, default property is often a Component (typically 'delegate'),
|
||||
// and inserting into such property will silently overwrite implicit component, if any.
|
||||
if (typeName == "QtQml.Component")
|
||||
return nullptr;
|
||||
|
||||
const NodeMetaInfo info = newNode.metaInfo();
|
||||
const NodeMetaInfo targetInfo = targetNode.metaInfo();
|
||||
ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(info, targetInfo);
|
||||
|
||||
@@ -581,21 +581,29 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
|
||||
addImport(import);
|
||||
}
|
||||
|
||||
bool moveNodesAfter = true;
|
||||
m_view->executeInTransaction("NavigatorTreeModel::dropMimeData", [&] {
|
||||
for (const QString &assetPath : assetsPaths) {
|
||||
auto assetTypeAndData = AssetsLibraryWidget::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 (assetType == "application/vnd.bauhaus.libraryresource.image") {
|
||||
currNode = handleItemLibraryImageDrop(assetPath, targetProperty,
|
||||
rowModelIndex, moveNodesAfter);
|
||||
} else if (assetType == "application/vnd.bauhaus.libraryresource.font") {
|
||||
currNode = handleItemLibraryFontDrop(assetData, // assetData is fontFamily
|
||||
targetProperty, rowModelIndex);
|
||||
} else if (assetType == "application/vnd.bauhaus.libraryresource.shader") {
|
||||
currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f",
|
||||
targetProperty, rowModelIndex,
|
||||
moveNodesAfter);
|
||||
} 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, moveNodesAfter);
|
||||
}
|
||||
|
||||
if (currNode.isValid())
|
||||
addedNodes.append(currNode);
|
||||
@@ -603,7 +611,8 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
|
||||
});
|
||||
|
||||
if (!addedNodes.isEmpty()) {
|
||||
moveNodesInteractive(targetProperty, addedNodes, rowNumber);
|
||||
if (moveNodesAfter)
|
||||
moveNodesInteractive(targetProperty, addedNodes, rowNumber);
|
||||
m_view->setSelectedModelNodes(addedNodes);
|
||||
}
|
||||
}
|
||||
@@ -682,21 +691,22 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
||||
dialog->exec();
|
||||
if (soloProperty || dialog->result() == QDialog::Accepted) {
|
||||
TypeName selectedProp = dialog->selectedProperty();
|
||||
BindingProperty listProp = targetNode.bindingProperty(selectedProp);
|
||||
if (targetNode.metaInfo().propertyIsListProperty(selectedProp)) {
|
||||
if ((newModelNode.isSubclassOf("QtQuick3D.Shader") || newModelNode.isSubclassOf("QtQuick3D.Command"))
|
||||
&& targetProperty.parentModelNode().isSubclassOf("QtQuick3D.Pass")) {
|
||||
NodeAbstractProperty parentProp = targetProperty.parentProperty();
|
||||
if (parentProp.isValid()) {
|
||||
targetProperty = parentProp;
|
||||
ModelNode targetModel = targetProperty.parentModelNode();
|
||||
targetRowNumber = rowCount(indexForModelNode(targetModel));
|
||||
// Move node to new parent within the same transaction as we don't
|
||||
// want undo to place the node under invalid parent
|
||||
moveNodesInteractive(targetProperty, {newQmlObjectNode}, targetRowNumber, false);
|
||||
moveNodesAfter = false;
|
||||
}
|
||||
|
||||
// Pass and TextureInput can't have children, so we have to move nodes under parent
|
||||
if (((newModelNode.isSubclassOf("QtQuick3D.Shader")
|
||||
|| newModelNode.isSubclassOf("QtQuick3D.Command")
|
||||
|| newModelNode.isSubclassOf("QtQuick3D.Buffer"))
|
||||
&& targetProperty.parentModelNode().isSubclassOf("QtQuick3D.Pass"))
|
||||
|| (newModelNode.isSubclassOf("QtQuick3D.Texture")
|
||||
&& targetProperty.parentModelNode().isSubclassOf("QtQuick3D.TextureInput"))) {
|
||||
if (moveNodeToParent(targetProperty, newQmlObjectNode)) {
|
||||
targetProperty = targetProperty.parentProperty();
|
||||
moveNodesAfter = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetNode.metaInfo().propertyIsListProperty(selectedProp)) {
|
||||
BindingProperty listProp = targetNode.bindingProperty(selectedProp);
|
||||
listProp.addModelNodeToArray(newModelNode);
|
||||
validContainer = true;
|
||||
} else {
|
||||
@@ -778,7 +788,8 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
||||
|
||||
ModelNode NavigatorTreeModel::handleItemLibraryImageDrop(const QString &imagePath,
|
||||
NodeAbstractProperty targetProperty,
|
||||
const QModelIndex &rowModelIndex)
|
||||
const QModelIndex &rowModelIndex,
|
||||
bool &outMoveNodesAfter)
|
||||
{
|
||||
QTC_ASSERT(m_view, return {});
|
||||
|
||||
@@ -788,7 +799,7 @@ ModelNode NavigatorTreeModel::handleItemLibraryImageDrop(const QString &imagePat
|
||||
|
||||
ModelNode newModelNode;
|
||||
|
||||
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePathRelative, newModelNode)) {
|
||||
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePathRelative, newModelNode, outMoveNodesAfter)) {
|
||||
if (targetNode.isSubclassOf("QtQuick.Image") || targetNode.isSubclassOf("QtQuick.BorderImage")) {
|
||||
// if dropping an image on an existing image, set the source
|
||||
targetNode.variantProperty("source").setValue(imagePathRelative);
|
||||
@@ -846,9 +857,23 @@ void NavigatorTreeModel::addImport(const QString &importName)
|
||||
}
|
||||
}
|
||||
|
||||
bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractProperty &targetProperty,
|
||||
const ModelNode &node)
|
||||
{
|
||||
NodeAbstractProperty parentProp = targetProperty.parentProperty();
|
||||
if (parentProp.isValid()) {
|
||||
ModelNode targetModel = parentProp.parentModelNode();
|
||||
int targetRowNumber = rowCount(indexForModelNode(targetModel));
|
||||
moveNodesInteractive(parentProp, {node}, targetRowNumber, false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader,
|
||||
NodeAbstractProperty targetProperty,
|
||||
const QModelIndex &rowModelIndex)
|
||||
const QModelIndex &rowModelIndex,
|
||||
bool &outMoveNodesAfter)
|
||||
{
|
||||
QTC_ASSERT(m_view, return {});
|
||||
|
||||
@@ -863,29 +888,37 @@ ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderP
|
||||
: "Shader.Vertex");
|
||||
targetNode.variantProperty("shader").setValue(relPath);
|
||||
} else {
|
||||
// create a new Shader
|
||||
ItemLibraryEntry itemLibraryEntry;
|
||||
itemLibraryEntry.setName("Shader");
|
||||
itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0);
|
||||
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 = "QByteArray";
|
||||
QVariant val = relPath;
|
||||
itemLibraryEntry.addProperty(prop, type, val);
|
||||
prop = "stage";
|
||||
type = "enum";
|
||||
val = isFragShader ? "Shader.Fragment" : "Shader.Vertex";
|
||||
itemLibraryEntry.addProperty(prop, type, val);
|
||||
// set shader properties
|
||||
PropertyName prop = "shader";
|
||||
QString type = "QByteArray";
|
||||
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);
|
||||
// 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->generateNewId(fi.baseName(),
|
||||
"shader"));
|
||||
// Rename the node based on shader source
|
||||
QFileInfo fi(relPath);
|
||||
newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(),
|
||||
"shader"));
|
||||
// Passes can't have children, so move shader node under parent
|
||||
if (targetProperty.parentModelNode().isSubclassOf("QtQuick3D.Pass")) {
|
||||
BindingProperty listProp = targetNode.bindingProperty("shaders");
|
||||
listProp.addModelNodeToArray(newModelNode);
|
||||
outMoveNodesAfter = !moveNodeToParent(targetProperty, newModelNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return newModelNode;
|
||||
@@ -932,7 +965,8 @@ ModelNode NavigatorTreeModel::handleItemLibrarySoundDrop(const QString &soundPat
|
||||
|
||||
ModelNode NavigatorTreeModel::handleItemLibraryTexture3dDrop(const QString &tex3DPath,
|
||||
NodeAbstractProperty targetProperty,
|
||||
const QModelIndex &rowModelIndex)
|
||||
const QModelIndex &rowModelIndex,
|
||||
bool &outMoveNodesAfter)
|
||||
{
|
||||
QTC_ASSERT(m_view, return {});
|
||||
|
||||
@@ -947,7 +981,7 @@ ModelNode NavigatorTreeModel::handleItemLibraryTexture3dDrop(const QString &tex3
|
||||
|
||||
ModelNode newModelNode;
|
||||
|
||||
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePath, newModelNode)) {
|
||||
if (!dropAsImage3dTexture(targetNode, targetProperty, imagePath, newModelNode, outMoveNodesAfter)) {
|
||||
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] {
|
||||
// create a standalone Texture3D at drop location
|
||||
newModelNode = createTextureNode(targetProperty, imagePath);
|
||||
@@ -962,8 +996,23 @@ ModelNode NavigatorTreeModel::handleItemLibraryTexture3dDrop(const QString &tex3
|
||||
bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode,
|
||||
const NodeAbstractProperty &targetProp,
|
||||
const QString &imagePath,
|
||||
ModelNode &newNode)
|
||||
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.isSubclassOf("QtQuick3D.DefaultMaterial")
|
||||
|| targetNode.isSubclassOf("QtQuick3D.PrincipledMaterial")) {
|
||||
// if dropping an image on a material, create a texture instead of image
|
||||
@@ -986,16 +1035,13 @@ bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode,
|
||||
delete dialog;
|
||||
return true;
|
||||
} else if (targetNode.isSubclassOf("QtQuick3D.TextureInput")) {
|
||||
// If dropping an image on a TextureInput, create a texture on the same level as
|
||||
// TextureInput, as the TextureInput doesn't support Texture children (QTBUG-86219)
|
||||
m_view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
|
||||
NodeAbstractProperty parentProp = targetProp.parentProperty();
|
||||
newNode = createTextureNode(parentProp, imagePath);
|
||||
if (newNode.isValid()) {
|
||||
// Automatically set the texture to texture property
|
||||
targetNode.bindingProperty("texture").setExpression(newNode.validId());
|
||||
}
|
||||
});
|
||||
bindToProperty("texture", true);
|
||||
return newNode.isValid();
|
||||
} else if (targetNode.isSubclassOf("QtQuick3D.Particles3D.SpriteParticle3D")) {
|
||||
bindToProperty("sprite", false);
|
||||
return newNode.isValid();
|
||||
} else if (targetNode.isSubclassOf("QtQuick3D.SceneEnvironment")) {
|
||||
bindToProperty("lightProbe", false);
|
||||
return newNode.isValid();
|
||||
} else if (targetNode.isSubclassOf("QtQuick3D.Texture")) {
|
||||
// if dropping an image on an existing texture, set the source
|
||||
|
||||
@@ -115,21 +115,24 @@ private:
|
||||
void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
|
||||
void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
|
||||
ModelNode handleItemLibraryImageDrop(const QString &imagePath, NodeAbstractProperty targetProperty,
|
||||
const QModelIndex &rowModelIndex);
|
||||
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);
|
||||
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);
|
||||
const QModelIndex &rowModelIndex, bool &outMoveNodesAfter);
|
||||
bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp,
|
||||
const QString &imagePath, ModelNode &newNode);
|
||||
const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter);
|
||||
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath);
|
||||
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
|
||||
void addImport(const QString &importName);
|
||||
QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const;
|
||||
bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const ModelNode &newModelNode);
|
||||
|
||||
QPointer<NavigatorView> m_view;
|
||||
mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash;
|
||||
|
||||
Reference in New Issue
Block a user