From 18780dcbbdb468568895164573d98df1742e2061 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 27 Feb 2025 17:42:51 +0200 Subject: [PATCH] QmlDesigner: Add backend for textures in PropertyEditor Task-number: QDS-14805 Change-Id: I71a5935af3c2cddbeb87f496d2e15464244639c2 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../propertyeditorqmlbackend.cpp | 6 + .../propertyeditor/propertyeditorqmlbackend.h | 2 + .../propertyeditor/qmltexturenodeproxy.cpp | 170 ++++++++++++++++++ .../propertyeditor/qmltexturenodeproxy.h | 64 +++++++ .../quick2propertyeditorview.cpp | 2 + 6 files changed, 245 insertions(+) create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index baa53432110..8c4150bc8db 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -424,6 +424,7 @@ extend_qtc_plugin(QmlDesigner qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h qmlmaterialnodeproxy.cpp qmlmaterialnodeproxy.h qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h + qmltexturenodeproxy.cpp qmltexturenodeproxy.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 c9297694fd0..f97d9b6dfeb 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -339,6 +339,7 @@ void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(cons void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property) { m_backendModelNode.handleBindingPropertyChanged(property); + m_backendTextureNode.handleBindingPropertyChanged(property); updateInstanceImage(); } @@ -350,11 +351,13 @@ void PropertyEditorQmlBackend::handleBindingPropertyInModelNodeProxyAboutToChang if (expressionNode.metaInfo().isQtQuick3DTexture()) updateInstanceImage(); } + m_backendTextureNode.handleBindingPropertyChanged(property); } void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property) { m_backendModelNode.handlePropertiesRemoved(property); + m_backendTextureNode.handlePropertiesRemoved(property); updateInstanceImage(); } @@ -559,6 +562,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q context()->setContextProperty("modelNodeBackend", &m_backendModelNode); m_backendMaterialNode.setup(qmlObjectNode); + m_backendTextureNode.setup(qmlObjectNode); // className auto valueObject = qobject_cast(variantToQObject( @@ -995,6 +999,7 @@ void PropertyEditorQmlBackend::setupContextProperties() context()->setContextProperties({ {"modelNodeBackend", QVariant::fromValue(&m_backendModelNode)}, {"materialNodeBackend", QVariant::fromValue(&m_backendMaterialNode)}, + {"textureNodeBackend", QVariant::fromValue(&m_backendTextureNode)}, {"anchorBackend", QVariant::fromValue(&m_backendAnchorBinding)}, {"transaction", QVariant::fromValue(m_propertyEditorTransaction.get())}, {"dummyBackendValue", QVariant::fromValue(m_dummyPropertyEditorValue.get())}, @@ -1051,6 +1056,7 @@ bool PropertyEditorQmlBackend::checkIfUrlExists(const QUrl &url) void PropertyEditorQmlBackend::emitSelectionToBeChanged() { m_backendModelNode.emitSelectionToBeChanged(); + m_backendTextureNode.updateSelectionDetails(); } void PropertyEditorQmlBackend::emitSelectionChanged() diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 8e735232870..e58822b076e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -9,6 +9,7 @@ #include "qmlanchorbindingproxy.h" #include "qmlmaterialnodeproxy.h" #include "qmlmodelnodeproxy.h" +#include "qmltexturenodeproxy.h" #include "quick2propertyeditorview.h" #include @@ -129,6 +130,7 @@ private: Utils::UniqueObjectPtr m_view = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlMaterialNodeProxy m_backendMaterialNode; + QmlTextureNodeProxy m_backendTextureNode; QmlModelNodeProxy m_backendModelNode; std::unique_ptr m_propertyEditorTransaction; std::unique_ptr m_dummyPropertyEditorValue; diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.cpp new file mode 100644 index 00000000000..b030d08d4df --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.cpp @@ -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 +#include +#include +#include +#include + +#include + +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("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 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.h new file mode 100644 index 00000000000..eee8f9b5f2f --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmltexturenodeproxy.h @@ -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 + +#include + +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 diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 21f9dd47433..c5cf236bdcc 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -27,6 +27,7 @@ #include "propertynamevalidator.h" #include "qmlanchorbindingproxy.h" #include "qmlmaterialnodeproxy.h" +#include "qmltexturenodeproxy.h" #include "richtexteditor/richtexteditorproxy.h" #include "selectiondynamicpropertiesproxymodel.h" #include "theme.h" @@ -67,6 +68,7 @@ void Quick2PropertyEditorView::registerQmlTypes() ColorPaletteBackend::registerDeclarativeType(); QmlAnchorBindingProxy::registerDeclarativeType(); QmlMaterialNodeProxy::registerDeclarativeType(); + QmlTextureNodeProxy::registerDeclarativeType(); BindingEditor::registerDeclarativeType(); ActionEditor::registerDeclarativeType(); AnnotationEditor::registerDeclarativeType();