QmlDesigner: Add support for component materials

Component materials can now be seen on material browser and their
properties are properly shown on material editor

Fixes: QDS-7390
Change-Id: I3f7edfe655bdb0da1fa71739c825d09d6101c386
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Miikka Heikkinen
2022-08-12 12:47:21 +03:00
parent 2e8574bd76
commit 326f70c40f
8 changed files with 223 additions and 24 deletions

View File

@@ -63,6 +63,24 @@ PropertyEditorPane {
Item { width: 1; height: 10 }
Loader {
id: specificsTwo
property string theSource: specificQmlData
anchors.left: parent.left
anchors.right: parent.right
visible: theSource !== ""
sourceComponent: specificQmlComponent
onTheSourceChanged: {
active = false
active = true
}
}
Item { width: 1; height: 10 }
Loader {
id: specificsOne
anchors.left: parent.left

View File

@@ -227,17 +227,8 @@ Column {
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
ComboBox {
currentIndex: {
if (backendValues.__classNamePrivateInternal.value === "CustomMaterial")
return 2
if (backendValues.__classNamePrivateInternal.value === "PrincipledMaterial")
return 1
return 0
}
model: ["DefaultMaterial", "PrincipledMaterial", "CustomMaterial"]
currentIndex: possibleTypeIndex
model: possibleTypes
showExtendedFunctionButton: false
implicitWidth: StudioTheme.Values.singleControlColumnWidth

View File

@@ -163,7 +163,7 @@ void MaterialBrowserView::refreshModel(bool updateImages)
bool MaterialBrowserView::isMaterial(const ModelNode &node) const
{
if (!node.isValid() || node.isComponent())
if (!node.isValid())
return false;
return node.isSubclassOf("QtQuick3D.Material");

View File

@@ -32,6 +32,7 @@
#include <qmlmodelnodeproxy.h>
#include <qmlobjectnode.h>
#include <qmltimeline.h>
#include <documentmanager.h>
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
@@ -47,12 +48,24 @@
namespace QmlDesigner {
MaterialEditorContextObject::MaterialEditorContextObject(QObject *parent)
MaterialEditorContextObject::MaterialEditorContextObject(QQmlContext *context, QObject *parent)
: QObject(parent)
, m_qmlContext(context)
{
qmlRegisterUncreatableType<MaterialEditorContextObject>("ToolBarAction", 1, 0, "ToolBarAction", "Enum type");
}
QQmlComponent *MaterialEditorContextObject::specificQmlComponent()
{
if (m_specificQmlComponent)
return m_specificQmlComponent;
m_specificQmlComponent = new QQmlComponent(m_qmlContext->engine(), this);
m_specificQmlComponent->setData(m_specificQmlData.toUtf8(), QUrl::fromLocalFile("specifics.qml"));
return m_specificQmlComponent;
}
QString MaterialEditorContextObject::convertColorToString(const QVariant &color)
{
QString colorString;
@@ -158,8 +171,10 @@ void MaterialEditorContextObject::changeTypeName(const QString &typeName)
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
if (msgBox.exec() == QMessageBox::Cancel)
if (msgBox.exec() == QMessageBox::Cancel) {
updatePossibleTypeIndex();
return;
}
for (const auto &p : std::as_const(incompatibleProperties))
m_selectedMaterial.removeProperty(p);
@@ -275,6 +290,20 @@ void MaterialEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl)
emit specificsUrlChanged();
}
void MaterialEditorContextObject::setSpecificQmlData(const QString &newSpecificQmlData)
{
if (newSpecificQmlData == m_specificQmlData)
return;
m_specificQmlData = newSpecificQmlData;
delete m_specificQmlComponent;
m_specificQmlComponent = nullptr;
emit specificQmlComponentChanged();
emit specificQmlDataChanged();
}
void MaterialEditorContextObject::setStateName(const QString &newStateName)
{
if (newStateName == m_stateName)
@@ -293,6 +322,23 @@ void MaterialEditorContextObject::setAllStateNames(const QStringList &allStates)
emit allStateNamesChanged();
}
void MaterialEditorContextObject::setPossibleTypes(const QStringList &types)
{
if (types == m_possibleTypes)
return;
m_possibleTypes = types;
emit possibleTypesChanged();
updatePossibleTypeIndex();
}
void MaterialEditorContextObject::setCurrentType(const QString &type)
{
m_currentType = type.split('.').last();
updatePossibleTypeIndex();
}
void MaterialEditorContextObject::setIsBaseState(bool newIsBaseState)
{
if (newIsBaseState == m_isBaseState)
@@ -339,6 +385,20 @@ void MaterialEditorContextObject::setHasAliasExport(bool hasAliasExport)
emit hasAliasExportChanged();
}
void MaterialEditorContextObject::updatePossibleTypeIndex()
{
int newIndex = -1;
if (!m_currentType.isEmpty())
newIndex = m_possibleTypes.indexOf(m_currentType);
// Emit valid possible type index change even if the index doesn't change, as currentIndex on
// QML side will change to default internally if model is updated
if (m_possibleTypeIndex != -1 || m_possibleTypeIndex != newIndex) {
m_possibleTypeIndex = newIndex;
emit possibleTypeIndexChanged();
}
}
void MaterialEditorContextObject::hideCursor()
{
if (QApplication::overrideCursor())
@@ -403,4 +463,10 @@ bool MaterialEditorContextObject::isBlocked(const QString &propName) const
return false;
}
void MaterialEditorContextObject::goIntoComponent()
{
QTC_ASSERT(m_model, return);
DocumentManager::goIntoComponent(m_selectedMaterial);
}
} // QmlDesigner

View File

@@ -43,9 +43,13 @@ class MaterialEditorContextObject : public QObject
Q_OBJECT
Q_PROPERTY(QUrl specificsUrl READ specificsUrl WRITE setSpecificsUrl NOTIFY specificsUrlChanged)
Q_PROPERTY(QString specificQmlData READ specificQmlData WRITE setSpecificQmlData NOTIFY specificQmlDataChanged)
Q_PROPERTY(QQmlComponent *specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged)
Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged)
Q_PROPERTY(QStringList allStateNames READ allStateNames WRITE setAllStateNames NOTIFY allStateNamesChanged)
Q_PROPERTY(QStringList possibleTypes READ possibleTypes WRITE setPossibleTypes NOTIFY possibleTypesChanged)
Q_PROPERTY(int possibleTypeIndex READ possibleTypeIndex NOTIFY possibleTypeIndexChanged)
Q_PROPERTY(bool isBaseState READ isBaseState WRITE setIsBaseState NOTIFY isBaseStateChanged)
Q_PROPERTY(bool selectionChanged READ selectionChanged WRITE setSelectionChanged NOTIFY selectionChangedChanged)
@@ -61,11 +65,15 @@ class MaterialEditorContextObject : public QObject
Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
public:
MaterialEditorContextObject(QObject *parent = nullptr);
MaterialEditorContextObject(QQmlContext *context, QObject *parent = nullptr);
QUrl specificsUrl() const { return m_specificsUrl; }
QString specificQmlData() const {return m_specificQmlData; }
QQmlComponent *specificQmlComponent();
QString stateName() const { return m_stateName; }
QStringList allStateNames() const { return m_allStateNames; }
QStringList possibleTypes() const { return m_possibleTypes; }
int possibleTypeIndex() const { return m_possibleTypeIndex; }
bool isBaseState() const { return m_isBaseState; }
bool selectionChanged() const { return m_selectionChanged; }
@@ -87,6 +95,7 @@ public:
Q_INVOKABLE QStringList allStatesForId(const QString &id);
Q_INVOKABLE bool isBlocked(const QString &propName) const;
Q_INVOKABLE void goIntoComponent();
enum ToolBarAction {
ApplyToSelected = 0,
@@ -117,8 +126,11 @@ public:
void setSelectedMaterial(const ModelNode &matNode);
void setSpecificsUrl(const QUrl &newSpecificsUrl);
void setSpecificQmlData(const QString &newSpecificQmlData);
void setStateName(const QString &newStateName);
void setAllStateNames(const QStringList &allStates);
void setPossibleTypes(const QStringList &types);
void setCurrentType(const QString &type);
void setIsBaseState(bool newIsBaseState);
void setSelectionChanged(bool newSelectionChanged);
void setBackendValues(QQmlPropertyMap *newBackendValues);
@@ -129,8 +141,12 @@ public:
signals:
void specificsUrlChanged();
void specificQmlDataChanged();
void specificQmlComponentChanged();
void stateNameChanged();
void allStateNamesChanged();
void possibleTypesChanged();
void possibleTypeIndexChanged();
void isBaseStateChanged();
void selectionChangedChanged();
void backendValuesChanged();
@@ -142,15 +158,22 @@ signals:
void hasModelSelectionChanged();
private:
void updatePossibleTypeIndex();
QUrl m_specificsUrl;
QString m_specificQmlData;
QQmlComponent *m_specificQmlComponent = nullptr;
QQmlContext *m_qmlContext = nullptr;
QString m_stateName;
QStringList m_allStateNames;
QStringList m_possibleTypes;
int m_possibleTypeIndex = -1;
QString m_currentType;
int m_majorVersion = 1;
QQmlPropertyMap *m_backendValues = nullptr;
QQmlComponent *m_qmlComponent = nullptr;
Model *m_model = nullptr;
QPoint m_lastPos;

View File

@@ -102,7 +102,7 @@ public:
MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor)
: m_view(new QQuickWidget)
, m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor))
, m_contextObject(new MaterialEditorContextObject())
, m_contextObject(new MaterialEditorContextObject(m_view->rootContext()))
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
{
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);

View File

@@ -51,6 +51,7 @@
#include <qmldesignerplugin.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <propertyeditorqmlbackend.h>
#include <QApplication>
#include <QDebug>
@@ -83,6 +84,10 @@ MaterialEditorView::MaterialEditorView(QWidget *parent)
}
});
m_typeUpdateTimer.setSingleShot(true);
m_typeUpdateTimer.setInterval(500);
connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes);
m_stackedWidget->setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
m_stackedWidget->setMinimumWidth(250);
@@ -526,14 +531,30 @@ void MaterialEditorView::setupQmlBackend()
{
QUrl qmlPaneUrl;
QUrl qmlSpecificsUrl;
QString specificQmlData;
QString currentTypeName;
if (m_selectedMaterial.isValid() && m_hasQuick3DImport) {
qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/MaterialEditorPane.qml");
TypeName diffClassName;
NodeMetaInfo metaInfo = m_selectedMaterial.metaInfo();
QDir importDir(metaInfo.importDirectoryPath() + Constants::QML_DESIGNER_SUBFOLDER);
QString typeName = QString::fromUtf8(metaInfo.typeName().split('.').constLast());
qmlSpecificsUrl = QUrl::fromLocalFile(importDir.absoluteFilePath(typeName + "Specifics.qml"));
if (metaInfo.isValid()) {
diffClassName = metaInfo.typeName();
const QList<NodeMetaInfo> hierarchy = metaInfo.classHierarchy();
for (const NodeMetaInfo &metaInfo : hierarchy) {
if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsUrl))
break;
qmlSpecificsUrl = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName()
+ "Specifics", metaInfo);
diffClassName = metaInfo.typeName();
}
}
if (metaInfo.isValid() && diffClassName != m_selectedMaterial.type()) {
specificQmlData = PropertyEditorQmlBackend::templateGeneration(
metaInfo, model()->metaInfo(diffClassName), m_selectedMaterial);
}
currentTypeName = QString::fromLatin1(m_selectedMaterial.type());
} else {
qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/EmptyMaterialEditorPane.qml");
}
@@ -566,11 +587,15 @@ void MaterialEditorView::setupQmlBackend()
currentQmlBackend->widget()->installEventFilter(this);
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
currentQmlBackend->contextObject()->setHasMaterialRoot(m_hasMaterialRoot);
m_stackedWidget->setCurrentWidget(currentQmlBackend->widget());
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
currentQmlBackend->contextObject()->setCurrentType(currentTypeName);
m_qmlBackEnd = currentQmlBackend;
delayedTypeUpdate();
initPreviewData();
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
}
void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
@@ -636,6 +661,53 @@ void MaterialEditorView::initPreviewData()
}
}
void MaterialEditorView::delayedTypeUpdate()
{
m_typeUpdateTimer.start();
}
static Import entryToImport(const ItemLibraryEntry &entry)
{
if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
return Import::createFileImport(entry.requiredImport());
return Import::createLibraryImport(entry.requiredImport(),
QString::number(entry.majorVersion()) + QLatin1Char('.') +
QString::number(entry.minorVersion()));
}
void MaterialEditorView::updatePossibleTypes()
{
QTC_ASSERT(model(), return);
if (!m_qmlBackEnd)
return;
// Ensure basic types are always first
static const QStringList basicTypes {"DefaultMaterial", "PrincipledMaterial", "CustomMaterial"};
QStringList allTypes = basicTypes;
const QList<ItemLibraryEntry> itemLibEntries = m_itemLibraryInfo->entries();
for (const ItemLibraryEntry &entry : itemLibEntries) {
NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName());
bool valid = metaInfo.isValid()
&& (metaInfo.majorVersion() >= entry.majorVersion()
|| metaInfo.majorVersion() < 0);
if (valid && metaInfo.isSubclassOf("QtQuick3D.Material")) {
bool addImport = entry.requiredImport().isEmpty();
if (!addImport) {
Import import = entryToImport(entry);
addImport = model()->hasImport(import, true, true);
}
if (addImport) {
QString typeName = QString::fromLatin1(entry.typeName().split('.').last());
if (!allTypes.contains(typeName))
allTypes.append(typeName);
}
}
}
m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes);
}
void MaterialEditorView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
@@ -653,6 +725,18 @@ void MaterialEditorView::modelAttached(Model *model)
m_ensureMatLibTimer.start(500);
}
if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) {
if (m_itemLibraryInfo) {
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &MaterialEditorView::delayedTypeUpdate);
}
m_itemLibraryInfo = model->metaInfo().itemLibraryInfo();
if (m_itemLibraryInfo) {
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &MaterialEditorView::delayedTypeUpdate);
}
}
if (!m_setupCompleted) {
reloadQml();
m_setupCompleted = true;
@@ -816,10 +900,20 @@ void MaterialEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
m_locked = false;
}
void MaterialEditorView::nodeTypeChanged(const ModelNode &node, const TypeName &, int, int)
void MaterialEditorView::nodeTypeChanged(const ModelNode &node, const TypeName &typeName, int, int)
{
if (node == m_selectedMaterial)
if (node == m_selectedMaterial) {
m_qmlBackEnd->contextObject()->setCurrentType(QString::fromLatin1(typeName));
delayedResetView();
}
}
void MaterialEditorView::rootNodeTypeChanged(const QString &type, int, int)
{
if (rootModelNode() == m_selectedMaterial) {
m_qmlBackEnd->contextObject()->setCurrentType(type);
delayedResetView();
}
}
void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap)

View File

@@ -26,6 +26,8 @@
#pragma once
#include <abstractview.h>
#include <itemlibraryinfo.h>
#include <QHash>
#include <QPointer>
#include <QTimer>
@@ -70,6 +72,7 @@ public:
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override;
void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override;
void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
@@ -119,9 +122,12 @@ private:
bool noValidSelection() const;
void initPreviewData();
void delayedTypeUpdate();
void updatePossibleTypes();
ModelNode m_selectedMaterial;
QTimer m_ensureMatLibTimer;
QTimer m_typeUpdateTimer;
QShortcut *m_updateShortcut = nullptr;
int m_timerId = 0;
QStackedWidget *m_stackedWidget = nullptr;
@@ -134,6 +140,7 @@ private:
bool m_hasMaterialRoot = false;
QPointer<QColorDialog> m_colorDialog;
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
};
} // namespace QmlDesigner