diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index d60c98933b6..8bb52bf4100 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -8,7 +8,7 @@ import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme -Column { +ColumnLayout { id: root property string previewEnv @@ -18,7 +18,7 @@ Column { property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { //This is how you can override stuff from the control styles - controlSize: Qt.size(previewOptions.width, previewOptions.width) + controlSize: Qt.size(optionsToolbar.height, optionsToolbar.height) baseIconFontSize: StudioTheme.Values.bigIconFontSize } @@ -118,16 +118,16 @@ Column { } } - Item { - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - height: previewRect.height + Row { + id: optionsToolbar + Layout.preferredHeight: 40 + Layout.fillWidth: true + + leftPadding: root.__horizontalSpacing StudioControls.AbstractButton { id: pinButton - x: root.__horizontalSpacing - style: root.buttonStyle iconSize: StudioTheme.Values.bigFont buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin @@ -136,56 +136,53 @@ Column { onCheckedChanged: itemPane.headerDocked = pinButton.checked } - Rectangle { - id: previewRect - anchors.horizontalCenter: parent.horizontalCenter - width: 152 - height: 152 - color: "#000000" - - Image { - id: materialPreview - width: 150 - height: 150 - anchors.centerIn: parent - source: "image://materialEditor/preview" - cache: false - smooth: true - } + HelperWidgets.AbstractButton { + style: root.buttonStyle + buttonIcon: StudioTheme.Constants.textures_medium + tooltip: qsTr("Select preview environment.") + onClicked: envMenu.popup() } - Item { - id: previewOptions - width: 40 - height: previewRect.height - anchors.top: previewRect.top - anchors.left: previewRect.right - anchors.leftMargin: root.__horizontalSpacing + HelperWidgets.AbstractButton { + style: root.buttonStyle + buttonIcon: StudioTheme.Constants.cube_medium + tooltip: qsTr("Select preview model.") + onClicked: modelMenu.popup() + } + } - Column { - anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + id: previewRect - HelperWidgets.AbstractButton { - style: root.buttonStyle - buttonIcon: StudioTheme.Constants.textures_medium - tooltip: qsTr("Select preview environment.") - onClicked: envMenu.popup() - } + Layout.fillWidth: true + Layout.minimumWidth: 152 + implicitHeight: materialPreview.height - HelperWidgets.AbstractButton { - style: root.buttonStyle - buttonIcon: StudioTheme.Constants.cube_medium - tooltip: qsTr("Select preview model.") - onClicked: modelMenu.popup() - } - } + clip: true + color: "#000000" + + Image { + id: materialPreview + + width: root.width + height: Math.min(materialPreview.width * 0.75, 400) + anchors.centerIn: parent + + fillMode: Image.PreserveAspectFit + + source: "image://materialEditor/preview" + cache: false + smooth: true + + sourceSize.width: materialPreview.width + sourceSize.height: materialPreview.height } } HelperWidgets.Section { // Section with hidden header is used so properties are aligned with the other sections' properties hideHeader: true - width: parent.width + Layout.fillWidth: true collapsible: false HelperWidgets.SectionLayout { diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 273560ad2b4..8180a707824 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -844,6 +844,7 @@ extend_qtc_plugin(QmlDesigner SOURCES materialeditorcontextobject.cpp materialeditorcontextobject.h materialeditordynamicpropertiesproxymodel.cpp materialeditordynamicpropertiesproxymodel.h + materialeditorimageprovider.cpp materialeditorimageprovider.h materialeditorqmlbackend.cpp materialeditorqmlbackend.h materialeditortransaction.cpp materialeditortransaction.h materialeditorview.cpp materialeditorview.h diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 7d90dffffc6..096820b83df 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -335,7 +335,14 @@ void MaterialBrowserView::selectedNodesChanged(const QList &selectedN void MaterialBrowserView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) { - if (isMaterial(node)) + if (!isMaterial(node)) + return; + + // There might be multiple requests for different preview pixmap sizes. + // Here only the one with the default size is picked. + const double ratio = externalDependencies().formEditorDevicePixelRatio(); + const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; + if (pixmap.width() == dim && pixmap.height() == dim) m_widget->updateMaterialPreview(node, pixmap); } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.cpp new file mode 100644 index 00000000000..d88c4461fdc --- /dev/null +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "materialeditorimageprovider.h" + +#include "materialeditorview.h" + +#include +#include + +namespace QmlDesigner { + +MaterialEditorImageProvider::MaterialEditorImageProvider(MaterialEditorView *materialEditorView) + : QQuickImageProvider(Pixmap) + , m_delayTimer(new QTimer(this)) +{ + m_delayTimer->setInterval(500); + m_delayTimer->setSingleShot(true); + m_delayTimer->callOnTimeout([this] { + if (m_previewPixmap.size() != m_requestedSize) + emit this->requestPreview(m_requestedSize); + }); + + connect(this, + &MaterialEditorImageProvider::requestPreview, + materialEditorView, + &MaterialEditorView::handlePreviewSizeChanged); +} + +void MaterialEditorImageProvider::setPixmap(const QPixmap &pixmap) +{ + m_previewPixmap = pixmap; +} + +QPixmap MaterialEditorImageProvider::requestPixmap(const QString &id, + QSize *size, + const QSize &requestedSize) +{ + static QPixmap defaultPreview = QPixmap::fromImage( + QImage(":/materialeditor/images/defaultmaterialpreview.png")); + + QPixmap pixmap{150, 150}; + + if (id == "preview") { + if (!m_previewPixmap.isNull()) { + pixmap = m_previewPixmap; + setRequestedSize(requestedSize); + } else { + pixmap = defaultPreview.scaled(requestedSize, Qt::KeepAspectRatio); + } + } else { + qWarning() << __FUNCTION__ << "Unsupported image id:" << id; + pixmap.fill(Qt::red); + } + + if (size) + *size = pixmap.size(); + + return pixmap; +} + +/*! + * \internal + * \brief Sets the requested size. If the requested size is not the same as the + * size of the m_previewPixmap, it will ask \l {MaterialEditorView} to provide + * an image with the requested size + * The requests are delayed until the requested size is stable. + */ +void MaterialEditorImageProvider::setRequestedSize(const QSize &requestedSize) +{ + if (!requestedSize.isValid()) + return; + + m_requestedSize = requestedSize; + + if (m_previewPixmap.size() != requestedSize) + m_delayTimer->start(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.h new file mode 100644 index 00000000000..6730f418a2c --- /dev/null +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorimageprovider.h @@ -0,0 +1,37 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class MaterialEditorView; + +class MaterialEditorImageProvider : public QQuickImageProvider +{ + Q_OBJECT + +public: + explicit MaterialEditorImageProvider(MaterialEditorView *materialEditorView); + + void setPixmap(const QPixmap &pixmap); + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; + +signals: + void requestPreview(QSize); + +private: + void setRequestedSize(const QSize &requestedSize); + + QPixmap m_previewPixmap; + QSize m_requestedSize; + QTimer *m_delayTimer = nullptr; // Delays the preview requests +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index 0e508f8f360..4d97de19a88 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -3,9 +3,10 @@ #include "materialeditorqmlbackend.h" -#include "propertyeditorvalue.h" -#include "materialeditortransaction.h" #include "materialeditorcontextobject.h" +#include "materialeditorimageprovider.h" +#include "materialeditortransaction.h" +#include "propertyeditorvalue.h" #include #include @@ -22,7 +23,6 @@ #include #include -#include #include #include #include @@ -39,50 +39,11 @@ static QObject *variantToQObject(const QVariant &value) namespace QmlDesigner { -class MaterialEditorImageProvider : public QQuickImageProvider -{ - QPixmap m_previewPixmap; - -public: - MaterialEditorImageProvider() - : QQuickImageProvider(Pixmap) {} - - void setPixmap(const QPixmap &pixmap) - { - m_previewPixmap = pixmap; - } - - QPixmap requestPixmap(const QString &id, - QSize *size, - [[maybe_unused]] const QSize &requestedSize) override - { - static QPixmap defaultPreview = QPixmap::fromImage(QImage(":/materialeditor/images/defaultmaterialpreview.png")); - - QPixmap pixmap{150, 150}; - - if (id == "preview") { - if (!m_previewPixmap.isNull()) - pixmap = m_previewPixmap; - else - pixmap = defaultPreview; - } else { - qWarning() << __FUNCTION__ << "Unsupported image id:" << id; - pixmap.fill(Qt::red); - } - - - if (size) - *size = pixmap.size(); - - return pixmap; - } -}; - MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) : m_quickWidget(Utils::makeUniqueObjectPtr()) , m_materialEditorTransaction(std::make_unique(materialEditor)) , m_contextObject(std::make_unique(m_quickWidget.get())) - , m_materialEditorImageProvider(new MaterialEditorImageProvider()) + , m_materialEditorImageProvider(new MaterialEditorImageProvider(materialEditor)) { m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 8e7d10d300d..c9e380a54b3 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -525,6 +525,15 @@ void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr) emitCustomNotification("refresh_material_browser", {}); } +void MaterialEditorView::handlePreviewSizeChanged(const QSizeF &size) +{ + if (m_previewSize == size.toSize()) + return; + + m_previewSize = size.toSize(); + requestPreviewRender(); +} + void MaterialEditorView::setupQmlBackend() { #ifdef QDS_USE_PROJECTSTORAGE @@ -851,7 +860,9 @@ void MaterialEditorView::propertiesAboutToBeRemoved(const QListnodeInstanceView() && m_selectedMaterial.isValid()) - model()->nodeInstanceView()->previewImageDataForGenericNode(m_selectedMaterial, {}); + model()->nodeInstanceView()->previewImageDataForGenericNode(m_selectedMaterial, + {}, + m_previewSize); } bool MaterialEditorView::hasWidget() const @@ -937,8 +948,13 @@ void MaterialEditorView::rootNodeTypeChanged(const QString &type, int, int) void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) { - if (node == m_selectedMaterial) - m_qmlBackEnd->updateMaterialPreview(pixmap); + if (node != m_selectedMaterial) + return; + + if (m_previewSize.isValid() && pixmap.size() != m_previewSize) + return; + + m_qmlBackEnd->updateMaterialPreview(pixmap); } void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index 11bea460638..e6a80fc148e 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -8,6 +8,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -83,6 +84,7 @@ public slots: void handleToolBarAction(int action); void handlePreviewEnvChanged(const QString &envAndValue); void handlePreviewModelChanged(const QString &modelStr); + void handlePreviewSizeChanged(const QSizeF &size); protected: void timerEvent(QTimerEvent *event) override; @@ -124,6 +126,7 @@ private: bool m_hasQuick3DImport = false; bool m_hasMaterialRoot = false; bool m_initializingPreviewData = false; + QSize m_previewSize; QPointer m_colorDialog; QPointer m_itemLibraryInfo; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 090beb64717..47f3a590898 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -366,7 +366,12 @@ void NavigatorView::enableWidget() void NavigatorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) { - m_treeModel->updateToolTipPixmap(node, pixmap); + // There might be multiple requests for different preview pixmap sizes. + // Here only the one with the default size is picked. + const double ratio = externalDependencies().formEditorDevicePixelRatio(); + const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; + if (pixmap.width() == dim && pixmap.height() == dim) + m_treeModel->updateToolTipPixmap(node, pixmap); } ModelNode NavigatorView::modelNodeForIndex(const QModelIndex &modelIndex) const diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 80727bf5a9e..2147c8aa88b 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -134,13 +134,16 @@ public: void sendInputEvent(QEvent *e) const; void view3DAction(View3DActionType type, const QVariant &value) override; - void requestModelNodePreviewImage(const ModelNode &node, const ModelNode &renderNode) const; + void requestModelNodePreviewImage(const ModelNode &node, + const ModelNode &renderNode, + const QSize &size = {}) const; void edit3DViewResized(const QSize &size) const; void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override; QVariant previewImageDataForGenericNode(const ModelNode &modelNode, - const ModelNode &renderNode) const; + const ModelNode &renderNode, + const QSize &size = {}) const; QVariant previewImageDataForImageNode(const ModelNode &modelNode) const; void setCrashCallback(std::function crashCallback) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index c033c983316..889dc5ba7e4 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1778,9 +1778,6 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto node = modelNodeForInternalId(container.instanceId()); if (node.isValid()) { const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); - const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; - if (image.height() != dim || image.width() != dim) - image = image.scaled(dim, dim, Qt::KeepAspectRatio); image.setDevicePixelRatio(ratio); updatePreviewImageForNode(node, image); } @@ -1826,13 +1823,15 @@ void NodeInstanceView::view3DAction(View3DActionType type, const QVariant &value } void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node, - const ModelNode &renderNode) const + const ModelNode &renderNode, + const QSize &size) const { if (m_nodeInstanceServer && node.isValid() && hasInstanceForModelNode(node)) { auto instance = instanceForModelNode(node); if (instance.isValid()) { qint32 renderItemId = -1; QString componentPath; + QSize imageSize; if (renderNode.isValid()) { auto renderInstance = instanceForModelNode(renderNode); if (renderInstance.isValid()) @@ -1842,11 +1841,17 @@ void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node, } else if (node.isComponent()) { componentPath = ModelUtils::componentFilePath(node); } - const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); - const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; - m_nodeInstanceServer->requestModelNodePreviewImage( - RequestModelNodePreviewImageCommand(instance.instanceId(), QSize(dim, dim), - componentPath, renderItemId)); + + if (size.isValid()) { + imageSize = size; + } else { + const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); + const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; + imageSize = {dim, dim}; + } + + m_nodeInstanceServer->requestModelNodePreviewImage(RequestModelNodePreviewImageCommand( + instance.instanceId(), imageSize, componentPath, renderItemId)); } } } @@ -1991,7 +1996,8 @@ void NodeInstanceView::endNanotrace() } QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &modelNode, - const ModelNode &renderNode) const + const ModelNode &renderNode, + const QSize &size) const { if (!modelNode.isValid()) return {}; @@ -2006,9 +2012,15 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model } else { imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode)); imageData.id = id; - m_imageDataMap.insert(id, imageData); + + // There might be multiple requests for different preview pixmap sizes. + // Here only the one with the default size is stored. + const double ratio = externalDependencies().formEditorDevicePixelRatio(); + const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; + if (size.width() == dim && size.height() == dim) + m_imageDataMap.insert(id, imageData); } - requestModelNodePreviewImage(modelNode, renderNode); + requestModelNodePreviewImage(modelNode, renderNode, size); return modelNodePreviewImageDataToVariant(imageData); } @@ -2028,7 +2040,6 @@ void NodeInstanceView::updateWatcher(const QString &path) QStringList oldDirs; QStringList newFiles; QStringList newDirs; - QStringList qsbFiles; const QString projPath = m_externalDependencies.currentProjectDirPath();