forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -186,6 +186,7 @@ void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e)
|
||||
|
||||
void Edit3DCanvas::dropEvent(QDropEvent *e)
|
||||
{
|
||||
m_parent->view()->executeInTransaction(__FUNCTION__, [&] {
|
||||
auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode();
|
||||
QTC_ASSERT(modelNode.isValid(), return);
|
||||
|
||||
@@ -193,35 +194,9 @@ void Edit3DCanvas::dropEvent(QDropEvent *e)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
BindingProperty modelMatsProp = modelNode.bindingProperty("materials");
|
||||
modelMatsProp.setExpression(material.id());
|
||||
}
|
||||
if (modelNode.isSubclassOf("QtQuick3D.Model"))
|
||||
m_parent->view()->assignMaterialTo3dModel(modelNode);
|
||||
});
|
||||
}
|
||||
|
||||
void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user