diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/ChooseMaterialProperty.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/ChooseMaterialProperty.qml new file mode 100644 index 00000000000..d9135d1d39d --- /dev/null +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/ChooseMaterialProperty.qml @@ -0,0 +1,153 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + color: StudioTheme.Values.themePanelBackground + + Column { + id: col + padding: 5 + spacing: 5 + + Row { + spacing: 5 + + Column { + spacing: 5 + + Text { + text: qsTr("Select material:") + font.bold: true + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + } + + ListView { + id: materialsListView + + width: root.width * .5 - 5 + height: root.height - 60 + focus: true + clip: true + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: StudioControls.ScrollBar { + visible: materialsListView.height < materialsListView.contentHeight + } + model: materialsModel + delegate: Rectangle { + width: materialsListView.width + height: 20 + color: ListView.isCurrentItem ? StudioTheme.Values.themeTextSelectionColor + : "transparent" + + function id() { + return modelData.match(/\((.*)\)/).pop() + } + + Text { + text: modelData + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + leftPadding: 5 + } + + MouseArea { + anchors.fill: parent + + onClicked: { + materialsListView.currentIndex = index + rootView.updatePropsModel(id()) + } + } + } + } + } + + Column { + spacing: 5 + Text { + text: qsTr("Select property:") + font.bold: true + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + } + + ListView { + id: propertiesListView + + width: root.width * .5 - 5 + height: root.height - 60 + focus: true + clip: true + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: StudioControls.ScrollBar { + visible: propertiesListView.height < propertiesListView.contentHeight + } + model: propertiesModel + delegate: Rectangle { + width: propertiesListView.width + height: 20 + color: ListView.isCurrentItem ? StudioTheme.Values.themeTextSelectionColor + : "transparent" + + function propName() { + return modelData + } + + Text { + text: modelData + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + leftPadding: 5 + } + + MouseArea { + anchors.fill: parent + + onClicked: { + propertiesListView.currentIndex = index + } + } + } + } + } + } + + Row { + spacing: 5 + anchors.right: parent.right + anchors.rightMargin: 10 + + Button { + text: qsTr("Cancel") + + onClicked: { + rootView.closeChooseMatPropsView() + } + } + + Button { + text: qsTr("Apply") + + onClicked: { + let matId = materialsListView.currentItem.id() + let prop = propertiesListView.currentItem.propName() + + rootView.applyTextureToMaterial(matId, prop) + } + } + } + } +} diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 6098e49fbcf..2eedb474d98 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -2,33 +2,48 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "edit3dview.h" + #include "backgroundcolorselection.h" +#include "bindingproperty.h" +#include "designersettings.h" +#include "designmodecontext.h" #include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dviewconfig.h" #include "edit3dwidget.h" +#include "materialbrowserwidget.h" #include "metainfo.h" #include "nodehints.h" +#include "nodeinstanceview.h" +#include "qmldesignerconstants.h" +#include "qmldesignericons.h" +#include "qmldesignerplugin.h" #include "seekerslider.h" +#include "variantproperty.h" #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include +#include +#include +#include #include namespace QmlDesigner { +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies) : AbstractView{externalDependencies} { @@ -261,6 +276,24 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, resetPuppet(); } +bool Edit3DView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) { + if (obj == m_chooseMatPropsView) + m_chooseMatPropsView->close(); + } + } else if (event->type() == QEvent::Close) { + if (obj == m_chooseMatPropsView) { + m_droppedModelNode = {}; + m_chooseMatPropsView->deleteLater(); + } + } + + return AbstractView::eventFilter(obj, event); +} + /** * @brief Get node at position from puppet process * @@ -278,15 +311,64 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos setSelectedModelNode(modelNode); m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode, pos3d); } else if (m_nodeAtPosReqType == NodeAtPosReqType::MaterialDrop) { - const bool isModel = modelNode.metaInfo().isQtQuick3DModel(); - if (m_droppedMaterial.isValid() && modelNode.isValid() && isModel) { + bool isModel = modelNode.metaInfo().isQtQuick3DModel(); + if (m_droppedModelNode.isValid() && modelNode.isValid() && isModel) { executeInTransaction(__FUNCTION__, [&] { - assignMaterialTo3dModel(modelNode, m_droppedMaterial); + assignMaterialTo3dModel(modelNode, m_droppedModelNode); }); } } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) { emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView + } else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) { + if (m_droppedModelNode.isValid() && modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel()) { + // get model's material list + BindingProperty matsProp = modelNode.bindingProperty("materials"); + QList materials; + if (hasId(matsProp.expression())) + materials.append(modelNodeForId(matsProp.expression())); + else + materials = matsProp.resolveToModelNodeList(); + + if (materials.size() > 0) { + m_textureModels.clear(); + QStringList materialsModel; + for (const ModelNode &mat : std::as_const(materials)) { + QString matName = mat.variantProperty("objectName").value().toString(); + materialsModel.append(QLatin1String("%1 (%2)").arg(matName, mat.id())); + QList texProps; + for (const PropertyMetaInfo &p : mat.metaInfo().properties()) { + if (p.propertyType().isQtQuick3DTexture()) + texProps.append(p.name()); + } + m_textureModels.insert(mat.id(), texProps); + } + + QString path = MaterialBrowserWidget::qmlSourcesPath() + "/ChooseMaterialProperty.qml"; + + m_chooseMatPropsView = new QQuickView; + m_chooseMatPropsView->setTitle(tr("Select a material property")); + m_chooseMatPropsView->setResizeMode(QQuickView::SizeRootObjectToView); + m_chooseMatPropsView->setMinimumSize({150, 100}); + m_chooseMatPropsView->setMaximumSize({600, 400}); + m_chooseMatPropsView->setWidth(450); + m_chooseMatPropsView->setHeight(300); + m_chooseMatPropsView->setFlags(Qt::Widget); + m_chooseMatPropsView->setModality(Qt::ApplicationModal); + m_chooseMatPropsView->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_chooseMatPropsView->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"materialsModel", QVariant::fromValue(materialsModel)}, + {"propertiesModel", QVariant::fromValue(m_textureModels.value(materials.at(0).id()))}, + }); + m_chooseMatPropsView->setSource(QUrl::fromLocalFile(path)); + m_chooseMatPropsView->installEventFilter(this); + m_chooseMatPropsView->show(); + } + } } + + if (m_nodeAtPosReqType != NodeAtPosReqType::TextureDrop) + m_droppedModelNode = {}; m_nodeAtPosReqType = NodeAtPosReqType::None; } @@ -842,7 +924,7 @@ void Edit3DView::startContextMenu(const QPoint &pos) void Edit3DView::dropMaterial(const ModelNode &matNode, const QPointF &pos) { m_nodeAtPosReqType = NodeAtPosReqType::MaterialDrop; - m_droppedMaterial = matNode; + m_droppedModelNode = matNode; emitView3DAction(View3DActionType::GetNodeAtPos, pos); } @@ -852,4 +934,37 @@ void Edit3DView::dropBundleMaterial(const QPointF &pos) emitView3DAction(View3DActionType::GetNodeAtPos, pos); } +void Edit3DView::dropTexture(const ModelNode &textureNode, const QPointF &pos) +{ + m_nodeAtPosReqType = NodeAtPosReqType::TextureDrop; + m_droppedModelNode = textureNode; + emitView3DAction(View3DActionType::GetNodeAtPos, pos); +} + +void Edit3DView::updatePropsModel(const QString &matId) +{ + m_chooseMatPropsView->rootContext()->setContextProperty("propertiesModel", + QVariant::fromValue(m_textureModels.value(matId))); +} + +void Edit3DView::applyTextureToMaterial(const QString &matId, const QString &propName) +{ + QTC_ASSERT(m_droppedModelNode.isValid(), return); + + ModelNode mat = modelNodeForId(matId); + QTC_ASSERT(mat.isValid(), return); + + BindingProperty texProp = mat.bindingProperty(propName.toLatin1()); + QTC_ASSERT(texProp.isValid(), return); + + texProp.setExpression(m_droppedModelNode.id()); + + closeChooseMatPropsView(); +} + +void Edit3DView::closeChooseMatPropsView() +{ + m_chooseMatPropsView->close(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index eaa881d5b1e..be510e45660 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -8,14 +8,16 @@ #include #include +#include #include #include #include #include QT_BEGIN_NAMESPACE -class QInputEvent; class QAction; +class QInputEvent; +class QQuickView; QT_END_NAMESPACE namespace QmlDesigner { @@ -62,6 +64,14 @@ public: void startContextMenu(const QPoint &pos); void dropMaterial(const ModelNode &matNode, const QPointF &pos); void dropBundleMaterial(const QPointF &pos); + void dropTexture(const ModelNode &textureNode, const QPointF &pos); + + Q_INVOKABLE void updatePropsModel(const QString &matId); + Q_INVOKABLE void applyTextureToMaterial(const QString &matId, const QString &propName); + Q_INVOKABLE void closeChooseMatPropsView(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; private slots: void onEntriesChanged(); @@ -70,6 +80,7 @@ private: enum class NodeAtPosReqType { BundleMaterialDrop, MaterialDrop, + TextureDrop, ContextMenu, None }; @@ -112,10 +123,12 @@ private: SeekerSlider *m_seeker = nullptr; int particlemode; ModelCache m_canvasCache; - ModelNode m_droppedMaterial; + ModelNode m_droppedModelNode; NodeAtPosReqType m_nodeAtPosReqType; QPoint m_contextMenuPos; QTimer m_compressionTimer; + QPointer m_chooseMatPropsView; + QHash> m_textureModels; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index e2da1c48135..8261301e1ee 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -427,7 +427,8 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) ->viewManager().designerActionManager(); if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL) - || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) { + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { dragEnterEvent->acceptProposedAction(); } } @@ -436,15 +437,23 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) { const QPointF pos = m_canvas->mapFrom(this, dropEvent->position()); - // handle dropping materials - if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL)) { - QByteArray data = dropEvent->mimeData()->data(Constants::MIME_TYPE_MATERIAL); + // handle dropping materials and textures + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL) + || dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { + bool isMaterial = dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL); + QByteArray data = dropEvent->mimeData()->data(isMaterial + ? QString::fromLatin1(Constants::MIME_TYPE_MATERIAL) + : QString::fromLatin1(Constants::MIME_TYPE_TEXTURE)); QDataStream stream(data); qint32 internalId; stream >> internalId; - if (ModelNode matNode = m_view->modelNodeForInternalId(internalId)) - m_view->dropMaterial(matNode, pos); + if (ModelNode dropNode = m_view->modelNodeForInternalId(internalId)) { + if (isMaterial) + m_view->dropMaterial(dropNode, pos); + else + m_view->dropTexture(dropNode, pos); + } return; } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 75aad7bd7af..0cac19c4988 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -134,7 +134,7 @@ bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event) QByteArray data; QMimeData *mimeData = new QMimeData; QDataStream stream(&data, QIODevice::WriteOnly); - stream << m_materialToDrag.internalId(); + stream << (isMaterial ? m_materialToDrag.internalId() : m_textureToDrag.internalId()); mimeData->setData(isMaterial ? QString::fromLatin1(Constants::MIME_TYPE_MATERIAL) : QString::fromLatin1(Constants::MIME_TYPE_TEXTURE), data); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index b1594f80f93..97d93bb7b8f 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -1149,7 +1149,7 @@ bool MaterialEditorView::eventFilter(QObject *obj, QEvent *event) if (m_qmlBackEnd && m_qmlBackEnd->widget() == obj) QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), "closeContextMenu"); } - return QObject::eventFilter(obj, event); + return AbstractView::eventFilter(obj, event); } void MaterialEditorView::reloadQml()