From 7222a14808fbaf9c324db6fda39c4896101ca40d Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 20 Feb 2025 11:30:17 +0200 Subject: [PATCH] QmlDesigner: Add backend for materials in PropertyEditor Task-number: QDS-14784 Change-Id: I531124231545971c5ceb171da8685efeb3eaf0fb Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../propertyeditorqmlbackend.cpp | 47 ++- .../propertyeditor/propertyeditorqmlbackend.h | 5 + .../propertyeditor/propertyeditorview.cpp | 70 ++++- .../propertyeditor/qmlmaterialnodeproxy.cpp | 285 ++++++++++++++++++ .../propertyeditor/qmlmaterialnodeproxy.h | 89 ++++++ .../quick2propertyeditorview.cpp | 2 + 7 files changed, 486 insertions(+), 13 deletions(-) create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 2140f15dfe5..baa53432110 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -422,6 +422,7 @@ extend_qtc_plugin(QmlDesigner propertynamevalidator.cpp propertynamevalidator.h tooltip.cpp tooltip.h qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h + qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h quick2propertyeditorview.cpp quick2propertyeditorview.h propertyeditorutils.cpp propertyeditorutils.h diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 110dbf4cf11..c9297694fd0 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,19 @@ namespace QmlDesigner { using namespace Qt::StringLiterals; +static bool isMaterialAuxiliaryKey(AuxiliaryDataKeyView key) +{ + static constexpr auto previewKeys = Utils::to_array( + materialPreviewEnvDocProperty, + materialPreviewEnvValueDocProperty, + materialPreviewModelDocProperty, + materialPreviewEnvProperty, + materialPreviewEnvValueProperty, + materialPreviewModelProperty); + + return std::ranges::find(previewKeys, key) != std::ranges::end(previewKeys); +} + PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, AsynchronousImageCache &imageCache) : m_view(Utils::makeUniqueObjectPtr(imageCache)) @@ -307,19 +321,41 @@ void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy( m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName); } +void PropertyEditorQmlBackend::handleAuxiliaryDataChanges(const QmlObjectNode &qmlObjectNode, + AuxiliaryDataKeyView key) +{ + if (qmlObjectNode.isRootModelNode() && isMaterialAuxiliaryKey(key)) { + m_backendMaterialNode.handleAuxiliaryPropertyChanges(); + m_view->instanceImageProvider()->invalidate(); + } +} + void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property) { m_backendModelNode.handleVariantPropertyChanged(property); + updateInstanceImage(); } void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property) { m_backendModelNode.handleBindingPropertyChanged(property); + updateInstanceImage(); +} + +void PropertyEditorQmlBackend::handleBindingPropertyInModelNodeProxyAboutToChange( + const BindingProperty &property) +{ + if (m_backendMaterialNode.materialNode()) { + ModelNode expressionNode = property.resolveToModelNode(); + if (expressionNode.metaInfo().isQtQuick3DTexture()) + updateInstanceImage(); + } } void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property) { m_backendModelNode.handlePropertiesRemoved(property); + updateInstanceImage(); } void PropertyEditorQmlBackend::handleModelNodePreviewPixmapChanged(const ModelNode &node, @@ -496,10 +532,15 @@ void QmlDesigner::PropertyEditorQmlBackend::createPropertyEditorValues(const Qml #endif } +void PropertyEditorQmlBackend::updateInstanceImage() +{ + m_view->instanceImageProvider()->invalidate(); + refreshPreview(); +} + void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor) { if (qmlObjectNode.isValid()) { - m_contextObject->setModel(propertyEditor->model()); qCInfo(propertyEditorBenchmark) << Q_FUNC_INFO; @@ -517,6 +558,8 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q m_backendModelNode.setup(qmlObjectNode.modelNode()); context()->setContextProperty("modelNodeBackend", &m_backendModelNode); + m_backendMaterialNode.setup(qmlObjectNode); + // className auto valueObject = qobject_cast(variantToQObject( m_backendValuesPropertyMap.value(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY))); @@ -607,6 +650,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q contextObject()->setHasQuick3DImport(propertyEditor->model()->hasImport("QtQuick3D")); m_view->instanceImageProvider()->setModelNode(propertyEditor->firstSelectedModelNode()); + updateInstanceImage(); qCInfo(propertyEditorBenchmark) << "final:" << time.elapsed(); } else { @@ -950,6 +994,7 @@ void PropertyEditorQmlBackend::setupContextProperties() { context()->setContextProperties({ {"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)}, + {"materialNodeBackend", QVariant::fromValue(&m_backendMaterialNode)}, {"anchorBackend", QVariant::fromValue(&m_backendAnchorBinding)}, {"transaction", QVariant::fromValue(m_propertyEditorTransaction.get())}, {"dummyBackendValue", QVariant::fromValue(m_dummyPropertyEditorValue.get())}, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index cf464282717..8e735232870 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -7,6 +7,7 @@ #include "propertyeditorcontextobject.h" #include "propertyeditorvalue.h" #include "qmlanchorbindingproxy.h" +#include "qmlmaterialnodeproxy.h" #include "qmlmodelnodeproxy.h" #include "quick2propertyeditorview.h" @@ -84,8 +85,10 @@ public: void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode, PropertyNameView propertyName); + void handleAuxiliaryDataChanges(const QmlObjectNode &qmlObjectNode, AuxiliaryDataKeyView key); void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property); void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property); + void handleBindingPropertyInModelNodeProxyAboutToChange(const BindingProperty &property); void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property); void handleModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap, @@ -95,6 +98,7 @@ public: void refreshBackendModel(); void refreshPreview(); + void updateInstanceImage(); void setupContextProperties(); @@ -124,6 +128,7 @@ private: Utils::UniqueObjectPtr m_view = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; + QmlMaterialNodeProxy m_backendMaterialNode; QmlModelNodeProxy m_backendModelNode; std::unique_ptr m_propertyEditorTransaction; std::unique_ptr m_dummyPropertyEditorValue; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index d1d08cb5c7f..3eb0c0c8d4e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -755,6 +755,7 @@ void PropertyEditorView::propertiesRemoved(const QList &proper QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + bool changed = false; for (const AbstractProperty &property : propertyList) { m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property); @@ -765,6 +766,7 @@ void PropertyEditorView::propertiesRemoved(const QList &proper if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) { m_locked = true; + changed = true; const PropertyName propertyName = property.name().toByteArray(); PropertyName convertedpropertyName = propertyName; @@ -814,6 +816,8 @@ void PropertyEditorView::propertiesRemoved(const QList &proper m_qmlBackEndForCurrentType->backendAnchorBinding().invalidate(m_selectedNode); } } + if (changed) + m_qmlBackEndForCurrentType->updateInstanceImage(); } void PropertyEditorView::variantPropertiesChanged(const QList& propertyList, PropertyChangeFlags /*propertyChange*/) @@ -823,6 +827,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList& QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + bool changed = false; + + bool selectedNodeIsMaterial = m_selectedNode.metaInfo().isQtQuick3DMaterial(); + bool selectedNodeHasBindingProperties = !m_selectedNode.bindingProperties().isEmpty(); + for (const VariantProperty &property : propertyList) { m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property); @@ -841,17 +850,38 @@ void PropertyEditorView::variantPropertiesChanged(const QList& setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name())); else setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name())); + changed = true; + } + + if (!changed) { + // Check if property changes affects the selected node preview + + if (selectedNodeIsMaterial && selectedNodeHasBindingProperties + && node.metaInfo().isQtQuick3DTexture()) { + changed = true; + } } } + + if (changed) + m_qmlBackEndForCurrentType->updateInstanceImage(); } -void PropertyEditorView::bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags /*propertyChange*/) +void PropertyEditorView::bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags /*propertyChange*/) { - if (locked() || noValidSelection()) + if (noValidSelection()) return; - QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + QTC_ASSERT(m_qmlBackEndForCurrentType, return); + if (locked()) { + for (const BindingProperty &property : propertyList) + m_qmlBackEndForCurrentType->handleBindingPropertyInModelNodeProxyAboutToChange(property); + return; + } + + bool changed = false; for (const BindingProperty &property : propertyList) { m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property); @@ -868,8 +898,12 @@ void PropertyEditorView::bindingPropertiesChanged(const QList & QString exp = QmlObjectNode(m_selectedNode).bindingProperty(property.name()).expression(); m_qmlBackEndForCurrentType->setExpression(property.name(), exp); m_locked = false; + changed = true; } } + + if (changed) + m_qmlBackEndForCurrentType->updateInstanceImage(); } void PropertyEditorView::auxiliaryDataChanged(const ModelNode &node, @@ -879,10 +913,21 @@ void PropertyEditorView::auxiliaryDataChanged(const ModelNode &node, if (noValidSelection()) return; + bool saved = false; + + QScopeGuard rootGuard([this, node, key, &saved] { + if (node.isRootNode()) { + if (!saved) + m_qmlBackEndForCurrentType->setValueforAuxiliaryProperties(m_selectedNode, key); + m_qmlBackEndForCurrentType->handleAuxiliaryDataChanges(node, key); + } + }); + if (!node.isSelected()) return; m_qmlBackEndForCurrentType->setValueforAuxiliaryProperties(m_selectedNode, key); + saved = true; if (key == insightEnabledProperty) m_qmlBackEndForCurrentType->contextObject()->setInsightEnabled(data.toBool()); @@ -979,7 +1024,7 @@ void PropertyEditorView::instancePropertyChanged(const QList; for (const ModelNodePropertyPair &propertyPair : propertyList) { const ModelNode modelNode = propertyPair.first; @@ -989,20 +1034,21 @@ void PropertyEditorView::instancePropertyChanged(const QListhandleInstancePropertyChangedInModelNodeProxy(modelNode, propertyName); - if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode + if (qmlObjectNode.isValid() && modelNode == m_selectedNode && qmlObjectNode.currentState().isValid()) { const AbstractProperty property = modelNode.property(propertyName); - if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) { - if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() ) - setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name())); - else - setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name())); - } + if (!modelNode.hasProperty(propertyName) || property.isBindingProperty()) + setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name())); + else + setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name())); + changed = true; } } - m_locked = false; + if (changed) + m_qmlBackEndForCurrentType->updateInstanceImage(); + m_locked = false; } void PropertyEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp new file mode 100644 index 00000000000..6cac8b471c7 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.cpp @@ -0,0 +1,285 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmlmaterialnodeproxy.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +using namespace Qt::StringLiterals; + +static void renameMaterial(ModelNode &material, const QString &newName) +{ + QTC_ASSERT(material.isValid(), return); + QmlObjectNode(material).setNameAndId(newName, "material"); +} + +QmlMaterialNodeProxy::QmlMaterialNodeProxy() + : QObject() + , m_previewUpdateTimer(this) +{ + m_previewUpdateTimer.setInterval(200); + m_previewUpdateTimer.setSingleShot(true); + m_previewUpdateTimer.callOnTimeout( + std::bind_front(&QmlMaterialNodeProxy::updatePreviewModel, this)); +} + +QmlMaterialNodeProxy::~QmlMaterialNodeProxy() = default; + +void QmlMaterialNodeProxy::setup(const QmlObjectNode &objectNode) +{ + const QmlObjectNode material = objectNode.metaInfo().isQtQuick3DMaterial() ? objectNode + : QmlObjectNode{}; + setMaterialNode(material); + updatePossibleTypes(); + updatePreviewModel(); +} + +ModelNode QmlMaterialNodeProxy::materialNode() const +{ + return m_materialNode; +} + +void QmlMaterialNodeProxy::setPossibleTypes(const QStringList &types) +{ + if (types == m_possibleTypes) + return; + + m_possibleTypes = types; + emit possibleTypesChanged(); + + updatePossibleTypeIndex(); +} + +void QmlMaterialNodeProxy::updatePossibleTypes() +{ + static const QStringList basicTypes{ + "CustomMaterial", + "DefaultMaterial", + "PrincipledMaterial", + "SpecularGlossyMaterial", + }; + + const QString &matType = materialNode().simplifiedTypeName(); + setPossibleTypes(basicTypes.contains(matType) ? basicTypes : QStringList{matType}); + setCurrentType(matType); +} + +void QmlMaterialNodeProxy::setCurrentType(const QString &type) +{ + m_currentType = type.split('.').last(); + updatePossibleTypeIndex(); +} + +void QmlMaterialNodeProxy::toolBarAction(int action) +{ + QTC_ASSERT(hasQuick3DImport(), return); + + switch (ToolBarAction(action)) { + case ToolBarAction::ApplyToSelected: { + Utils3D::applyMaterialToModels(materialView(), + materialNode(), + Utils3D::getSelectedModels(materialView())); + break; + } + + case ToolBarAction::ApplyToSelectedAdd: { + Utils3D::applyMaterialToModels(materialView(), + materialNode(), + Utils3D::getSelectedModels(materialView()), + true); + break; + } + + case ToolBarAction::AddNewMaterial: { + if (!materialNode()) + break; + + ModelNode newMatNode; + AbstractView *view = materialView(); + view->executeInTransaction(__FUNCTION__, [&] { + ModelNode matLib = Utils3D::materialLibraryNode(view); + if (!matLib.isValid()) + return; +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newMatNode = view->createModelNode("PrincipledMaterial"); +#else + NodeMetaInfo metaInfo = materialView()->model()->qtQuick3DPrincipledMaterialMetaInfo(); + newMatNode = materialView()->createModelNode("QtQuick3D.PrincipledMaterial", + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif + renameMaterial(newMatNode, "New Material"); + Utils3D::materialLibraryNode(view).defaultNodeListProperty().reparentHere(newMatNode); + }); + QTimer::singleShot(0, this, [newMatNode]() { + newMatNode.model()->setSelectedModelNodes({newMatNode}); + }); + break; + } + + case ToolBarAction::DeleteCurrentMaterial: { + if (materialNode().isValid()) + materialView()->executeInTransaction(__FUNCTION__, [&] { materialNode().destroy(); }); + break; + } + + case ToolBarAction::OpenMaterialBrowser: { + QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser", true); + break; + } + } +} + +void QmlMaterialNodeProxy::setPreviewEnv(const QString &envAndValue) +{ + if (envAndValue.isEmpty()) + return; + + if (!hasQuick3DImport()) + return; + + AbstractView *view = m_materialNode.modelNode().view(); + ModelNode rootModelNode = view->rootModelNode(); + + QStringList parts = envAndValue.split('='); + QString env = parts[0]; + QString value; + if (parts.size() > 1) + value = parts[1]; + + if (env == "Color" && value.isEmpty()) + value = rootModelNode.auxiliaryDataWithDefault(materialPreviewColorDocProperty).toString(); + + auto renderPreviews = [rootModelNode](const QString &auxEnv, const QString &auxValue) { + if (!rootModelNode) + return; + rootModelNode.setAuxiliaryData(materialPreviewEnvDocProperty, auxEnv); + rootModelNode.setAuxiliaryData(materialPreviewEnvProperty, auxEnv); + rootModelNode.setAuxiliaryData(materialPreviewEnvValueDocProperty, auxValue); + rootModelNode.setAuxiliaryData(materialPreviewEnvValueProperty, auxValue); + + if (auxEnv == "Color" && !auxValue.isEmpty()) + rootModelNode.setAuxiliaryData(materialPreviewColorDocProperty, auxEnv); + + rootModelNode.view()->emitCustomNotification("refresh_material_browser", {}); + }; + + QMetaObject::invokeMethod(view, renderPreviews, env, value); +} + +void QmlMaterialNodeProxy::setPreviewModel(const QString &modelStr) +{ + if (modelStr.isEmpty()) + return; + + if (!hasQuick3DImport()) + return; + + AbstractView *view = m_materialNode.modelNode().view(); + ModelNode rootModelNode = view->rootModelNode(); + + auto renderPreviews = [rootModelNode](const QString &modelStr) { + if (!rootModelNode) + return; + + rootModelNode.setAuxiliaryData(materialPreviewModelDocProperty, modelStr); + rootModelNode.setAuxiliaryData(materialPreviewModelProperty, modelStr); + + rootModelNode.view()->emitCustomNotification("refresh_material_browser", {}); + }; + + QMetaObject::invokeMethod(view, renderPreviews, modelStr); +} + +void QmlMaterialNodeProxy::handleAuxiliaryPropertyChanges() +{ + if (!hasQuick3DImport()) + return; + + m_previewUpdateTimer.start(); +} + +void QmlMaterialNodeProxy::registerDeclarativeType() +{ + qmlRegisterType("HelperWidgets", 2, 0, "QmlMaterialNodeProxy"); +} + +void QmlMaterialNodeProxy::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 QmlMaterialNodeProxy::updatePreviewModel() +{ + if (!hasQuick3DImport()) + return; + + AbstractView *view = m_materialNode.modelNode().view(); + ModelNode rootModelNode = view->rootModelNode(); + + // Read auxiliary preview Data + QString env = rootModelNode.auxiliaryDataWithDefault(materialPreviewEnvDocProperty).toString(); + QString envValue = rootModelNode.auxiliaryDataWithDefault(materialPreviewEnvValueDocProperty) + .toString(); + QString modelStr = rootModelNode.auxiliaryDataWithDefault(materialPreviewModelDocProperty).toString(); + + if (!envValue.isEmpty() && env != "Basic") { + env += '='; + env += envValue; + } + if (env.isEmpty()) + env = "SkyBox=preview_studio"; + + if (modelStr.isEmpty()) + modelStr = "#Sphere"; + + // Set node proxy properties + if (m_previewModel != modelStr) { + m_previewModel = modelStr; + emit previewModelChanged(); + } + + if (m_previewEnv != env) { + m_previewEnv = env; + emit previewEnvChanged(); + } +} + +void QmlMaterialNodeProxy::setMaterialNode(const QmlObjectNode &material) +{ + if (material == m_materialNode) + return; + + m_materialNode = material; + emit materialNodeChanged(); +} + +bool QmlMaterialNodeProxy::hasQuick3DImport() const +{ + return materialNode().isValid() && materialNode().model()->hasImport("QtQuick3D"_L1); +} + +AbstractView *QmlMaterialNodeProxy::materialView() const +{ + return materialNode().view(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.h new file mode 100644 index 00000000000..58bf8935970 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmaterialnodeproxy.h @@ -0,0 +1,89 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include +#include + +namespace QmlDesigner { + +class ModelNode; + +class QmlMaterialNodeProxy : public QObject +{ + Q_OBJECT + + Q_PROPERTY(ModelNode materialNode READ materialNode NOTIFY materialNodeChanged) + Q_PROPERTY(QStringList possibleTypes READ possibleTypes NOTIFY possibleTypesChanged) + Q_PROPERTY(int possibleTypeIndex READ possibleTypeIndex NOTIFY possibleTypeIndexChanged) + Q_PROPERTY(QString previewEnv MEMBER m_previewEnv WRITE setPreviewEnv NOTIFY previewEnvChanged) + Q_PROPERTY(QString previewModel MEMBER m_previewModel WRITE setPreviewModel NOTIFY previewModelChanged) + +public: + enum class ToolBarAction { + ApplyToSelected = 0, + ApplyToSelectedAdd, + AddNewMaterial, + DeleteCurrentMaterial, + OpenMaterialBrowser + }; + Q_ENUM(ToolBarAction) + + explicit QmlMaterialNodeProxy(); + ~QmlMaterialNodeProxy() override; + + void setup(const QmlObjectNode &objectNode); + + QStringList possibleTypes() const { return m_possibleTypes; } + + ModelNode materialNode() const; + + int possibleTypeIndex() const { return m_possibleTypeIndex; } + + void setCurrentType(const QString &type); + + Q_INVOKABLE void toolBarAction(int action); + + void setPreviewEnv(const QString &envAndValue); + void setPreviewModel(const QString &modelStr); + + void handleAuxiliaryPropertyChanges(); + + static void registerDeclarativeType(); + +signals: + void possibleTypesChanged(); + void possibleTypeIndexChanged(); + void materialNodeChanged(); + void previewEnvChanged(); + void previewModelChanged(); + +private: // Methods + void setPossibleTypes(const QStringList &types); + void updatePossibleTypes(); + void updatePossibleTypeIndex(); + void updatePreviewModel(); + void setMaterialNode(const QmlObjectNode &material); + + bool hasQuick3DImport() const; + + AbstractView *materialView() const; + +private: + bool m_has3DModelSelection = false; + + QmlObjectNode m_materialNode; + + QStringList m_possibleTypes; + int m_possibleTypeIndex = -1; + QString m_currentType; + + QString m_previewEnv; + QString m_previewModel; + QTimer m_previewUpdateTimer; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index c5b4039540f..21f9dd47433 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -26,6 +26,7 @@ #include "propertymodel.h" #include "propertynamevalidator.h" #include "qmlanchorbindingproxy.h" +#include "qmlmaterialnodeproxy.h" #include "richtexteditor/richtexteditorproxy.h" #include "selectiondynamicpropertiesproxymodel.h" #include "theme.h" @@ -65,6 +66,7 @@ void Quick2PropertyEditorView::registerQmlTypes() ListValidator::registerDeclarativeType(); ColorPaletteBackend::registerDeclarativeType(); QmlAnchorBindingProxy::registerDeclarativeType(); + QmlMaterialNodeProxy::registerDeclarativeType(); BindingEditor::registerDeclarativeType(); ActionEditor::registerDeclarativeType(); AnnotationEditor::registerDeclarativeType();