forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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");
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user