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
|
||||
tooltip.cpp tooltip.h
|
||||
qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h
|
||||
qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h
|
||||
qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h
|
||||
quick2propertyeditorview.cpp quick2propertyeditorview.h
|
||||
propertyeditorutils.cpp propertyeditorutils.h
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include <coreplugin/messagebox.h>
|
||||
#include <qmljs/qmljssimplereader.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/array.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/smallstring.h>
|
||||
@@ -90,6 +91,19 @@ namespace QmlDesigner {
|
||||
|
||||
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,
|
||||
AsynchronousImageCache &imageCache)
|
||||
: m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(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<PropertyEditorValue *>(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())},
|
||||
|
@@ -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<Quick2PropertyEditorView> m_view = nullptr;
|
||||
QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||
QmlMaterialNodeProxy m_backendMaterialNode;
|
||||
QmlModelNodeProxy m_backendModelNode;
|
||||
std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction;
|
||||
std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue;
|
||||
|
@@ -755,6 +755,7 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &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<AbstractProperty> &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<AbstractProperty> &proper
|
||||
m_qmlBackEndForCurrentType->backendAnchorBinding().invalidate(m_selectedNode);
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
m_qmlBackEndForCurrentType->updateInstanceImage();
|
||||
}
|
||||
|
||||
void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||
@@ -823,6 +827,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
|
||||
|
||||
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<VariantProperty>&
|
||||
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<BindingProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||
void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &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<BindingProperty> &
|
||||
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<QPair<ModelNode, Pr
|
||||
QTC_ASSERT(m_qmlBackEndForCurrentType, return );
|
||||
|
||||
m_locked = true;
|
||||
|
||||
bool changed = false;
|
||||
using ModelNodePropertyPair = QPair<ModelNode, PropertyName>;
|
||||
for (const ModelNodePropertyPair &propertyPair : propertyList) {
|
||||
const ModelNode modelNode = propertyPair.first;
|
||||
@@ -989,20 +1034,21 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
|
||||
m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(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*/)
|
||||
|
@@ -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 "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();
|
||||
|
Reference in New Issue
Block a user