forked from qt-creator/qt-creator
QmlDesigner: Implement copying specific material properties section
Change-Id: I34bed00c89018e86941c4e5a7ddeae44c06f850d Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -39,6 +39,8 @@ Item {
|
|||||||
property var currentMaterial: null
|
property var currentMaterial: null
|
||||||
property int currentMaterialIdx: 0
|
property int currentMaterialIdx: 0
|
||||||
|
|
||||||
|
property var matSectionsModel: []
|
||||||
|
|
||||||
// Called also from C++ to close context menu on focus out
|
// Called also from C++ to close context menu on focus out
|
||||||
function closeContextMenu()
|
function closeContextMenu()
|
||||||
{
|
{
|
||||||
@@ -108,16 +110,45 @@ Item {
|
|||||||
height: StudioTheme.Values.border
|
height: StudioTheme.Values.border
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.Menu {
|
||||||
text: qsTr("Copy properties")
|
title: qsTr("Copy properties")
|
||||||
enabled: root.currentMaterial
|
enabled: root.currentMaterial
|
||||||
onTriggered: materialBrowserModel.copyMaterialProperties(root.currentMaterialIdx)
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
onAboutToShow: {
|
||||||
|
root.matSectionsModel = ["All"];
|
||||||
|
|
||||||
|
switch (root.currentMaterial.materialType) {
|
||||||
|
case "DefaultMaterial":
|
||||||
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.defaultMaterialSections);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PrincipledMaterial":
|
||||||
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.principledMaterialSections);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "CustomMaterial":
|
||||||
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.customMaterialSections);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.matSectionsModel
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: modelData
|
||||||
|
enabled: root.currentMaterial
|
||||||
|
onTriggered: materialBrowserModel.copyMaterialProperties(root.currentMaterialIdx, modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Paste properties")
|
text: qsTr("Paste properties")
|
||||||
enabled: root.currentMaterial && root.currentMaterial.materialType.toString()
|
enabled: root.currentMaterial && root.currentMaterial.materialType
|
||||||
=== materialBrowserModel.copiedMaterialType.toString()
|
=== materialBrowserModel.copiedMaterialType
|
||||||
onTriggered: materialBrowserModel.pasteMaterialProperties(root.currentMaterialIdx)
|
onTriggered: materialBrowserModel.pasteMaterialProperties(root.currentMaterialIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -65,8 +65,12 @@ QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
|
|||||||
if (roleName == "materialVisible")
|
if (roleName == "materialVisible")
|
||||||
return isMaterialVisible(index.row());
|
return isMaterialVisible(index.row());
|
||||||
|
|
||||||
if (roleName == "materialType")
|
if (roleName == "materialType") {
|
||||||
return m_materialList.at(index.row()).type();
|
QString matType = QString::fromLatin1(m_materialList.at(index.row()).type());
|
||||||
|
if (matType.startsWith("QtQuick3D."))
|
||||||
|
matType.remove("QtQuick3D.");
|
||||||
|
return matType;
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -85,6 +89,50 @@ bool MaterialBrowserModel::isValidIndex(int idx) const
|
|||||||
return idx > -1 && idx < rowCount();
|
return idx > -1 && idx < rowCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loads and parses propertyGroups.json from QtQuick3D module's designer folder
|
||||||
|
*
|
||||||
|
* propertyGroups.json contains lists of QtQuick3D objects' properties grouped by sections
|
||||||
|
*
|
||||||
|
* @param path path to propertyGroups.json file
|
||||||
|
*/
|
||||||
|
void MaterialBrowserModel::loadPropertyGroups(const QString &path)
|
||||||
|
{
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
if (m_propertyGroupsObj.isEmpty()) {
|
||||||
|
QFile matPropsFile(path);
|
||||||
|
|
||||||
|
if (!matPropsFile.open(QIODevice::ReadOnly)) {
|
||||||
|
qWarning("Couldn't open propertyGroups.json");
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
QJsonDocument matPropsJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll());
|
||||||
|
if (matPropsJsonDoc.isNull()) {
|
||||||
|
qWarning("Invalid propertyGroups.json file");
|
||||||
|
ok = false;
|
||||||
|
} else {
|
||||||
|
m_propertyGroupsObj = matPropsJsonDoc.object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_defaultMaterialSections.clear();
|
||||||
|
m_principledMaterialSections.clear();
|
||||||
|
m_customMaterialSections.clear();
|
||||||
|
if (ok) {
|
||||||
|
m_defaultMaterialSections.append(m_propertyGroupsObj.value("DefaultMaterial").toObject().keys());
|
||||||
|
m_principledMaterialSections.append(m_propertyGroupsObj.value("PrincipledMaterial").toObject().keys());
|
||||||
|
|
||||||
|
QStringList customMatSections = m_propertyGroupsObj.value("CustomMaterial").toObject().keys();
|
||||||
|
if (customMatSections.size() > 1) // as of now custom material has only 1 section, so we don't add it
|
||||||
|
m_customMaterialSections.append(customMatSections);
|
||||||
|
}
|
||||||
|
emit materialSectionsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
|
QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
|
||||||
{
|
{
|
||||||
static const QHash<int, QByteArray> roles {
|
static const QHash<int, QByteArray> roles {
|
||||||
@@ -138,12 +186,12 @@ void MaterialBrowserModel::setHasMaterialRoot(bool b)
|
|||||||
emit hasMaterialRootChanged();
|
emit hasMaterialRootChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeName MaterialBrowserModel::copiedMaterialType() const
|
QString MaterialBrowserModel::copiedMaterialType() const
|
||||||
{
|
{
|
||||||
return m_copiedMaterialType;
|
return m_copiedMaterialType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::setCopiedMaterialType(const TypeName &matType)
|
void MaterialBrowserModel::setCopiedMaterialType(const QString &matType)
|
||||||
{
|
{
|
||||||
if (matType == m_copiedMaterialType)
|
if (matType == m_copiedMaterialType)
|
||||||
return;
|
return;
|
||||||
@@ -294,16 +342,40 @@ void MaterialBrowserModel::duplicateMaterial(int idx)
|
|||||||
emit duplicateMaterialTriggered(m_materialList.at(idx));
|
emit duplicateMaterialTriggered(m_materialList.at(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::copyMaterialProperties(int idx)
|
void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §ion)
|
||||||
{
|
{
|
||||||
ModelNode mat = m_materialList.at(idx);
|
ModelNode mat = m_materialList.at(idx);
|
||||||
m_copiedMaterialProps = mat.properties();
|
QString matType = QString::fromLatin1(mat.type());
|
||||||
setCopiedMaterialType(mat.type());
|
|
||||||
|
if (matType.startsWith("QtQuick3D."))
|
||||||
|
matType.remove("QtQuick3D.");
|
||||||
|
|
||||||
|
setCopiedMaterialType(matType);
|
||||||
|
m_allPropsCopied = section == "All";
|
||||||
|
|
||||||
|
if (m_allPropsCopied || m_propertyGroupsObj.empty()) {
|
||||||
|
m_copiedMaterialProps = mat.properties();
|
||||||
|
} else {
|
||||||
|
QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject();
|
||||||
|
if (propsSpecObj.contains(section)) { // should always be true
|
||||||
|
m_copiedMaterialProps.clear();
|
||||||
|
const QJsonArray propNames = propsSpecObj.value(section).toArray();
|
||||||
|
for (const QJsonValueRef &propName : propNames)
|
||||||
|
m_copiedMaterialProps.append(mat.property(propName.toString().toLatin1()));
|
||||||
|
|
||||||
|
if (section == "Base") { // add QtQuick3D.Material base props as well
|
||||||
|
QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject();
|
||||||
|
const QJsonArray propNames = propsMatObj.value("Base").toArray();
|
||||||
|
for (const QJsonValueRef &propName : propNames)
|
||||||
|
m_copiedMaterialProps.append(mat.property(propName.toString().toLatin1()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::pasteMaterialProperties(int idx)
|
void MaterialBrowserModel::pasteMaterialProperties(int idx)
|
||||||
{
|
{
|
||||||
emit pasteMaterialPropertiesTriggered(m_materialList.at(idx), m_copiedMaterialProps);
|
emit pasteMaterialPropertiesTriggered(m_materialList.at(idx), m_copiedMaterialProps, m_allPropsCopied);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::deleteMaterial(int idx)
|
void MaterialBrowserModel::deleteMaterial(int idx)
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "qjsonobject.h"
|
||||||
#include <modelnode.h>
|
#include <modelnode.h>
|
||||||
#include <qmlobjectnode.h>
|
#include <qmlobjectnode.h>
|
||||||
|
|
||||||
@@ -43,7 +44,10 @@ class MaterialBrowserModel : public QAbstractListModel
|
|||||||
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
||||||
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
|
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
|
||||||
Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged)
|
Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged)
|
||||||
Q_PROPERTY(TypeName copiedMaterialType READ copiedMaterialType WRITE setCopiedMaterialType NOTIFY copiedMaterialTypeChanged)
|
Q_PROPERTY(QString copiedMaterialType READ copiedMaterialType WRITE setCopiedMaterialType NOTIFY copiedMaterialTypeChanged)
|
||||||
|
Q_PROPERTY(QStringList defaultMaterialSections MEMBER m_defaultMaterialSections NOTIFY materialSectionsChanged)
|
||||||
|
Q_PROPERTY(QStringList principledMaterialSections MEMBER m_principledMaterialSections NOTIFY materialSectionsChanged)
|
||||||
|
Q_PROPERTY(QStringList customMaterialSections MEMBER m_customMaterialSections NOTIFY materialSectionsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MaterialBrowserModel(QObject *parent = nullptr);
|
MaterialBrowserModel(QObject *parent = nullptr);
|
||||||
@@ -64,8 +68,8 @@ public:
|
|||||||
bool hasMaterialRoot() const;
|
bool hasMaterialRoot() const;
|
||||||
void setHasMaterialRoot(bool b);
|
void setHasMaterialRoot(bool b);
|
||||||
|
|
||||||
TypeName copiedMaterialType() const;
|
QString copiedMaterialType() const;
|
||||||
void setCopiedMaterialType(const TypeName &matType);
|
void setCopiedMaterialType(const QString &matType);
|
||||||
|
|
||||||
QList<ModelNode> materials() const;
|
QList<ModelNode> materials() const;
|
||||||
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
|
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
|
||||||
@@ -75,12 +79,13 @@ public:
|
|||||||
void updateSelectedMaterial();
|
void updateSelectedMaterial();
|
||||||
int materialIndex(const ModelNode &material) const;
|
int materialIndex(const ModelNode &material) const;
|
||||||
ModelNode materialAt(int idx) const;
|
ModelNode materialAt(int idx) const;
|
||||||
|
void loadPropertyGroups(const QString &path);
|
||||||
|
|
||||||
void resetModel();
|
void resetModel();
|
||||||
|
|
||||||
Q_INVOKABLE void selectMaterial(int idx, bool force = false);
|
Q_INVOKABLE void selectMaterial(int idx, bool force = false);
|
||||||
Q_INVOKABLE void duplicateMaterial(int idx);
|
Q_INVOKABLE void duplicateMaterial(int idx);
|
||||||
Q_INVOKABLE void copyMaterialProperties(int idx);
|
Q_INVOKABLE void copyMaterialProperties(int idx, const QString §ion);
|
||||||
Q_INVOKABLE void pasteMaterialProperties(int idx);
|
Q_INVOKABLE void pasteMaterialProperties(int idx);
|
||||||
Q_INVOKABLE void deleteMaterial(int idx);
|
Q_INVOKABLE void deleteMaterial(int idx);
|
||||||
Q_INVOKABLE void renameMaterial(int idx, const QString &newName);
|
Q_INVOKABLE void renameMaterial(int idx, const QString &newName);
|
||||||
@@ -94,13 +99,15 @@ signals:
|
|||||||
void hasModelSelectionChanged();
|
void hasModelSelectionChanged();
|
||||||
void hasMaterialRootChanged();
|
void hasMaterialRootChanged();
|
||||||
void copiedMaterialTypeChanged();
|
void copiedMaterialTypeChanged();
|
||||||
|
void materialSectionsChanged();
|
||||||
void selectedIndexChanged(int idx);
|
void selectedIndexChanged(int idx);
|
||||||
void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName);
|
void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName);
|
||||||
void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
||||||
void addNewMaterialTriggered();
|
void addNewMaterialTriggered();
|
||||||
void duplicateMaterialTriggered(const QmlDesigner::ModelNode &material);
|
void duplicateMaterialTriggered(const QmlDesigner::ModelNode &material);
|
||||||
void pasteMaterialPropertiesTriggered(const QmlDesigner::ModelNode &material,
|
void pasteMaterialPropertiesTriggered(const QmlDesigner::ModelNode &material,
|
||||||
const QList<QmlDesigner::AbstractProperty> &props);
|
const QList<QmlDesigner::AbstractProperty> &props,
|
||||||
|
bool all);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isMaterialVisible(int idx) const;
|
bool isMaterialVisible(int idx) const;
|
||||||
@@ -108,15 +115,20 @@ private:
|
|||||||
|
|
||||||
QString m_searchText;
|
QString m_searchText;
|
||||||
QList<ModelNode> m_materialList;
|
QList<ModelNode> m_materialList;
|
||||||
|
QStringList m_defaultMaterialSections;
|
||||||
|
QStringList m_principledMaterialSections;
|
||||||
|
QStringList m_customMaterialSections;
|
||||||
QList<AbstractProperty> m_copiedMaterialProps;
|
QList<AbstractProperty> m_copiedMaterialProps;
|
||||||
QHash<qint32, int> m_materialIndexHash; // internalId -> index
|
QHash<qint32, int> m_materialIndexHash; // internalId -> index
|
||||||
|
QJsonObject m_propertyGroupsObj;
|
||||||
|
|
||||||
int m_selectedIndex = 0;
|
int m_selectedIndex = 0;
|
||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
bool m_hasQuick3DImport = false;
|
bool m_hasQuick3DImport = false;
|
||||||
bool m_hasModelSelection = false;
|
bool m_hasModelSelection = false;
|
||||||
bool m_hasMaterialRoot = false;
|
bool m_hasMaterialRoot = false;
|
||||||
TypeName m_copiedMaterialType;
|
bool m_allPropsCopied = true;
|
||||||
|
QString m_copiedMaterialType;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include "materialbrowserwidget.h"
|
#include "materialbrowserwidget.h"
|
||||||
#include "materialbrowsermodel.h"
|
#include "materialbrowsermodel.h"
|
||||||
#include "nodeabstractproperty.h"
|
#include "nodeabstractproperty.h"
|
||||||
|
#include "nodemetainfo.h"
|
||||||
#include "qmlobjectnode.h"
|
#include "qmlobjectnode.h"
|
||||||
#include "variantproperty.h"
|
#include "variantproperty.h"
|
||||||
|
|
||||||
@@ -43,7 +44,6 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
MaterialBrowserView::MaterialBrowserView(QObject *parent)
|
MaterialBrowserView::MaterialBrowserView(QObject *parent)
|
||||||
: AbstractView(parent)
|
: AbstractView(parent)
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
MaterialBrowserView::~MaterialBrowserView()
|
MaterialBrowserView::~MaterialBrowserView()
|
||||||
@@ -91,14 +91,16 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
});
|
});
|
||||||
|
|
||||||
connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this,
|
connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this,
|
||||||
[&] (const ModelNode &material, const QList<AbstractProperty> &props) {
|
[&] (const ModelNode &material, const QList<AbstractProperty> &props, bool all) {
|
||||||
QmlObjectNode mat(material);
|
QmlObjectNode mat(material);
|
||||||
executeInTransaction(__FUNCTION__, [&] {
|
executeInTransaction(__FUNCTION__, [&] {
|
||||||
// remove current properties
|
if (all) { // all material properties copied
|
||||||
const PropertyNameList propNames = material.propertyNames();
|
// remove current properties
|
||||||
for (const PropertyName &propName : propNames) {
|
const PropertyNameList propNames = material.propertyNames();
|
||||||
if (propName != "objectName")
|
for (const PropertyName &propName : propNames) {
|
||||||
mat.removeProperty(propName);
|
if (propName != "objectName")
|
||||||
|
mat.removeProperty(propName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply pasted properties
|
// apply pasted properties
|
||||||
@@ -126,6 +128,10 @@ void MaterialBrowserView::modelAttached(Model *model)
|
|||||||
{
|
{
|
||||||
AbstractView::modelAttached(model);
|
AbstractView::modelAttached(model);
|
||||||
|
|
||||||
|
QString matPropsPath = model->metaInfo("QtQuick3D.Material").importDirectoryPath()
|
||||||
|
+ "/designer/propertyGroups.json";
|
||||||
|
m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath);
|
||||||
|
|
||||||
m_widget->clearSearchFilter();
|
m_widget->clearSearchFilter();
|
||||||
m_widget->materialBrowserModel()->setHasMaterialRoot(rootModelNode().isSubclassOf("QtQuick3D.Material"));
|
m_widget->materialBrowserModel()->setHasMaterialRoot(rootModelNode().isSubclassOf("QtQuick3D.Material"));
|
||||||
m_hasQuick3DImport = model->hasImport("QtQuick3D");
|
m_hasQuick3DImport = model->hasImport("QtQuick3D");
|
||||||
|
Reference in New Issue
Block a user