QmlDesigner: Add backend for textures in PropertyEditor

Task-number: QDS-14805
Change-Id: I71a5935af3c2cddbeb87f496d2e15464244639c2
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2025-02-27 17:42:51 +02:00
parent 0e32bb4bea
commit 18780dcbbd
6 changed files with 245 additions and 0 deletions

View File

@@ -424,6 +424,7 @@ extend_qtc_plugin(QmlDesigner
qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h
qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h
qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h
qmltexturenodeproxy.cpp qmltexturenodeproxy.h
quick2propertyeditorview.cpp quick2propertyeditorview.h quick2propertyeditorview.cpp quick2propertyeditorview.h
propertyeditorutils.cpp propertyeditorutils.h propertyeditorutils.cpp propertyeditorutils.h
) )

View File

@@ -339,6 +339,7 @@ void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(cons
void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property) void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property)
{ {
m_backendModelNode.handleBindingPropertyChanged(property); m_backendModelNode.handleBindingPropertyChanged(property);
m_backendTextureNode.handleBindingPropertyChanged(property);
updateInstanceImage(); updateInstanceImage();
} }
@@ -350,11 +351,13 @@ void PropertyEditorQmlBackend::handleBindingPropertyInModelNodeProxyAboutToChang
if (expressionNode.metaInfo().isQtQuick3DTexture()) if (expressionNode.metaInfo().isQtQuick3DTexture())
updateInstanceImage(); updateInstanceImage();
} }
m_backendTextureNode.handleBindingPropertyChanged(property);
} }
void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property) void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property)
{ {
m_backendModelNode.handlePropertiesRemoved(property); m_backendModelNode.handlePropertiesRemoved(property);
m_backendTextureNode.handlePropertiesRemoved(property);
updateInstanceImage(); updateInstanceImage();
} }
@@ -559,6 +562,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
context()->setContextProperty("modelNodeBackend", &m_backendModelNode); context()->setContextProperty("modelNodeBackend", &m_backendModelNode);
m_backendMaterialNode.setup(qmlObjectNode); m_backendMaterialNode.setup(qmlObjectNode);
m_backendTextureNode.setup(qmlObjectNode);
// className // className
auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject( auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject(
@@ -995,6 +999,7 @@ void PropertyEditorQmlBackend::setupContextProperties()
context()->setContextProperties({ context()->setContextProperties({
{"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)}, {"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)},
{"materialNodeBackend", QVariant::fromValue(&m_backendMaterialNode)}, {"materialNodeBackend", QVariant::fromValue(&m_backendMaterialNode)},
{"textureNodeBackend", QVariant::fromValue(&m_backendTextureNode)},
{"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())},
@@ -1051,6 +1056,7 @@ bool PropertyEditorQmlBackend::checkIfUrlExists(const QUrl &url)
void PropertyEditorQmlBackend::emitSelectionToBeChanged() void PropertyEditorQmlBackend::emitSelectionToBeChanged()
{ {
m_backendModelNode.emitSelectionToBeChanged(); m_backendModelNode.emitSelectionToBeChanged();
m_backendTextureNode.updateSelectionDetails();
} }
void PropertyEditorQmlBackend::emitSelectionChanged() void PropertyEditorQmlBackend::emitSelectionChanged()

View File

@@ -9,6 +9,7 @@
#include "qmlanchorbindingproxy.h" #include "qmlanchorbindingproxy.h"
#include "qmlmaterialnodeproxy.h" #include "qmlmaterialnodeproxy.h"
#include "qmlmodelnodeproxy.h" #include "qmlmodelnodeproxy.h"
#include "qmltexturenodeproxy.h"
#include "quick2propertyeditorview.h" #include "quick2propertyeditorview.h"
#include <utils/uniqueobjectptr.h> #include <utils/uniqueobjectptr.h>
@@ -129,6 +130,7 @@ private:
Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr; Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding; QmlAnchorBindingProxy m_backendAnchorBinding;
QmlMaterialNodeProxy m_backendMaterialNode; QmlMaterialNodeProxy m_backendMaterialNode;
QmlTextureNodeProxy m_backendTextureNode;
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;

View File

@@ -0,0 +1,170 @@
// 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 "qmltexturenodeproxy.h"
#include <abstractview.h>
#include <createtexture.h>
#include <designmodewidget.h>
#include <model.h>
#include <qmldesignerplugin.h>
#include <qqml.h>
namespace QmlDesigner {
using namespace Qt::StringLiterals;
QmlTextureNodeProxy::QmlTextureNodeProxy() = default;
QmlTextureNodeProxy::~QmlTextureNodeProxy() = default;
void QmlTextureNodeProxy::setup(const QmlObjectNode &objectNode)
{
const QmlObjectNode texture = objectNode.metaInfo().isQtQuick3DTexture() ? objectNode
: QmlObjectNode{};
setTextureNode(texture);
updateSelectionDetails();
}
void QmlTextureNodeProxy::updateSelectionDetails()
{
QScopeGuard falseSetter{
std::bind_front(&QmlTextureNodeProxy::setSelectedNodeAcceptsMaterial, this, false)};
if (!textureNode())
return;
QmlObjectNode selectedNode = textureNode().view()->singleSelectedModelNode();
if (!selectedNode)
return;
falseSetter.dismiss();
setSelectedNodeAcceptsMaterial(selectedNode.hasBindingProperty("materials"));
}
void QmlTextureNodeProxy::handlePropertyChanged(const AbstractProperty &property)
{
if (!textureNode())
return;
QmlObjectNode node = property.parentModelNode();
if (!node)
return;
QmlObjectNode selectedNode = textureNode().view()->singleSelectedModelNode();
if (!selectedNode)
return;
if (property.name() == "materials"_L1
&& (selectedNode == node || selectedNode.propertyChangeForCurrentState() == node)) {
updateSelectionDetails();
}
}
void QmlTextureNodeProxy::handleBindingPropertyChanged(const BindingProperty &property)
{
handlePropertyChanged(property);
}
void QmlTextureNodeProxy::handlePropertiesRemoved(const AbstractProperty &property)
{
handlePropertyChanged(property);
}
QmlObjectNode QmlTextureNodeProxy::textureNode() const
{
return m_textureNode;
}
bool QmlTextureNodeProxy::hasTexture() const
{
return textureNode().isValid();
}
bool QmlTextureNodeProxy::selectedNodeAcceptsMaterial() const
{
return m_selectedNodeAcceptsMaterial;
}
QString QmlTextureNodeProxy::resolveResourcePath(const QString &path) const
{
if (Utils::FilePath::fromString(path).isAbsolutePath())
return path;
return QmlDesignerPlugin::instance()
->documentManager()
.currentDesignDocument()
->fileName()
.absolutePath()
.pathAppended(path)
.cleanPath()
.toUrlishString();
}
void QmlTextureNodeProxy::toolbarAction(int action)
{
if (!hasQuick3DImport())
return;
switch (action) {
case ToolBarAction::ApplyToSelected: {
if (!textureNode())
return;
AbstractView *view = textureNode().view();
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
ModelNode targetNode = view->singleSelectedModelNode();
view->emitCustomNotification("apply_texture_to_model3D", {targetNode, textureNode()});
} break;
case ToolBarAction::AddNewTexture: {
if (!textureNode())
break;
ModelNode newTexture = CreateTexture(textureNode().view()).execute();
QTimer::singleShot(0, this, [newTexture]() {
if (newTexture)
newTexture.model()->setSelectedModelNodes({newTexture});
});
} break;
case ToolBarAction::DeleteCurrentTexture: {
if (textureNode()) {
AbstractView *view = textureNode().view();
view->executeInTransaction(__FUNCTION__, [&] { textureNode().destroy(); });
}
} break;
case ToolBarAction::OpenMaterialBrowser: {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser", true);
} break;
}
}
void QmlTextureNodeProxy::registerDeclarativeType()
{
qmlRegisterType<QmlTextureNodeProxy>("HelperWidgets", 2, 0, "QmlTextureNodeProxy");
}
void QmlTextureNodeProxy::setTextureNode(const QmlObjectNode &node)
{
if (m_textureNode == node)
return;
m_textureNode = node;
emit textureNodeChanged();
}
void QmlTextureNodeProxy::setSelectedNodeAcceptsMaterial(bool value)
{
if (m_selectedNodeAcceptsMaterial == value)
return;
m_selectedNodeAcceptsMaterial = value;
emit selectedNodeAcceptsMaterialChanged();
}
bool QmlTextureNodeProxy::hasQuick3DImport() const
{
return textureNode().isValid() && textureNode().model()->hasImport("QtQuick3D"_L1);
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,64 @@
// 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>
namespace QmlDesigner {
class QmlTextureNodeProxy : public QObject
{
Q_OBJECT
Q_PROPERTY(QmlObjectNode textureNode READ textureNode NOTIFY textureNodeChanged)
Q_PROPERTY(bool hasTexture READ hasTexture NOTIFY textureNodeChanged)
Q_PROPERTY(
bool selectedNodeAcceptsMaterial
READ selectedNodeAcceptsMaterial
NOTIFY selectedNodeAcceptsMaterialChanged)
public:
enum ToolBarAction {
ApplyToSelected,
AddNewTexture,
DeleteCurrentTexture,
OpenMaterialBrowser
};
Q_ENUM(ToolBarAction)
explicit QmlTextureNodeProxy();
~QmlTextureNodeProxy() override;
void setup(const QmlObjectNode &objectNode);
void updateSelectionDetails();
void handlePropertyChanged(const AbstractProperty &property);
void handleBindingPropertyChanged(const BindingProperty &property);
void handlePropertiesRemoved(const AbstractProperty &property);
QmlObjectNode textureNode() const;
bool hasTexture() const;
bool selectedNodeAcceptsMaterial() const;
Q_INVOKABLE QString resolveResourcePath(const QString &path) const;
Q_INVOKABLE void toolbarAction(int action);
static void registerDeclarativeType();
signals:
void textureNodeChanged();
void selectedNodeAcceptsMaterialChanged();
private:
void setTextureNode(const QmlObjectNode &node);
void setSelectedNodeAcceptsMaterial(bool value);
bool hasQuick3DImport() const;
QmlObjectNode m_textureNode;
bool m_selectedNodeAcceptsMaterial = false;
};
} // namespace QmlDesigner

View File

@@ -27,6 +27,7 @@
#include "propertynamevalidator.h" #include "propertynamevalidator.h"
#include "qmlanchorbindingproxy.h" #include "qmlanchorbindingproxy.h"
#include "qmlmaterialnodeproxy.h" #include "qmlmaterialnodeproxy.h"
#include "qmltexturenodeproxy.h"
#include "richtexteditor/richtexteditorproxy.h" #include "richtexteditor/richtexteditorproxy.h"
#include "selectiondynamicpropertiesproxymodel.h" #include "selectiondynamicpropertiesproxymodel.h"
#include "theme.h" #include "theme.h"
@@ -67,6 +68,7 @@ void Quick2PropertyEditorView::registerQmlTypes()
ColorPaletteBackend::registerDeclarativeType(); ColorPaletteBackend::registerDeclarativeType();
QmlAnchorBindingProxy::registerDeclarativeType(); QmlAnchorBindingProxy::registerDeclarativeType();
QmlMaterialNodeProxy::registerDeclarativeType(); QmlMaterialNodeProxy::registerDeclarativeType();
QmlTextureNodeProxy::registerDeclarativeType();
BindingEditor::registerDeclarativeType(); BindingEditor::registerDeclarativeType();
ActionEditor::registerDeclarativeType(); ActionEditor::registerDeclarativeType();
AnnotationEditor::registerDeclarativeType(); AnnotationEditor::registerDeclarativeType();