diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2022-06-07 13:39:25 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2022-06-07 14:14:56 +0000 |
commit | 7269aafbd8a1fddb7d43f783ac20b4d97bbc20c5 (patch) | |
tree | ef602b89fc234e12fa5942151d98ec99345180e8 | |
parent | d2cc440c688540f85b7bd925625397511230f307 (diff) | |
download | qt-creator-7269aafbd8a1fddb7d43f783ac20b4d97bbc20c5.tar.gz |
QmlDesigner: Ensure material library is created when needed
Added material library accessor to AbstractView, which creates the
material library and moves existing materials under it in case it
doesn't yet exist. Also added material assignment function to
AbstractView. The reason these were added to AbstractView instead of
being handled e.g. via custom notification in material editor is that
they need to be called from multiple different views in the same
transaction that triggers the need of material library.
Fixes: QDS-7081
Change-Id: If2bb884f87d04c9f3599c2342df66ef51ec238ee
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
9 files changed, 144 insertions, 145 deletions
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index efb9e24c1c..02d798c1f6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -186,42 +186,17 @@ void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e) void Edit3DCanvas::dropEvent(QDropEvent *e) { - auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode(); - QTC_ASSERT(modelNode.isValid(), return); - - e->accept(); - m_parent->view()->setSelectedModelNode(modelNode); - - // if added node is a Model, assign it a material - if (modelNode.isSubclassOf("QtQuick3D.Model")) { - ModelNode matLib = m_parent->view()->modelNodeForId(Constants::MATERIAL_LIB_ID); - QTC_ASSERT(matLib.isValid(), return); - - const QList<ModelNode> materials = matLib.directSubModelNodes(); - ModelNode material; - if (materials.size() > 0) { - for (const ModelNode &mat : materials) { - if (mat.isSubclassOf("QtQuick3D.Material")) { - material = mat; - break; - } - } - } + m_parent->view()->executeInTransaction(__FUNCTION__, [&] { + auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode(); + QTC_ASSERT(modelNode.isValid(), return); - // if no valid material, create a new default material - if (!material.isValid()) { - NodeMetaInfo metaInfo = m_parent->view()->model()->metaInfo("QtQuick3D.DefaultMaterial"); - material = m_parent->view()->createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(), - metaInfo.minorVersion()); - VariantProperty matNameProp = material.variantProperty("objectName"); - matNameProp.setValue("New Material"); - material.validId(); - matLib.defaultNodeListProperty().reparentHere(material); - } + e->accept(); + m_parent->view()->setSelectedModelNode(modelNode); - BindingProperty modelMatsProp = modelNode.bindingProperty("materials"); - modelMatsProp.setExpression(material.id()); - } + // if added node is a Model, assign it a material + if (modelNode.isSubclassOf("QtQuick3D.Model")) + m_parent->view()->assignMaterialTo3dModel(modelNode); + }); } void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent) diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index c4a64adf8f..819bf58b34 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -33,6 +33,8 @@ #include <rewritingexception.h> #include "qmldesignerconstants.h" +#include <utils/qtcassert.h> + #include <QDebug> #include <QGraphicsSceneMouseEvent> #include <QLoggingCategory> @@ -405,10 +407,23 @@ void DragTool::move(const QPointF &scenePosition, const QList<QGraphicsItem *> & void DragTool::commitTransaction() { try { + handleView3dDrop(); m_rewriterTransaction.commit(); } catch (const RewritingException &e) { e.showException(); } } +void DragTool::handleView3dDrop() +{ + // If a View3D is dropped, we need to assign material to the included model + for (const QmlItemNode &dragNode : qAsConst(m_dragNodes)) { + if (dragNode.modelNode().isSubclassOf("QtQuick3D.View3D")) { + const QList<ModelNode> models = dragNode.modelNode().subModelNodesOfType("QtQuick3D.Model"); + QTC_ASSERT(models.size() == 1, return); + view()->assignMaterialTo3dModel(models.at(0)); + } + } +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 63d3de6e8f..bb6f562263 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -90,6 +90,7 @@ protected: void move(const QPointF &scenePos, const QList<QGraphicsItem *> &itemList); void createDragNodes(const QMimeData *mimeData, const QPointF &scenePosition, const QList<QGraphicsItem *> &itemList); void commitTransaction(); + void handleView3dDrop(); private: MoveManipulator m_moveManipulator; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 29a74304d8..28c17d7561 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -78,47 +78,6 @@ MaterialEditorView::MaterialEditorView(QWidget *parent) m_stackedWidget->setMinimumWidth(250); } -void MaterialEditorView::ensureMaterialLibraryNode() -{ - if (!m_hasQuick3DImport) - return; - - m_materialLibrary = modelNodeForId(Constants::MATERIAL_LIB_ID); - if (m_materialLibrary.isValid()) - return; - - // create material library node - TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "QtQuick3D.Node" : "QtQuick.Item"; - NodeMetaInfo metaInfo = model()->metaInfo(nodeType); - m_materialLibrary = createModelNode(nodeType, metaInfo.majorVersion(), metaInfo.minorVersion()); - - m_materialLibrary.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); - rootModelNode().defaultNodeListProperty().reparentHere(m_materialLibrary); - - const QList<ModelNode> materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material"); - if (materials.isEmpty()) - return; - - RewriterTransaction transaction = beginRewriterTransaction( - "MaterialEditorView::ensureMaterialLibraryNode"); - - try { - // move all materials to under material library node - for (const ModelNode &node : materials) { - // if material has no name, set name to id - QString matName = node.variantProperty("objectName").value().toString(); - if (matName.isEmpty()) { - VariantProperty objNameProp = node.variantProperty("objectName"); - objNameProp.setValue(node.id()); - } - - m_materialLibrary.defaultNodeListProperty().reparentHere(node); - } - } catch (Exception &e) { - e.showException(); - } -} - MaterialEditorView::~MaterialEditorView() { qDeleteAll(m_qmlBackendHash); @@ -447,15 +406,13 @@ void MaterialEditorView::handleToolBarAction(int action) } case MaterialEditorContextObject::AddNewMaterial: { - ensureMaterialLibraryNode(); - executeInTransaction("MaterialEditorView:handleToolBarAction", [&] { NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial"); ModelNode newMatNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(), metaInfo.minorVersion()); renameMaterial(newMatNode, "New Material"); - m_materialLibrary.defaultNodeListProperty().reparentHere(newMatNode); + materialLibraryNode().defaultNodeListProperty().reparentHere(newMatNode); }); break; } @@ -759,8 +716,6 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) { QTC_ASSERT(material.isValid(), return); - ensureMaterialLibraryNode(); - TypeName matType = material.type(); QmlObjectNode sourceMat(material); @@ -786,7 +741,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) duplicateMat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); } - m_materialLibrary.defaultNodeListProperty().reparentHere(duplicateMat); + materialLibraryNode().defaultNodeListProperty().reparentHere(duplicateMat); }); } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index 3e8632d26f..d9c9e4c1ba 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -99,7 +99,6 @@ private: void highlightSupportedProperties(bool highlight = true); QString generateIdFromName(const QString &name); - void ensureMaterialLibraryNode(); void requestPreviewRender(); void applyMaterialToSelectedModels(const ModelNode &material, bool add = false); @@ -115,7 +114,6 @@ private: bool noValidSelection() const; ModelNode m_selectedMaterial; - ModelNode m_materialLibrary; QShortcut *m_updateShortcut = nullptr; int m_timerId = 0; QStackedWidget *m_stackedWidget = nullptr; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 0a92f81d93..d610014d5b 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -686,18 +686,31 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in bool validContainer = false; ModelNode targetNode = targetProperty.parentModelNode(); - // don't allow dropping materials on any node but Models - QString itemType = QString::fromLatin1(itemLibraryEntry.typeName()); - if (itemType.startsWith("QtQuick3D.") && itemType.endsWith("Material") - && !targetNode.isSubclassOf("QtQuick3D.Model")) { - return; - } - QmlObjectNode newQmlObjectNode; m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryItemDrop", [&] { newQmlObjectNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, QPointF(), targetProperty, false); ModelNode newModelNode = newQmlObjectNode.modelNode(); if (newModelNode.isValid()) { + if (newModelNode.isSubclassOf("QtQuick3D.Material")) { + // Don't allow dropping materials on any node but Models + if (!targetNode.isSubclassOf("QtQuick3D.Model")) { + newQmlObjectNode.destroy(); + return; + } + // We can't have material initially parented if material library is created in this + // same transaction (rewriter will not allow it for some reason) + ModelNode matLib = m_view->modelNodeForId(Constants::MATERIAL_LIB_ID); + if (!matLib.isValid()) { + newQmlObjectNode.destroy(); + newQmlObjectNode = QmlItemNode::createQmlObjectNode( + m_view, itemLibraryEntry, QPointF(), NodeAbstractProperty(), false); + newModelNode = newQmlObjectNode.modelNode(); + if (!newModelNode.isValid()) + return; + } + m_view->assignMaterialTo3dModel(targetNode, newModelNode); + } + ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded( targetNode, newModelNode, Core::ICore::dialogParent()); if (dialog) { @@ -734,28 +747,10 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in if (newModelNode.isSubclassOf("QtQuick3D.View3D")) { const QList<ModelNode> models = newModelNode.subModelNodesOfType("QtQuick3D.Model"); - QTC_ASSERT(models.size() == 1, return); - - assignMaterialToModel(models.at(0)); + m_view->assignMaterialTo3dModel(models.at(0)); } else if (newModelNode.isSubclassOf("QtQuick3D.Model")) { - assignMaterialToModel(newModelNode); - } - - // dropping a material on a model - if (newModelNode.isSubclassOf("QtQuick3D.Material") - && targetNode.isSubclassOf("QtQuick3D.Model")) { - // parent material to material library and assign it to target model - ModelNode matLib = m_view->modelNodeForId(Constants::MATERIAL_LIB_ID); - - QTC_ASSERT(matLib.isValid(), return); - - VariantProperty objName = newModelNode.variantProperty("objectName"); - objName.setValue("New Material"); - BindingProperty matsProp = targetNode.bindingProperty("materials"); - matsProp.setExpression(newModelNode.id()); - matLib.defaultNodeListProperty().reparentHere(newModelNode); - return; + m_view->assignMaterialTo3dModel(newModelNode); } if (!validContainer) { @@ -1089,40 +1084,6 @@ ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targ return {}; } -// Add a material to a Quick3D.Model node -void NavigatorTreeModel::assignMaterialToModel(const ModelNode &node) -{ - ModelNode matLib = m_view->modelNodeForId(Constants::MATERIAL_LIB_ID); - - QTC_ASSERT(matLib.isValid(), return); - QTC_ASSERT(node.isSubclassOf("QtQuick3D.Model"), return); - - const QList<ModelNode> materials = matLib.directSubModelNodes(); - ModelNode material; - if (materials.size() > 0) { - for (const ModelNode &mat : materials) { - if (mat.isSubclassOf("QtQuick3D.Material")) { - material = mat; - break; - } - } - } - - // if no valid material, create a new default material - if (!material.isValid()) { - NodeMetaInfo metaInfo = m_view->model()->metaInfo("QtQuick3D.DefaultMaterial"); - material = m_view->createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(), - metaInfo.minorVersion()); - VariantProperty matNameProp = material.variantProperty("objectName"); - matNameProp.setValue("New Material"); - material.validId(); - matLib.defaultNodeListProperty().reparentHere(material); - } - - BindingProperty modelMatsProp = node.bindingProperty("materials"); - modelMatsProp.setExpression(material.id()); -} - TypeName propertyType(const NodeAbstractProperty &property) { return property.parentModelNode().metaInfo().propertyTypeName(property.name()); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index 0fcb7aab3e..9652981607 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -130,7 +130,6 @@ private: bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp, const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter); ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath); - void assignMaterialToModel(const ModelNode &node); QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes); void addImport(const QString &importName); QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index e37ed0fb4b..18380aadb1 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -259,6 +259,9 @@ public: void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); + ModelNode materialLibraryNode(); + void assignMaterialTo3dModel(const ModelNode &modelNode, const ModelNode &materialNode = {}); + NodeInstanceView *nodeInstanceView() const; RewriterView *rewriterView() const; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 26040ba23d..86dab97031 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -31,6 +31,10 @@ #include "nodeinstanceview.h" #include <qmlstate.h> #include <qmltimeline.h> +#include <qmldesignerconstants.h> +#include <nodelistproperty.h> +#include <variantproperty.h> +#include <bindingproperty.h> #ifndef QMLDESIGNER_TEST #include <qmldesignerplugin.h> @@ -805,6 +809,94 @@ void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, in m_model.data()->d->changeRootNodeType(type, majorVersion, minorVersion); } +// Returns ModelNode for project's material library. +// If the material library doesn't exist yet, it is created and all existing materials are moved +// under material library. +// This function should be called only form inside a transaction, as it potentially does many +// changes to model. +ModelNode AbstractView::materialLibraryNode() +{ + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + if (matLib.isValid()) + return matLib; + + // Create material library node + TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "QtQuick3D.Node" + : "QtQuick.Item"; + NodeMetaInfo metaInfo = model()->metaInfo(nodeType); + matLib = createModelNode(nodeType, metaInfo.majorVersion(), metaInfo.minorVersion()); + + matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); + rootModelNode().defaultNodeListProperty().reparentHere(matLib); + + const QList<ModelNode> materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material"); + if (materials.isEmpty()) + return matLib; + + // Move all materials to under material library node + for (const ModelNode &node : materials) { + // If material has no name, set name to id + QString matName = node.variantProperty("objectName").value().toString(); + if (matName.isEmpty()) { + VariantProperty objNameProp = node.variantProperty("objectName"); + objNameProp.setValue(node.id()); + } + + matLib.defaultNodeListProperty().reparentHere(node); + } + + return matLib; +} + +// Assigns given material to a 3D model. +// The assigned material is also inserted into material library if not already there. +// If given material is not valid, first existing material from material library is used, +// or if material library is empty, a new material is created. +// This function should be called only from inside a transaction, as it potentially does many +// changes to model. +void AbstractView::assignMaterialTo3dModel(const ModelNode &modelNode, const ModelNode &materialNode) +{ + QTC_ASSERT(modelNode.isValid() && modelNode.isSubclassOf("QtQuick3D.Model"), return); + + ModelNode matLib = materialLibraryNode(); + ModelNode newMaterialNode; + + if (materialNode.isValid() && materialNode.isSubclassOf("QtQuick3D.Material")) { + newMaterialNode = materialNode; + } else { + const QList<ModelNode> materials = matLib.directSubModelNodes(); + if (materials.size() > 0) { + for (const ModelNode &mat : materials) { + if (mat.isSubclassOf("QtQuick3D.Material")) { + newMaterialNode = mat; + break; + } + } + } + + // if no valid material, create a new default material + if (!newMaterialNode.isValid()) { + NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial"); + newMaterialNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(), + metaInfo.minorVersion()); + newMaterialNode.validId(); + } + } + + QTC_ASSERT(newMaterialNode.isValid(), return); + + VariantProperty matNameProp = newMaterialNode.variantProperty("objectName"); + if (matNameProp.value().isNull()) + matNameProp.setValue("New Material"); + + if (!newMaterialNode.hasParentProperty() + || newMaterialNode.parentProperty() != matLib.defaultNodeListProperty()) { + matLib.defaultNodeListProperty().reparentHere(newMaterialNode); + } + BindingProperty modelMatsProp = modelNode.bindingProperty("materials"); + modelMatsProp.setExpression(newMaterialNode.id()); +} + ModelNode AbstractView::currentStateNode() const { if (model()) |