From bd51b4fdc2eb080b5883ebecea4a31320aa6ae2d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 3 Jun 2022 14:34:11 +0200 Subject: QmlDesigner: Add tooltips to UrlChooser * Add tooltips with thumbnails to UrlChooser enable preview of image formats and meshes * Add property editor image provider which makes use of the image cache * Add mesh image cache collector in order to create thumbnails for meshes and built-in primitves * Fix typo in explicit image cache image provider * Add return value in time stamp provider if provided file does not exist Change-Id: I2290d2ace87ddd90e9899e343f2ad1ecd2993fdf Reviewed-by: Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/UrlChooser.qml | 150 +++++++++++++++++++-- src/plugins/qmldesigner/CMakeLists.txt | 4 +- .../propertyeditor/propertyeditorimageprovider.cpp | 68 ++++++++++ .../propertyeditor/propertyeditorimageprovider.h | 48 +++++++ .../propertyeditor/propertyeditorqmlbackend.cpp | 13 +- .../propertyeditor/propertyeditorqmlbackend.h | 3 +- .../propertyeditor/propertyeditorview.cpp | 24 ++-- .../components/propertyeditor/propertyeditorview.h | 4 +- .../propertyeditor/quick2propertyeditorview.cpp | 7 +- .../propertyeditor/quick2propertyeditorview.h | 2 +- .../imagecache/explicitimagecacheimageprovider.cpp | 16 ++- .../imagecache/meshimagecachecollector.cpp | 112 +++++++++++++++ .../imagecache/meshimagecachecollector.h | 70 ++++++++++ .../imagecache/smallimagecacheprovider.cpp | 87 ++++++++++++ .../imagecache/smallimagecacheprovider.h | 68 ++++++++++ .../designercore/imagecache/timestampprovider.cpp | 8 +- .../qmldesigner/designercore/include/viewmanager.h | 3 +- .../qmldesigner/designercore/model/viewmanager.cpp | 7 +- src/plugins/qmldesigner/qmldesignercore.cmake | 2 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 3 +- src/plugins/qmldesigner/qmldesignerplugin.qbs | 6 + .../qmldesigner/qmldesignerprojectmanager.cpp | 33 +++-- .../qmldesigner/qmldesignerprojectmanager.h | 1 + 23 files changed, 682 insertions(+), 57 deletions(-) create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h create mode 100644 src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp create mode 100644 src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h create mode 100644 src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp create mode 100644 src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index ee80d95a96..ec246f56f2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -44,6 +44,9 @@ Row { // by QtQuick3D to add built-in primitives to the model. property var defaultItems + // Current item + property string absoluteFilePath: "" + FileResourcesModel { id: fileModel modelNodeBackendProperty: modelNodeBackend @@ -74,16 +77,64 @@ Row { visible: comboBox.hover && toolTip.text !== "" text: root.backendValue.valueToString delay: StudioTheme.Values.toolTipDelay - height: StudioTheme.Values.toolTipHeight + background: Rectangle { color: StudioTheme.Values.themeToolTipBackground border.color: StudioTheme.Values.themeToolTipOutline border.width: StudioTheme.Values.border } - contentItem: Text { - color: StudioTheme.Values.themeToolTipText - text: toolTip.text - verticalAlignment: Text.AlignVCenter + + contentItem: RowLayout { + spacing: 10 + + Item { + visible: thumbnail.status === Image.Ready + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + Image { + id: checker + visible: !root.isMesh(root.absoluteFilePath) + anchors.fill: parent + fillMode: Image.Tile + source: "images/checkers.png" + } + + Image { + id: thumbnail + asynchronous: true + anchors.fill: parent + fillMode: Image.PreserveAspectFit + source: { + if (root.isBuiltInPrimitive(root.absoluteFilePath)) + return "image://qmldesigner_thumbnails/" + + root.absoluteFilePath.substring(1, root.absoluteFilePath.length) + + ".builtin" + + if (fileModel.isLocal(root.absoluteFilePath)) + return "image://qmldesigner_thumbnails/" + root.absoluteFilePath + + return root.absoluteFilePath + } + } + } + + ColumnLayout { + Text { + text: root.fileName(toolTip.text) + color: StudioTheme.Values.themeToolTipText + font: toolTip.font + } + + Text { + Layout.fillWidth: true + text: root.isBuiltInPrimitive(toolTip.text) ? qsTr("Built-in primitive") + : toolTip.text + font: toolTip.font + color: StudioTheme.Values.themeToolTipText + wrapMode: Text.WordWrap + } + } } } @@ -155,16 +206,62 @@ Row { visible: delegateRoot.hovered text: delegateRoot.relativeFilePath delay: StudioTheme.Values.toolTipDelay - height: StudioTheme.Values.toolTipHeight + background: Rectangle { color: StudioTheme.Values.themeToolTipBackground border.color: StudioTheme.Values.themeToolTipOutline border.width: StudioTheme.Values.border } - contentItem: Text { - color: StudioTheme.Values.themeToolTipText - text: itemToolTip.text - verticalAlignment: Text.AlignVCenter + + contentItem: RowLayout { + spacing: 10 + + Item { + visible: delegateThumbnail.status === Image.Ready + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + Image { + id: delegateChecker + visible: !root.isMesh(delegateRoot.absoluteFilePath) + anchors.fill: parent + fillMode: Image.Tile + source: "images/checkers.png" + } + + Image { + id: delegateThumbnail + asynchronous: true + anchors.fill: parent + fillMode: Image.PreserveAspectFit + source: { + if (root.isBuiltInPrimitive(delegateRoot.name)) + return "image://qmldesigner_thumbnails/" + + delegateRoot.name.substring(1, delegateRoot.name.length) + + ".builtin" + + return "image://qmldesigner_thumbnails/" + delegateRoot.absoluteFilePath + } + } + } + + ColumnLayout { + Text { + text: delegateRoot.name + color: StudioTheme.Values.themeToolTipText + font: delegateToolTip.font + } + + Text { + Layout.fillWidth: true + text: root.isBuiltInPrimitive(delegateToolTip.text) + ? qsTr("Built-in primitive") + : delegateToolTip.text + font: delegateToolTip.font + color: StudioTheme.Values.themeToolTipText + wrapMode: Text.WordWrap + } + } } } } @@ -235,6 +332,10 @@ Row { inputValue = comboBox.items.get(index).model.relativeFilePath root.backendValue.value = inputValue + + if (!root.backendValue.isBound) + root.absoluteFilePath = fileModel.resolve(root.backendValue.value) + comboBox.dirty = false } @@ -259,6 +360,9 @@ Row { if (root.backendValue.value !== inputValue) root.backendValue.value = inputValue + if (!root.backendValue.isBound) + root.absoluteFilePath = fileModel.resolve(root.backendValue.value) + comboBox.dirty = false } @@ -275,6 +379,23 @@ Row { } } + function isBuiltInPrimitive(value) { + return value.startsWith('#') + } + + function isMesh(value) { + return root.isBuiltInPrimitive(value) + || root.hasFileExtension(root.fileName(value), "mesh") + } + + function hasFileExtension(fileName, extension) { + return fileName.split('.').pop() === extension + } + + function fileName(filePath) { + return filePath.substr(filePath.lastIndexOf('/') + 1) + } + function createModel() { // Build the combobox model comboBox.listModel.clear() @@ -322,6 +443,9 @@ Row { Component.onCompleted: { root.createModel() comboBox.updateTextValue() + + if (!root.backendValue.isBound) + root.absoluteFilePath = fileModel.resolve(root.backendValue.value) } function indexOf(model, criteria) { @@ -340,7 +464,7 @@ Row { if (comboBox.popup.opened && !root.backendValue.isBound) { var index = root.indexOf(comboBox.items, function(item) { - return item.fullPath === root.backendValue.value + return item.relativeFilePath === root.backendValue.value }) if (index !== -1) { @@ -359,8 +483,10 @@ Row { iconColor: root.textColor onClicked: { fileModel.openFileDialog() - if (fileModel.fileName !== "") + if (fileModel.fileName !== "") { root.backendValue.value = fileModel.fileName + root.absoluteFilePath = fileModel.resolve(root.backendValue.value) + } } } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 1d56e323e9..1ac2f477bf 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -302,6 +302,7 @@ extend_qtc_plugin(QmlDesigner gradientpresetitem.cpp gradientpresetitem.h gradientpresetlistmodel.cpp gradientpresetlistmodel.h propertyeditorcontextobject.cpp propertyeditorcontextobject.h + propertyeditorimageprovider.cpp propertyeditorimageprovider.h propertyeditorqmlbackend.cpp propertyeditorqmlbackend.h propertyeditortransaction.cpp propertyeditortransaction.h propertyeditorvalue.cpp propertyeditorvalue.h @@ -389,7 +390,8 @@ extend_qtc_plugin(QmlDesigner SOURCES explicitimagecacheimageprovider.cpp explicitimagecacheimageprovider.h - + smallimagecacheprovider.cpp + smallimagecacheprovider.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp new file mode 100644 index 0000000000..2037e1509e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "propertyeditorimageprovider.h" +#include "assetslibrarymodel.h" + +#include +#include +#include + +#include +#include + +namespace QmlDesigner { + +QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QString &id, + const QSize &requestedSize) +{ + const QString suffix = "*." + id.split('.').last().toLower(); + + if (suffix == "*.mesh") + return m_smallImageCacheProvider.requestImageResponse(id, requestedSize); + + if (suffix == "*.builtin") + return m_smallImageCacheProvider.requestImageResponse("#" + id.split('.').first(), + requestedSize); + + QImage image; + auto response = std::make_unique(image); + + QMetaObject::invokeMethod( + response.get(), + [response = QPointer(response.get()), image, suffix, id] { + if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) + response->setImage(QImage(Utils::StyleHelper::dpiSpecificImageFile(id))); + else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) + response->setImage(HdrImage{id}.image()); + else + response->abort(); + }, + Qt::QueuedConnection); + + return response.release(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h new file mode 100644 index 0000000000..bb883e4450 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "imagecache/smallimagecacheprovider.h" + +#include + +namespace QmlDesigner { + +class PropertyEditorImageProvider : public QQuickAsyncImageProvider +{ +public: + PropertyEditorImageProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {}) + : m_smallImageCacheProvider(imageCache, defaultImage) + {} + + QQuickImageResponse *requestImageResponse(const QString &id, + const QSize &requestedSize) override; + +private: + SmallImageCacheProvider m_smallImageCacheProvider; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index c73290bf76..d00957fa8c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -95,9 +95,12 @@ static QObject *variantToQObject(const QVariant &value) namespace QmlDesigner { -PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor) : - m_view(new Quick2PropertyEditorView), m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)), m_dummyPropertyEditorValue(new PropertyEditorValue()), - m_contextObject(new PropertyEditorContextObject()) +PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, + AsynchronousImageCache &imageCache) + : m_view(new Quick2PropertyEditorView(imageCache)) + , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)) + , m_dummyPropertyEditorValue(new PropertyEditorValue()) + , m_contextObject(new PropertyEditorContextObject()) { m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance() ->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool()); @@ -115,7 +118,9 @@ PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyE PropertyEditorQmlBackend::~PropertyEditorQmlBackend() = default; -void PropertyEditorQmlBackend::setupPropertyEditorValue(const PropertyName &name, PropertyEditorView *propertyEditor, const QString &type) +void PropertyEditorQmlBackend::setupPropertyEditorValue(const PropertyName &name, + PropertyEditorView *propertyEditor, + const QString &type) { QmlDesigner::PropertyName propertyName(name); propertyName.replace('.', '_'); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 983a917f2f..7abfc550d8 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -50,7 +50,8 @@ class PropertyEditorQmlBackend public: - PropertyEditorQmlBackend(PropertyEditorView *propertyEditor); + PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, + class AsynchronousImageCache &imageCache); ~PropertyEditorQmlBackend(); void setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 7f190b6288..586c4dec05 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -69,16 +69,16 @@ static bool propertyIsAttachedLayoutProperty(const PropertyName &propertyName) return propertyName.contains("Layout."); } -PropertyEditorView::PropertyEditorView(QWidget *parent) : - AbstractView(parent), - m_parent(parent), - m_updateShortcut(nullptr), - m_timerId(0), - m_stackedWidget(new PropertyEditorWidget(parent)), - m_qmlBackEndForCurrentType(nullptr), - m_locked(false), - m_setupCompleted(false), - m_singleShotTimer(new QTimer(this)) +PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache) + : AbstractView() + , m_imageCache(imageCache) + , m_updateShortcut(nullptr) + , m_timerId(0) + , m_stackedWidget(new PropertyEditorWidget()) + , m_qmlBackEndForCurrentType(nullptr) + , m_locked(false) + , m_setupCompleted(false) + , m_singleShotTimer(new QTimer(this)) { m_qmlDir = PropertyEditorQmlBackend::propertyEditorResourcesPath(); @@ -117,7 +117,7 @@ void PropertyEditorView::setupPane(const TypeName &typeName) PropertyEditorQmlBackend *qmlBackend = m_qmlBackendHash.value(qmlFile.toString()); if (!qmlBackend) { - qmlBackend = new PropertyEditorQmlBackend(this); + qmlBackend = new PropertyEditorQmlBackend(this, m_imageCache); qmlBackend->initialSetup(typeName, qmlSpecificsFile, this); qmlBackend->setSource(qmlFile); @@ -484,7 +484,7 @@ void PropertyEditorView::setupQmlBackend() QString currentStateName = currentState().isBaseState() ? currentState().name() : QStringLiteral("invalid state"); if (!currentQmlBackend) { - currentQmlBackend = new PropertyEditorQmlBackend(this); + currentQmlBackend = new PropertyEditorQmlBackend(this, m_imageCache); m_stackedWidget->addWidget(currentQmlBackend->widget()); m_qmlBackendHash.insert(qmlFile.toString(), currentQmlBackend); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index 06e86fd57c..3bbe502050 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -51,7 +51,7 @@ class PropertyEditorView: public AbstractView Q_OBJECT public: - PropertyEditorView(QWidget *parent = nullptr); + PropertyEditorView(class AsynchronousImageCache &imageCache); ~PropertyEditorView() override; bool hasWidget() const override; @@ -119,8 +119,8 @@ private: //functions bool noValidSelection() const; private: //variables + AsynchronousImageCache &m_imageCache; ModelNode m_selectedNode; - QWidget *m_parent; QShortcut *m_updateShortcut; int m_timerId; PropertyEditorWidget* m_stackedWidget; diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 35b74111d1..08358353c1 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -36,6 +36,7 @@ #include "gradientpresetdefaultlistmodel.h" #include "itemfiltermodel.h" #include "propertyeditorcontextobject.h" +#include "propertyeditorimageprovider.h" #include "propertyeditorqmlbackend.h" #include "propertyeditorvalue.h" #include "qmlanchorbindingproxy.h" @@ -45,11 +46,13 @@ namespace QmlDesigner { -Quick2PropertyEditorView::Quick2PropertyEditorView(QWidget *parent) : - QQuickWidget(parent) +Quick2PropertyEditorView::Quick2PropertyEditorView(AsynchronousImageCache &imageCache) + : QQuickWidget() { setResizeMode(QQuickWidget::SizeRootObjectToView); Theme::setupTheme(engine()); + engine()->addImageProvider("qmldesigner_thumbnails", + new PropertyEditorImageProvider(imageCache)); } void Quick2PropertyEditorView::registerQmlTypes() diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.h index 7bfc6f1558..ca92f2a6ef 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.h @@ -35,7 +35,7 @@ class Quick2PropertyEditorView : public QQuickWidget Q_OBJECT public: - explicit Quick2PropertyEditorView(QWidget *parent = nullptr); + explicit Quick2PropertyEditorView(class AsynchronousImageCache &imageCache); static void registerQmlTypes(); }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp index d94221f382..57a96f1c1c 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp @@ -30,12 +30,12 @@ #include #include -namespace QmlDesigner { +namespace { -class ImageRespose : public QQuickImageResponse +class ImageResponse : public QQuickImageResponse { public: - ImageRespose(const QImage &defaultImage) + ImageResponse(const QImage &defaultImage) : m_image(defaultImage) {} @@ -57,14 +57,18 @@ private: QImage m_image; }; +} // namespace + +namespace QmlDesigner { + QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const QString &id, const QSize &) { - auto response = std::make_unique(m_defaultImage); + auto response = std::make_unique<::ImageResponse>(m_defaultImage); m_cache.requestImage( id, - [response = QPointer(response.get())](const QImage &image) { + [response = QPointer<::ImageResponse>(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, [response, image] { @@ -73,7 +77,7 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const }, Qt::QueuedConnection); }, - [response = QPointer(response.get())](ImageCache::AbortReason abortReason) { + [response = QPointer<::ImageResponse>(response.get())](ImageCache::AbortReason abortReason) { QMetaObject::invokeMethod( response, [response, abortReason] { diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp new file mode 100644 index 0000000000..7602ee7a11 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "meshimagecachecollector.h" +#include "imagecacheconnectionmanager.h" + +#include +#include +#include + +#include + +namespace QmlDesigner { + +MeshImageCacheCollector::MeshImageCacheCollector( + ImageCacheConnectionManager &connectionManager, + QSize captureImageMinimumSize, + QSize captureImageMaximumSize, + ImageCacheCollectorNullImageHandling nullImageHandling) + : m_imageCacheCollector(connectionManager, + captureImageMinimumSize, + captureImageMaximumSize, + nullImageHandling) +{} + +MeshImageCacheCollector::~MeshImageCacheCollector() = default; + +void MeshImageCacheCollector::start(Utils::SmallStringView name, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData, + CaptureCallback captureCallback, + AbortCallback abortCallback) +{ + QTemporaryFile file(QDir::tempPath() + "/mesh-XXXXXX.qml"); + if (file.open()) { + QString qtQuickVersion; + QString qtQuick3DVersion; + QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target()->kit()); + if (qtVersion && qtVersion->qtVersion() < QtSupport::QtVersionNumber(6, 0, 0)) { + qtQuickVersion = "2.15"; + qtQuick3DVersion = "1.15"; + } + + QString content{ + R"(import QtQuick %1 + import QtQuick3D %2 + Node { + Model { + source: "%3" + DefaultMaterial { id: defaultMaterial; diffuseColor: "#ff999999" } + materials: [ defaultMaterial ] + } + })"}; + + content = content.arg(qtQuickVersion, qtQuick3DVersion, QString(name)); + + file.write(content.toUtf8()); + file.close(); + } + + Utils::PathString path{file.fileName()}; + + m_imageCacheCollector.start(path, state, auxiliaryData, captureCallback, abortCallback); +} + +std::pair MeshImageCacheCollector::createImage(Utils::SmallStringView, + Utils::SmallStringView, + const ImageCache::AuxiliaryData &) +{ + return {}; +} + +QIcon MeshImageCacheCollector::createIcon(Utils::SmallStringView, + Utils::SmallStringView, + const ImageCache::AuxiliaryData &) +{ + return {}; +} + +void MeshImageCacheCollector::setTarget(ProjectExplorer::Target *target) +{ + m_imageCacheCollector.setTarget(target); +} + +ProjectExplorer::Target *MeshImageCacheCollector::target() const +{ + return m_imageCacheCollector.target(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h new file mode 100644 index 0000000000..c2cc63bfd9 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "imagecachecollectorinterface.h" +#include "imagecachecollector.h" + +namespace ProjectExplorer { +class Target; +} + +namespace QmlDesigner { + +class ImageCacheConnectionManager; + +class MeshImageCacheCollector final : public ImageCacheCollectorInterface +{ +public: + MeshImageCacheCollector(ImageCacheConnectionManager &connectionManager, + QSize captureImageMinimumSize, + QSize captureImageMaximumSize, + ImageCacheCollectorNullImageHandling nullImageHandling = {}); + + ~MeshImageCacheCollector(); + + void start(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData, + CaptureCallback captureCallback, + AbortCallback abortCallback) override; + + std::pair createImage(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData) override; + + QIcon createIcon(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData) override; + + void setTarget(ProjectExplorer::Target *target); + ProjectExplorer::Target *target() const; + +private: + ImageCacheCollector m_imageCacheCollector; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp new file mode 100644 index 0000000000..36bd55c2fe --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "smallimagecacheprovider.h" + +#include + +#include + +namespace QmlDesigner { + +QQuickTextureFactory *ImageResponse::textureFactory() const +{ + return QQuickTextureFactory::textureFactoryForImage(m_image); +} + +void ImageResponse::setImage(const QImage &image) +{ + m_image = image; + + emit finished(); +} + +void ImageResponse::abort() +{ + emit finished(); +} + +QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString &id, const QSize &) +{ + auto response = std::make_unique(m_defaultImage); + + m_cache.requestSmallImage( + id, + [response = QPointer(response.get())](const QImage &image) { + QMetaObject::invokeMethod( + response, + [response, image] { + if (response) + response->setImage(image); + }, + Qt::QueuedConnection); + }, + [response = QPointer(response.get())]( + ImageCache::AbortReason abortReason) { + QMetaObject::invokeMethod( + response, + [response, abortReason] { + switch (abortReason) { + case ImageCache::AbortReason::Failed: + if (response) + response->abort(); + break; + case ImageCache::AbortReason::Abort: + response->cancel(); + break; + } + }, + Qt::QueuedConnection); + }); + + return response.release(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h new file mode 100644 index 0000000000..05674143e6 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class AsynchronousImageCache; + +class ImageResponse : public QQuickImageResponse +{ +public: + ImageResponse(const QImage &defaultImage) + : m_image(defaultImage) + {} + + QQuickTextureFactory *textureFactory() const override; + + void setImage(const QImage &image); + + void abort(); + +private: + QImage m_image; +}; + +class SmallImageCacheProvider : public QQuickAsyncImageProvider +{ +public: + SmallImageCacheProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {}) + : m_cache{imageCache} + , m_defaultImage(defaultImage) + {} + + QQuickImageResponse *requestImageResponse(const QString &id, + const QSize &requestedSize) override; + +private: + AsynchronousImageCache &m_cache; + QImage m_defaultImage; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp index 99573f175f..67ccc7b75c 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp @@ -28,11 +28,17 @@ #include #include +#include + namespace QmlDesigner { Sqlite::TimeStamp TimeStampProvider::timeStamp(Utils::SmallStringView name) const { - return QFileInfo{QString{name}}.lastModified().toSecsSinceEpoch(); + QFileInfo info{QString{name}}; + if (info.exists()) + return info.lastModified().toSecsSinceEpoch(); + + return {std::numeric_limits::max()}; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/viewmanager.h b/src/plugins/qmldesigner/designercore/include/viewmanager.h index dd77c07f45..1be785655a 100644 --- a/src/plugins/qmldesigner/designercore/include/viewmanager.h +++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h @@ -51,7 +51,8 @@ class ViewManagerData; class QMLDESIGNERCORE_EXPORT ViewManager { public: - ViewManager(class AsynchronousImageCache &imageCache); + ViewManager(class AsynchronousImageCache &imageCache, + class AsynchronousImageCache &meshImageCache); ~ViewManager(); void attachRewriterView(); diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index 8e96507cd5..2896c1571d 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -62,8 +62,9 @@ static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData { public: - ViewManagerData(AsynchronousImageCache &imageCache) + ViewManagerData(AsynchronousImageCache &imageCache, AsynchronousImageCache &meshImageCache) : itemLibraryView(imageCache) + , propertyEditorView(meshImageCache) {} InteractiveConnectionManager connectionManager; @@ -94,8 +95,8 @@ static CrumbleBar *crumbleBar() { return QmlDesignerPlugin::instance()->mainWidget()->crumbleBar(); } -ViewManager::ViewManager(AsynchronousImageCache &imageCache) - : d(std::make_unique(imageCache)) +ViewManager::ViewManager(AsynchronousImageCache &imageCache, AsynchronousImageCache &meshImageCache) + : d(std::make_unique(imageCache, meshImageCache)) { d->formEditorView.setGotoErrorCallback([this](int line, int column) { d->textEditorView.gotoCursorPosition(line, column); diff --git a/src/plugins/qmldesigner/qmldesignercore.cmake b/src/plugins/qmldesigner/qmldesignercore.cmake index 83870a36fe..9a2b0b6f3e 100644 --- a/src/plugins/qmldesigner/qmldesignercore.cmake +++ b/src/plugins/qmldesigner/qmldesignercore.cmake @@ -123,6 +123,8 @@ function(extend_with_qmldesigner_core target_name) imagecache/imagecachegeneratorinterface.h imagecache/imagecachestorage.h imagecache/imagecachestorageinterface.h + imagecache/meshimagecachecollector.cpp + imagecache/meshimagecachecollector.h imagecache/synchronousimagecache.cpp imagecache/timestampprovider.cpp imagecache/timestampprovider.h diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index e6f40d14fc..5ab50135eb 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -137,7 +137,8 @@ class QmlDesignerPluginPrivate { public: QmlDesignerProjectManager projectManager; - ViewManager viewManager{projectManager.asynchronousImageCache()}; + ViewManager viewManager{projectManager.asynchronousImageCache(), + projectManager.asynchronousMeshImageCache()}; DocumentManager documentManager; ShortCutManager shortCutManager; SettingsPage settingsPage; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index ed113d4fe5..5266a146a3 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -442,6 +442,10 @@ Project { "imagecache/imagecachegenerator.h", "imagecache/imagecachestorageinterface.h", "imagecache/imagecachestorage.h", + "imagecache/meshimagecachecollector.cpp", + "imagecache/meshimagecachecollector.h", + "imagecache/smallimagecacheprovider.cpp", + "imagecache/smallimagecacheprovider.h", "imagecache/synchronousimagecache.cpp", "imagecache/timestampproviderinterface.h", "imagecache/timestampprovider.h", @@ -737,6 +741,8 @@ Project { "propertyeditor/gradientpresetlistmodel.h", "propertyeditor/propertyeditorcontextobject.cpp", "propertyeditor/propertyeditorcontextobject.h", + "propertyeditor/propertyeditorimageprovider.cpp", + "propertyeditor/propertyeditorimageprovider.h", "propertyeditor/propertyeditortransaction.cpp", "propertyeditor/propertyeditortransaction.h", "propertyeditor/propertyeditorvalue.cpp", diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 43070221bc..90e54ba65b 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -51,7 +51,8 @@ #include #include #include -#include +#include +#include #include @@ -79,7 +80,7 @@ QString defaultImagePath() return qobject_cast<::QmlProjectManager::QmlBuildSystem *>(target->buildSystem()); } -class TimeStampProvider : public TimeStampProviderInterface +class PreviewTimeStampProvider : public TimeStampProviderInterface { public: Sqlite::TimeStamp timeStamp(Utils::SmallStringView) const override @@ -102,15 +103,18 @@ class QmlDesignerProjectManager::ImageCacheData { public: Sqlite::Database database{Utils::PathString{ - Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, + Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, Sqlite::JournalMode::Wal, Sqlite::LockingMode::Normal}; ImageCacheStorage storage{database}; ImageCacheConnectionManager connectionManager; - ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}}; - ImageCacheGenerator generator{collector, storage}; + MeshImageCacheCollector meshImageCollector{connectionManager, QSize{300, 300}, QSize{600, 600}}; + ImageCacheGenerator meshGenerator{meshImageCollector, storage}; + ImageCacheCollector nodeInstanceCollector{connectionManager, QSize{300, 300}, QSize{600, 600}}; + ImageCacheGenerator nodeInstanceGenerator{nodeInstanceCollector, storage}; TimeStampProvider timeStampProvider; - AsynchronousImageCache asynchronousImageCache{storage, generator, timeStampProvider}; + AsynchronousImageCache asynchronousImageCache{storage, nodeInstanceGenerator, timeStampProvider}; + AsynchronousImageCache asynchronousMeshImageCache{storage, meshGenerator, timeStampProvider}; }; class QmlDesignerProjectManager::PreviewImageCacheData @@ -135,7 +139,7 @@ public: QSize{300, 300}, QSize{1000, 1000}, ImageCacheCollectorNullImageHandling::DontCaptureNullImage}; - TimeStampProvider timeStampProvider; + PreviewTimeStampProvider timeStampProvider; AsynchronousImageFactory factory; ::ProjectExplorer::Target *activeTarget = nullptr; }; @@ -180,6 +184,11 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() return imageCacheData()->asynchronousImageCache; } +AsynchronousImageCache &QmlDesignerProjectManager::asynchronousMeshImageCache() +{ + return imageCacheData()->asynchronousMeshImageCache; +} + void QmlDesignerProjectManager::editorOpened(::Core::IEditor *) {} void QmlDesignerProjectManager::currentEditorChanged(::Core::IEditor *) @@ -218,17 +227,21 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache m_imageCacheData = std::make_unique(); auto setTargetInImageCache = [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { - if (target == imageCacheData->collector.target()) + if (target == imageCacheData->nodeInstanceCollector.target()) return; if (target) imageCacheData->asynchronousImageCache.clean(); - imageCacheData->collector.setTarget(target); + // TODO wrap in function in image cache data + imageCacheData->meshImageCollector.setTarget(target); + imageCacheData->nodeInstanceCollector.setTarget(target); }; if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { - m_imageCacheData->collector.setTarget(project->activeTarget()); + // TODO wrap in function in image cache data + m_imageCacheData->meshImageCollector.setTarget(project->activeTarget()); + m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget()); QObject::connect(project, &ProjectExplorer::Project::activeTargetChanged, this, diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index 6b94fa9e6f..68cd809be3 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -59,6 +59,7 @@ public: void registerPreviewImageProvider(QQmlEngine *engine) const; class AsynchronousImageCache &asynchronousImageCache(); + class AsynchronousImageCache &asynchronousMeshImageCache(); private: void editorOpened(::Core::IEditor *editor); -- cgit v1.2.3