forked from qt-creator/qt-creator
QmlDesigner: Add backend for materials in PropertyEditor
Task-number: QDS-14784 Change-Id: I531124231545971c5ceb171da8685efeb3eaf0fb Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
@@ -422,6 +422,7 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
propertynamevalidator.cpp propertynamevalidator.h
|
propertynamevalidator.cpp propertynamevalidator.h
|
||||||
tooltip.cpp tooltip.h
|
tooltip.cpp tooltip.h
|
||||||
qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h
|
qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h
|
||||||
|
qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h
|
||||||
qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h
|
qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h
|
||||||
quick2propertyeditorview.cpp quick2propertyeditorview.h
|
quick2propertyeditorview.cpp quick2propertyeditorview.h
|
||||||
propertyeditorutils.cpp propertyeditorutils.h
|
propertyeditorutils.cpp propertyeditorutils.h
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include <coreplugin/messagebox.h>
|
#include <coreplugin/messagebox.h>
|
||||||
#include <qmljs/qmljssimplereader.h>
|
#include <qmljs/qmljssimplereader.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/array.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/smallstring.h>
|
#include <utils/smallstring.h>
|
||||||
@@ -90,6 +91,19 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
static bool isMaterialAuxiliaryKey(AuxiliaryDataKeyView key)
|
||||||
|
{
|
||||||
|
static constexpr auto previewKeys = Utils::to_array<AuxiliaryDataKeyView>(
|
||||||
|
materialPreviewEnvDocProperty,
|
||||||
|
materialPreviewEnvValueDocProperty,
|
||||||
|
materialPreviewModelDocProperty,
|
||||||
|
materialPreviewEnvProperty,
|
||||||
|
materialPreviewEnvValueProperty,
|
||||||
|
materialPreviewModelProperty);
|
||||||
|
|
||||||
|
return std::ranges::find(previewKeys, key) != std::ranges::end(previewKeys);
|
||||||
|
}
|
||||||
|
|
||||||
PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor,
|
PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor,
|
||||||
AsynchronousImageCache &imageCache)
|
AsynchronousImageCache &imageCache)
|
||||||
: m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(imageCache))
|
: m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(imageCache))
|
||||||
@@ -307,19 +321,41 @@ void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy(
|
|||||||
m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName);
|
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)
|
void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property)
|
||||||
{
|
{
|
||||||
m_backendModelNode.handleVariantPropertyChanged(property);
|
m_backendModelNode.handleVariantPropertyChanged(property);
|
||||||
|
updateInstanceImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property)
|
void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property)
|
||||||
{
|
{
|
||||||
m_backendModelNode.handleBindingPropertyChanged(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)
|
void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property)
|
||||||
{
|
{
|
||||||
m_backendModelNode.handlePropertiesRemoved(property);
|
m_backendModelNode.handlePropertiesRemoved(property);
|
||||||
|
updateInstanceImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorQmlBackend::handleModelNodePreviewPixmapChanged(const ModelNode &node,
|
void PropertyEditorQmlBackend::handleModelNodePreviewPixmapChanged(const ModelNode &node,
|
||||||
@@ -496,10 +532,15 @@ void QmlDesigner::PropertyEditorQmlBackend::createPropertyEditorValues(const Qml
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PropertyEditorQmlBackend::updateInstanceImage()
|
||||||
|
{
|
||||||
|
m_view->instanceImageProvider()->invalidate();
|
||||||
|
refreshPreview();
|
||||||
|
}
|
||||||
|
|
||||||
void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor)
|
void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor)
|
||||||
{
|
{
|
||||||
if (qmlObjectNode.isValid()) {
|
if (qmlObjectNode.isValid()) {
|
||||||
|
|
||||||
m_contextObject->setModel(propertyEditor->model());
|
m_contextObject->setModel(propertyEditor->model());
|
||||||
|
|
||||||
qCInfo(propertyEditorBenchmark) << Q_FUNC_INFO;
|
qCInfo(propertyEditorBenchmark) << Q_FUNC_INFO;
|
||||||
@@ -517,6 +558,8 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
|
|||||||
m_backendModelNode.setup(qmlObjectNode.modelNode());
|
m_backendModelNode.setup(qmlObjectNode.modelNode());
|
||||||
context()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
context()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
||||||
|
|
||||||
|
m_backendMaterialNode.setup(qmlObjectNode);
|
||||||
|
|
||||||
// className
|
// className
|
||||||
auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject(
|
auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject(
|
||||||
m_backendValuesPropertyMap.value(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY)));
|
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"));
|
contextObject()->setHasQuick3DImport(propertyEditor->model()->hasImport("QtQuick3D"));
|
||||||
|
|
||||||
m_view->instanceImageProvider()->setModelNode(propertyEditor->firstSelectedModelNode());
|
m_view->instanceImageProvider()->setModelNode(propertyEditor->firstSelectedModelNode());
|
||||||
|
updateInstanceImage();
|
||||||
|
|
||||||
qCInfo(propertyEditorBenchmark) << "final:" << time.elapsed();
|
qCInfo(propertyEditorBenchmark) << "final:" << time.elapsed();
|
||||||
} else {
|
} else {
|
||||||
@@ -950,6 +994,7 @@ void PropertyEditorQmlBackend::setupContextProperties()
|
|||||||
{
|
{
|
||||||
context()->setContextProperties({
|
context()->setContextProperties({
|
||||||
{"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)},
|
{"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)},
|
||||||
|
{"materialNodeBackend", QVariant::fromValue(&m_backendMaterialNode)},
|
||||||
{"anchorBackend", QVariant::fromValue(&m_backendAnchorBinding)},
|
{"anchorBackend", QVariant::fromValue(&m_backendAnchorBinding)},
|
||||||
{"transaction", QVariant::fromValue(m_propertyEditorTransaction.get())},
|
{"transaction", QVariant::fromValue(m_propertyEditorTransaction.get())},
|
||||||
{"dummyBackendValue", QVariant::fromValue(m_dummyPropertyEditorValue.get())},
|
{"dummyBackendValue", QVariant::fromValue(m_dummyPropertyEditorValue.get())},
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include "propertyeditorcontextobject.h"
|
#include "propertyeditorcontextobject.h"
|
||||||
#include "propertyeditorvalue.h"
|
#include "propertyeditorvalue.h"
|
||||||
#include "qmlanchorbindingproxy.h"
|
#include "qmlanchorbindingproxy.h"
|
||||||
|
#include "qmlmaterialnodeproxy.h"
|
||||||
#include "qmlmodelnodeproxy.h"
|
#include "qmlmodelnodeproxy.h"
|
||||||
#include "quick2propertyeditorview.h"
|
#include "quick2propertyeditorview.h"
|
||||||
|
|
||||||
@@ -84,8 +85,10 @@ public:
|
|||||||
void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode,
|
void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode,
|
||||||
PropertyNameView propertyName);
|
PropertyNameView propertyName);
|
||||||
|
|
||||||
|
void handleAuxiliaryDataChanges(const QmlObjectNode &qmlObjectNode, AuxiliaryDataKeyView key);
|
||||||
void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property);
|
void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property);
|
||||||
void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property);
|
void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property);
|
||||||
|
void handleBindingPropertyInModelNodeProxyAboutToChange(const BindingProperty &property);
|
||||||
void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property);
|
void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property);
|
||||||
void handleModelNodePreviewPixmapChanged(const ModelNode &node,
|
void handleModelNodePreviewPixmapChanged(const ModelNode &node,
|
||||||
const QPixmap &pixmap,
|
const QPixmap &pixmap,
|
||||||
@@ -95,6 +98,7 @@ public:
|
|||||||
|
|
||||||
void refreshBackendModel();
|
void refreshBackendModel();
|
||||||
void refreshPreview();
|
void refreshPreview();
|
||||||
|
void updateInstanceImage();
|
||||||
|
|
||||||
void setupContextProperties();
|
void setupContextProperties();
|
||||||
|
|
||||||
@@ -124,6 +128,7 @@ private:
|
|||||||
|
|
||||||
Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr;
|
Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr;
|
||||||
QmlAnchorBindingProxy m_backendAnchorBinding;
|
QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||||
|
QmlMaterialNodeProxy m_backendMaterialNode;
|
||||||
QmlModelNodeProxy m_backendModelNode;
|
QmlModelNodeProxy m_backendModelNode;
|
||||||
std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction;
|
std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction;
|
||||||
std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue;
|
std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue;
|
||||||
|
@@ -755,6 +755,7 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
|
|||||||
|
|
||||||
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
for (const AbstractProperty &property : propertyList) {
|
for (const AbstractProperty &property : propertyList) {
|
||||||
m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property);
|
m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property);
|
||||||
|
|
||||||
@@ -765,6 +766,7 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
|
|||||||
|
|
||||||
if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
|
if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
|
||||||
m_locked = true;
|
m_locked = true;
|
||||||
|
changed = true;
|
||||||
|
|
||||||
const PropertyName propertyName = property.name().toByteArray();
|
const PropertyName propertyName = property.name().toByteArray();
|
||||||
PropertyName convertedpropertyName = propertyName;
|
PropertyName convertedpropertyName = propertyName;
|
||||||
@@ -814,6 +816,8 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
|
|||||||
m_qmlBackEndForCurrentType->backendAnchorBinding().invalidate(m_selectedNode);
|
m_qmlBackEndForCurrentType->backendAnchorBinding().invalidate(m_selectedNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (changed)
|
||||||
|
m_qmlBackEndForCurrentType->updateInstanceImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags /*propertyChange*/)
|
void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||||
@@ -823,6 +827,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
|
|||||||
|
|
||||||
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
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) {
|
for (const VariantProperty &property : propertyList) {
|
||||||
m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property);
|
m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property);
|
||||||
|
|
||||||
@@ -841,17 +850,38 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
|
|||||||
setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
|
setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
|
||||||
else
|
else
|
||||||
setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
|
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<BindingProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
|
||||||
|
PropertyChangeFlags /*propertyChange*/)
|
||||||
{
|
{
|
||||||
if (locked() || noValidSelection())
|
if (noValidSelection())
|
||||||
return;
|
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) {
|
for (const BindingProperty &property : propertyList) {
|
||||||
m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property);
|
m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property);
|
||||||
|
|
||||||
@@ -868,8 +898,12 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
|
|||||||
QString exp = QmlObjectNode(m_selectedNode).bindingProperty(property.name()).expression();
|
QString exp = QmlObjectNode(m_selectedNode).bindingProperty(property.name()).expression();
|
||||||
m_qmlBackEndForCurrentType->setExpression(property.name(), exp);
|
m_qmlBackEndForCurrentType->setExpression(property.name(), exp);
|
||||||
m_locked = false;
|
m_locked = false;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
m_qmlBackEndForCurrentType->updateInstanceImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorView::auxiliaryDataChanged(const ModelNode &node,
|
void PropertyEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||||
@@ -879,10 +913,21 @@ void PropertyEditorView::auxiliaryDataChanged(const ModelNode &node,
|
|||||||
if (noValidSelection())
|
if (noValidSelection())
|
||||||
return;
|
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())
|
if (!node.isSelected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_qmlBackEndForCurrentType->setValueforAuxiliaryProperties(m_selectedNode, key);
|
m_qmlBackEndForCurrentType->setValueforAuxiliaryProperties(m_selectedNode, key);
|
||||||
|
saved = true;
|
||||||
|
|
||||||
if (key == insightEnabledProperty)
|
if (key == insightEnabledProperty)
|
||||||
m_qmlBackEndForCurrentType->contextObject()->setInsightEnabled(data.toBool());
|
m_qmlBackEndForCurrentType->contextObject()->setInsightEnabled(data.toBool());
|
||||||
@@ -979,7 +1024,7 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
|
|||||||
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
||||||
|
|
||||||
m_locked = true;
|
m_locked = true;
|
||||||
|
bool changed = false;
|
||||||
using ModelNodePropertyPair = QPair<ModelNode, PropertyName>;
|
using ModelNodePropertyPair = QPair<ModelNode, PropertyName>;
|
||||||
for (const ModelNodePropertyPair &propertyPair : propertyList) {
|
for (const ModelNodePropertyPair &propertyPair : propertyList) {
|
||||||
const ModelNode modelNode = propertyPair.first;
|
const ModelNode modelNode = propertyPair.first;
|
||||||
@@ -989,20 +1034,21 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
|
|||||||
m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(modelNode,
|
m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(modelNode,
|
||||||
propertyName);
|
propertyName);
|
||||||
|
|
||||||
if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode
|
if (qmlObjectNode.isValid() && modelNode == m_selectedNode
|
||||||
&& qmlObjectNode.currentState().isValid()) {
|
&& qmlObjectNode.currentState().isValid()) {
|
||||||
const AbstractProperty property = modelNode.property(propertyName);
|
const AbstractProperty property = modelNode.property(propertyName);
|
||||||
if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) {
|
if (!modelNode.hasProperty(propertyName) || property.isBindingProperty())
|
||||||
if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() )
|
|
||||||
setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name()));
|
setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name()));
|
||||||
else
|
else
|
||||||
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
|
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (changed)
|
||||||
|
m_qmlBackEndForCurrentType->updateInstanceImage();
|
||||||
|
|
||||||
m_locked = false;
|
m_locked = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
|
void PropertyEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
|
||||||
|
@@ -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 <auxiliarydataproperties.h>
|
||||||
|
#include <designmodewidget.h>
|
||||||
|
#include <nodeinstanceview.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
#include <utils3d.h>
|
||||||
|
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
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<QmlMaterialNodeProxy>("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
|
@@ -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 <qmlobjectnode.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
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
|
@@ -26,6 +26,7 @@
|
|||||||
#include "propertymodel.h"
|
#include "propertymodel.h"
|
||||||
#include "propertynamevalidator.h"
|
#include "propertynamevalidator.h"
|
||||||
#include "qmlanchorbindingproxy.h"
|
#include "qmlanchorbindingproxy.h"
|
||||||
|
#include "qmlmaterialnodeproxy.h"
|
||||||
#include "richtexteditor/richtexteditorproxy.h"
|
#include "richtexteditor/richtexteditorproxy.h"
|
||||||
#include "selectiondynamicpropertiesproxymodel.h"
|
#include "selectiondynamicpropertiesproxymodel.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
@@ -65,6 +66,7 @@ void Quick2PropertyEditorView::registerQmlTypes()
|
|||||||
ListValidator::registerDeclarativeType();
|
ListValidator::registerDeclarativeType();
|
||||||
ColorPaletteBackend::registerDeclarativeType();
|
ColorPaletteBackend::registerDeclarativeType();
|
||||||
QmlAnchorBindingProxy::registerDeclarativeType();
|
QmlAnchorBindingProxy::registerDeclarativeType();
|
||||||
|
QmlMaterialNodeProxy::registerDeclarativeType();
|
||||||
BindingEditor::registerDeclarativeType();
|
BindingEditor::registerDeclarativeType();
|
||||||
ActionEditor::registerDeclarativeType();
|
ActionEditor::registerDeclarativeType();
|
||||||
AnnotationEditor::registerDeclarativeType();
|
AnnotationEditor::registerDeclarativeType();
|
||||||
|
Reference in New Issue
Block a user