diff --git a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp index c29ec216c83..17f3a65a136 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp +++ b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp @@ -32,9 +32,10 @@ namespace QmlDesigner { RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand() = default; -RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand(qint32 id, const QSize &size) +RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand(qint32 id, const QSize &size, const QString &componentPath) : m_instanceId(id) , m_size(size) + , m_componentPath(componentPath) { } @@ -48,10 +49,16 @@ QSize QmlDesigner::RequestModelNodePreviewImageCommand::size() const return m_size; } +QString RequestModelNodePreviewImageCommand::componentPath() const +{ + return m_componentPath; +} + QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command) { out << int(command.instanceId()); out << command.size(); + out << command.componentPath(); return out; } @@ -60,6 +67,7 @@ QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &co { in >> command.m_instanceId; in >> command.m_size; + in >> command.m_componentPath; return in; } @@ -67,7 +75,8 @@ QDebug operator <<(QDebug debug, const RequestModelNodePreviewImageCommand &comm { return debug.nospace() << "RequestModelNodePreviewImageCommand(" << "instanceId: " << command.instanceId() << ", " - << "size: " << command.size() << ")"; + << "size: " << command.size() << ", " + << "componentPath: " << command.componentPath() << ")"; } } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h index f18055695b9..14ebe6156ff 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h @@ -40,14 +40,16 @@ class RequestModelNodePreviewImageCommand public: RequestModelNodePreviewImageCommand(); - explicit RequestModelNodePreviewImageCommand(qint32 id, const QSize &size); + explicit RequestModelNodePreviewImageCommand(qint32 id, const QSize &size, const QString &componentPath); qint32 instanceId() const; QSize size() const; + QString componentPath() const; private: qint32 m_instanceId; QSize m_size; + QString m_componentPath; }; QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml index 44580a0b302..277bc8431f9 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml @@ -41,6 +41,7 @@ Item { property var materialViewComponent property var effectViewComponent property var modelViewComponent + property var nodeViewComponent property bool ready: false @@ -65,6 +66,8 @@ Item { createViewForEffect(obj); else if (obj instanceof Model) createViewForModel(obj); + else if (obj instanceof Node) + createViewForNode(obj); previewObject = obj; } @@ -99,10 +102,20 @@ Item { view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); } + function createViewForNode(node) + { + if (!nodeViewComponent) + nodeViewComponent = Qt.createComponent("NodeNodeView.qml"); + + // Always recreate the view to ensure node is up to date + if (nodeViewComponent.status === Component.Ready) + view = nodeViewComponent.createObject(viewRect, {"importScene": node}); + } + function afterRender() { - if (previewObject instanceof Model) { - view.fitModel(); + if (previewObject instanceof Node) { + view.fitToViewPort(); ready = view.ready; } else { ready = true; diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNodeView.qml index 1379a27a057..dc10f441e1c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNodeView.qml @@ -36,7 +36,7 @@ View3D { property real prevZoomFactor: -1 property Model sourceModel - function fitModel() + function fitToViewPort() { cameraControl.focusObject(model, theCamera.eulerRotation, true, false); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/NodeNodeView.qml new file mode 100644 index 00000000000..b41c74af54e --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/NodeNodeView.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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. +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick3D 1.15 + +View3D { + id: root + anchors.fill: parent + environment: sceneEnv + camera: theCamera + + property bool ready: false + property bool first: true + property real prevZoomFactor: -1 + + function fitToViewPort() + { + if (first) { + first = false; + selectionBox.targetNode = root.importScene; + } else { + cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false); + + if (cameraControl._zoomFactor < 0.1) { + root.importScene.scale = root.importScene.scale.times(10); + } else if (cameraControl._zoomFactor > 10) { + root.importScene.scale = root.importScene.scale.times(0.1); + } else { + // We need one more render after zoom factor change, so only set ready when zoom factor + // or scaling hasn't changed from the previous frame + ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); + prevZoomFactor = cameraControl._zoomFactor; + selectionBox.visible = false; + } + } + } + + SceneEnvironment { + id: sceneEnv + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + SelectionBox { + id: selectionBox + view3D: root + geometryName: "NodeNodeViewSB" + } + + EditCameraController { + id: cameraControl + camera: theCamera + anchors.fill: parent + view3d: root + ignoreToolState: true + } + + DirectionalLight { + eulerRotation.x: -30 + eulerRotation.y: -30 + } + + PerspectiveCamera { + id: theCamera + z: 600 + y: 600 + x: 600 + eulerRotation.x: -45 + eulerRotation.y: -45 + clipFar: 10000 + clipNear: 1 + } +} diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 05f86ae4d34..8b6fe9f5829 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -533,37 +533,60 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() if (!m_ModelNode3DImageViewContentItem) m_ModelNode3DImageViewContentItem = getContentItemForRendering(m_ModelNode3DImageViewRootItem); - ServerNodeInstance instance = instanceForId(m_modelNodelPreviewImageCommand.instanceId()); - QObject *instanceObj = instance.internalObject(); - QSize renderSize = m_modelNodelPreviewImageCommand.size() * 2; - QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "createViewForObject", - Q_ARG(QVariant, objectToVariant(instanceObj)), - Q_ARG(QVariant, QVariant::fromValue(renderSize.width())), - Q_ARG(QVariant, QVariant::fromValue(renderSize.height()))); - - QImage renderImage; - bool ready = false; - int count = 0; // Ensure we don't ever get stuck in an infinite loop - while (!ready && ++count < 10) { - updateNodesRecursive(m_ModelNode3DImageViewContentItem); - - // Fake render loop signaling to update things like QML items as 3D textures - m_ModelNode3DImageView->beforeSynchronizing(); - m_ModelNode3DImageView->beforeRendering(); - - QSizeF size = qobject_cast(m_ModelNode3DImageViewContentItem)->size(); - QRectF renderRect(QPointF(0., 0.), size); - renderImage = designerSupport()->renderImageForItem(m_ModelNode3DImageViewContentItem, - renderRect, size.toSize()); - m_ModelNode3DImageView->afterRendering(); - - QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "afterRender"); - ready = QQmlProperty::read(m_ModelNode3DImageViewRootItem, "ready").value(); - } - QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "destroyView"); - // Key number is selected so that it is unlikely to conflict other ImageContainer use. - auto imgContainer = ImageContainer(m_modelNodelPreviewImageCommand.instanceId(), renderImage, 2100000001); + auto imgContainer = ImageContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001); + QImage renderImage; + if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) { + renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()]; + } else { + QObject *instanceObj = nullptr; + if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()) { + QQmlComponent *component = new QQmlComponent(engine()); + component->loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath())); + instanceObj = qobject_cast(component->create()); + if (!instanceObj) { + qWarning() << "Could not create preview component: " << component->errors(); + nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::RenderModelNodePreviewImage, + QVariant::fromValue(imgContainer)}); + return; + } + } else { + ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId()); + instanceObj = instance.internalObject(); + } + QSize renderSize = m_modelNodePreviewImageCommand.size() * 2; + + QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "createViewForObject", + Q_ARG(QVariant, objectToVariant(instanceObj)), + Q_ARG(QVariant, QVariant::fromValue(renderSize.width())), + Q_ARG(QVariant, QVariant::fromValue(renderSize.height()))); + + bool ready = false; + int count = 0; // Ensure we don't ever get stuck in an infinite loop + while (!ready && ++count < 10) { + updateNodesRecursive(m_ModelNode3DImageViewContentItem); + + // Fake render loop signaling to update things like QML items as 3D textures + m_ModelNode3DImageView->beforeSynchronizing(); + m_ModelNode3DImageView->beforeRendering(); + + QSizeF size = qobject_cast(m_ModelNode3DImageViewContentItem)->size(); + QRectF renderRect(QPointF(0., 0.), size); + renderImage = designerSupport()->renderImageForItem(m_ModelNode3DImageViewContentItem, + renderRect, size.toSize()); + m_ModelNode3DImageView->afterRendering(); + + QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "afterRender"); + ready = QQmlProperty::read(m_ModelNode3DImageViewRootItem, "ready").value(); + } + QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "destroyView"); + if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()) { + // If component changes, puppet will need a reset anyway, so we can cache the image + m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), renderImage); + } + } + + imgContainer.setImage(renderImage); // send the rendered image to creator process nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::RenderModelNodePreviewImage, @@ -1319,9 +1342,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) { - m_modelNodelPreviewImageCommand = command; + m_modelNodePreviewImageCommand = command; - ServerNodeInstance instance = instanceForId(m_modelNodelPreviewImageCommand.instanceId()); + ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId()); if (instance.isSubclassOf("QQuick3DObject")) renderModelNode3DImageView(); } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index f85d974e0fa..fa073231ebc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -34,6 +34,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE class QDragMoveEvent; @@ -126,7 +127,8 @@ private: QPointer m_ModelNode3DImageView; QQuickItem *m_ModelNode3DImageViewRootItem = nullptr; QQuickItem *m_ModelNode3DImageViewContentItem = nullptr; - RequestModelNodePreviewImageCommand m_modelNodelPreviewImageCommand; + RequestModelNodePreviewImageCommand m_modelNodePreviewImageCommand; + QHash m_modelNodePreviewImageCache; QSet m_view3Ds; QMultiHash m_3DSceneMap; // key: scene root, value: node QObject *m_active3DView = nullptr; diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc index 9b01f0249db..adbf6f58e8e 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc @@ -44,6 +44,7 @@ mockfiles/MaterialNodeView.qml mockfiles/EffectNodeView.qml mockfiles/ModelNodeView.qml + mockfiles/NodeNodeView.qml mockfiles/meshes/arrow.mesh mockfiles/meshes/scalerod.mesh mockfiles/meshes/ring.mesh diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index b7a59274b23..55438be21db 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -207,42 +207,31 @@ QMultiHash DesignerActionManager::modelN void DesignerActionManager::registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler) { m_modelNodePreviewImageHandlers.insert(handler.type, handler); - - // Registering a new handler potentially invalidates no-handler set - m_noModelNodePreviewImageHandlers.clear(); } bool DesignerActionManager::hasModelNodePreviewHandler(const ModelNode &node) const { - if (m_modelNodePreviewImageHandlers.contains(node.type())) - return true; - - if (m_noModelNodePreviewImageHandlers.contains(node.type())) - return false; - - // Node may be a subclass of a registered type + const bool isComponent = node.isComponent(); for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { - if (node.isSubclassOf(handler.type)) { + if ((isComponent || !handler.componentOnly) && node.isSubclassOf(handler.type)) { ModelNodePreviewImageHandler subClassHandler = handler; return true; } } - - m_noModelNodePreviewImageHandlers.insert(node.type()); return false; } ModelNodePreviewImageOperation DesignerActionManager::modelNodePreviewOperation(const ModelNode &node) const { ModelNodePreviewImageOperation op = nullptr; - if (!m_noModelNodePreviewImageHandlers.contains(node.type())) { - int prio = -1; - for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { - if (node.isSubclassOf(handler.type) && handler.priority > prio) - op = handler.operation; + int prio = -1; + const bool isComponent = node.isComponent(); + for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { + if ((isComponent || !handler.componentOnly) && handler.priority > prio + && node.isSubclassOf(handler.type)) { + op = handler.operation; + prio = handler.priority; } - if (!op) - m_noModelNodePreviewImageHandlers.insert(node.type()); } return op; } @@ -1415,6 +1404,10 @@ void DesignerActionManager::createDefaultModelNodePreviewImageHandlers() registerModelNodePreviewHandler( ModelNodePreviewImageHandler("QtQuick3D.Model", ModelNodeOperations::previewImageDataFor3DNode)); + registerModelNodePreviewHandler( + ModelNodePreviewImageHandler("QtQuick3D.Node", + ModelNodeOperations::previewImageDataFor3DNode, + true)); // TODO - Disabled until QTBUG-86616 is fixed // registerModelNodePreviewHandler( diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index b76d2faa6a5..7731b09e633 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -72,15 +72,18 @@ struct ModelNodePreviewImageHandler public: ModelNodePreviewImageHandler(const TypeName &t, ModelNodePreviewImageOperation op, + bool compOnly = false, int prio = 0) : type(t) , operation(op) + , componentOnly(compOnly) , priority(prio) { } TypeName type; ModelNodePreviewImageOperation operation = nullptr; + bool componentOnly = false; int priority = 0; }; @@ -137,7 +140,6 @@ private: DesignerActionManagerView *m_designerActionManagerView; QList m_addResourceHandler; QMultiHash m_modelNodePreviewImageHandlers; - mutable QSet m_noModelNodePreviewImageHandlers; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index cf22a9bbcf1..04794c72f38 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -1521,14 +1522,14 @@ void removeGroup(const SelectionContext &selectionContext) QVariant previewImageDataFor3DNode(const ModelNode &modelNode) { if (modelNode.isValid()) - return modelNode.model()->previewImageDataFor3DNode(modelNode); + return modelNode.model()->nodeInstanceView()->previewImageDataFor3DNode(modelNode); return {}; } QVariant previewImageDataForImageNode(const ModelNode &modelNode) { if (modelNode.isValid()) - return modelNode.model()->previewImageDataForImageNode(modelNode); + return modelNode.model()->nodeInstanceView()->previewImageDataForImageNode(modelNode); return {}; } diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 5c6ac55d032..4d97eb8694e 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -118,9 +118,6 @@ public: QList selectedNodes(AbstractView *view) const; - QVariant previewImageDataFor3DNode(const ModelNode &modelNode); - QVariant previewImageDataForImageNode(const ModelNode &modelNode); - protected: Model(); diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 1372f47b3b7..fbb0a718f56 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -143,6 +143,9 @@ public: void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override; + QVariant previewImageDataFor3DNode(const ModelNode &modelNode); + QVariant previewImageDataForImageNode(const ModelNode &modelNode); + protected: void timerEvent(QTimerEvent *event) override; @@ -201,6 +204,8 @@ private: // functions // puppet to creator command handlers void handlePuppetKeyPress(int key, Qt::KeyboardModifiers modifiers); + void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image); + private: NodeInstance m_rootNodeInstance; NodeInstance m_activeStateInstance; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index a7de6ae5de1..c55d6561c2b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1495,7 +1495,7 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto node = modelNodeForInternalId(container.instanceId()); if (node.isValid()) { image.setDevicePixelRatio(2.); - emitModelNodelPreviewImageChanged(node, image); + updatePreviewImageForNode(node, image); } } } @@ -1527,11 +1527,15 @@ void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node) if (node.isValid()) { auto instance = instanceForModelNode(node); if (instance.isValid()) { + QString componentPath; + if (node.isComponent()) + componentPath = node.metaInfo().componentFileName(); m_nodeInstanceServer->requestModelNodePreviewImage( RequestModelNodePreviewImageCommand( instance.instanceId(), QSize(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS, - Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS))); + Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS), + componentPath)); } } } @@ -1547,4 +1551,113 @@ void NodeInstanceView::timerEvent(QTimerEvent *event) restartProcess(); } +struct ImageData { + QDateTime time; + QImage image; + QString type; + QString id; + QString info; +}; +static QHash> imageDataMap; + +static QVariant imageDataToVariant(const ImageData &imageData) +{ + if (!imageData.image.isNull()) { + QVariantMap map; + map.insert("type", imageData.type); + map.insert("image", QVariant::fromValue(imageData.image)); + map.insert("id", imageData.id); + map.insert("info", imageData.info); + return map; + } + return {}; +} + +QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNode) +{ + if (!modelNode.isValid()) + return {}; + + // Images on file system can be cached globally as they are found by absolute paths + QHash &localDataMap = imageDataMap[{}]; + + VariantProperty prop = modelNode.variantProperty("source"); + QString imageSource = prop.value().toString(); + QFileInfo imageFi(imageSource); + if (imageFi.isRelative()) + imageSource = QFileInfo(modelNode.model()->fileUrl().toLocalFile()).dir().absoluteFilePath(imageSource); + + imageFi = QFileInfo(imageSource); + QDateTime modified = imageFi.lastModified(); + + ImageData imageData; + bool reload = true; + if (localDataMap.contains(imageSource)) { + imageData = localDataMap[imageSource]; + if (modified == imageData.time) + reload = false; + } + + if (reload) { + QImage originalImage; + originalImage.load(imageSource); + if (!originalImage.isNull()) { + imageData.image = originalImage.scaled(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2, + Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2, + Qt::KeepAspectRatio); + imageData.image.setDevicePixelRatio(2.); + + double imgSize = double(imageFi.size()); + imageData.type = QStringLiteral("%1 (%2)").arg(QString::fromLatin1(modelNode.type())).arg(imageFi.suffix()); + imageData.id = modelNode.id(); + static QStringList units({QObject::tr("B"), QObject::tr("KB"), QObject::tr("MB"), QObject::tr("GB")}); + int unitIndex = 0; + while (imgSize > 1024. && unitIndex < units.size() - 1) { + ++unitIndex; + imgSize /= 1024.; + } + imageData.info = QStringLiteral("%1 x %2 (%3%4)").arg(originalImage.width()).arg(originalImage.height()) + .arg(QString::number(imgSize, 'g', 3)).arg(units[unitIndex]); + localDataMap.insert(imageSource, imageData); + } + } + + return imageDataToVariant(imageData); +} + +QVariant NodeInstanceView::previewImageDataFor3DNode(const ModelNode &modelNode) +{ + QFileInfo docFi = QFileInfo(modelNode.model()->fileUrl().toLocalFile()); + QHash &localDataMap = imageDataMap[docFi.absoluteFilePath()]; + ImageData imageData; + static const QImage placeHolder(":/navigator/icon/tooltip_placeholder.png"); + + // We need puppet to generate the image, which needs to be asynchronous. + // Until the image is ready, we show a placeholder + const QString id = modelNode.id(); + if (localDataMap.contains(id)) { + imageData = localDataMap[id]; + } else { + imageData.type = QString::fromLatin1(modelNode.type()); + imageData.id = id; + imageData.image = placeHolder; + localDataMap.insert(id, imageData); + } + requestModelNodePreviewImage(modelNode); + + return imageDataToVariant(imageData); +} + +void NodeInstanceView::updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image) +{ + QFileInfo docFi = QFileInfo(modelNode.model()->fileUrl().toLocalFile()); + QString docPath = docFi.absoluteFilePath(); + if (imageDataMap.contains(docPath)) { + QHash &localDataMap = imageDataMap[docPath]; + if (localDataMap.contains(modelNode.id())) + localDataMap[modelNode.id()].image = image; + } + emitModelNodelPreviewImageChanged(modelNode, image); +} + } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index fc043da770b..e2ce604ba2b 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -26,7 +26,6 @@ #include "model.h" #include "model_p.h" #include -#include #include "internalnode_p.h" #include "invalidpropertyexception.h" #include "invalidargumentexception.h" @@ -286,111 +285,6 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &internalNodePo m_internalIdNodeHash.remove(internalNodePointer->internalId()); } -struct ImageData { - QDateTime time; - QImage image; - QString type; - QString id; - QString info; -}; -static QHash> imageDataMap; - -static QVariant imageDataToVariant(const ImageData &imageData) -{ - if (!imageData.image.isNull()) { - QVariantMap map; - map.insert("type", imageData.type); - map.insert("image", QVariant::fromValue(imageData.image)); - map.insert("id", imageData.id); - map.insert("info", imageData.info); - return map; - } - return {}; -} - -QVariant ModelPrivate::previewImageDataForImageNode(const ModelNode &modelNode) -{ - // Images on file system can be cached globally as they are found by absolute paths - QHash &localDataMap = imageDataMap[{}]; - - VariantProperty prop = modelNode.variantProperty("source"); - QString imageSource = prop.value().toString(); - QFileInfo imageFi(imageSource); - if (imageFi.isRelative()) - imageSource = QFileInfo(m_fileUrl.toLocalFile()).dir().absoluteFilePath(imageSource); - - imageFi = QFileInfo(imageSource); - QDateTime modified = imageFi.lastModified(); - - ImageData imageData; - bool reload = true; - if (localDataMap.contains(imageSource)) { - imageData = localDataMap[imageSource]; - if (modified == imageData.time) - reload = false; - } - - if (reload) { - QImage originalImage; - originalImage.load(imageSource); - if (!originalImage.isNull()) { - imageData.image = originalImage.scaled(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2, - Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2, - Qt::KeepAspectRatio); - imageData.image.setDevicePixelRatio(2.); - - double imgSize = double(imageFi.size()); - imageData.type = QStringLiteral("%1 (%2)").arg(QString::fromLatin1(modelNode.type())).arg(imageFi.suffix()); - imageData.id = modelNode.id(); - static QStringList units({QObject::tr("B"), QObject::tr("KB"), QObject::tr("MB"), QObject::tr("GB")}); - int unitIndex = 0; - while (imgSize > 1024. && unitIndex < units.size() - 1) { - ++unitIndex; - imgSize /= 1024.; - } - imageData.info = QStringLiteral("%1 x %2 (%3%4)").arg(originalImage.width()).arg(originalImage.height()) - .arg(QString::number(imgSize, 'g', 3)).arg(units[unitIndex]); - localDataMap.insert(imageSource, imageData); - } - } - - return imageDataToVariant(imageData); -} - -QVariant ModelPrivate::previewImageDataFor3DNode(const ModelNode &modelNode) -{ - QFileInfo docFi = QFileInfo(m_fileUrl.toLocalFile()); - QHash &localDataMap = Internal::imageDataMap[docFi.absoluteFilePath()]; - Internal::ImageData imageData; - static const QImage placeHolder(":/navigator/icon/tooltip_placeholder.png"); - - // We need puppet to generate the image, which needs to be asynchronous. - // Until the image is ready, we show a placeholder - const QString id = modelNode.id(); - if (localDataMap.contains(id)) { - imageData = localDataMap[id]; - } else { - imageData.type = QString::fromLatin1(modelNode.type()); - imageData.id = id; - imageData.image = placeHolder; - localDataMap.insert(id, imageData); - } - modelNode.model()->nodeInstanceView()->requestModelNodePreviewImage(modelNode); - - return imageDataToVariant(imageData); -} - -void ModelPrivate::updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image) -{ - QFileInfo docFi = QFileInfo(m_fileUrl.toLocalFile()); - QString docPath = docFi.absoluteFilePath(); - if (imageDataMap.contains(docPath)) { - QHash &localDataMap = imageDataMap[docPath]; - if (localDataMap.contains(modelNode.id())) - localDataMap[modelNode.id()].image = image; - } -} - void ModelPrivate::removeAllSubNodes(const InternalNode::Pointer &internalNodePointer) { foreach (const InternalNodePointer &subNode, internalNodePointer->allSubNodes()) { @@ -808,8 +702,6 @@ void ModelPrivate::notifyUpdateActiveScene3D(const QVariantMap &sceneState) void ModelPrivate::notifyModelNodePreviewImageChanged(const ModelNode &node, const QImage &image) { - updatePreviewImageForNode(node, image); - for (const QPointer &view : qAsConst(m_viewList)) { Q_ASSERT(view != nullptr); view->modelNodePreviewImageChanged(node, image); @@ -2183,16 +2075,6 @@ QList Model::selectedNodes(AbstractView *view) const return d->toModelNodeList(d->selectedNodes(), view); } -QVariant Model::previewImageDataFor3DNode(const ModelNode &modelNode) -{ - return d->previewImageDataFor3DNode(modelNode); -} - -QVariant Model::previewImageDataForImageNode(const ModelNode &modelNode) -{ - return d->previewImageDataForImageNode(modelNode); -} - /*! \brief Returns the URL against which relative URLs within the model should be resolved. \return The base URL. diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index a406984d83a..2d6669aeb9e 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -243,10 +243,6 @@ private: //functions QVector toModelNodeVector(const QVector &internalNodeVector, AbstractView *view) const; QVector toInternalNodeVector(const QVector &internalNodeVector) const; - QVariant previewImageDataFor3DNode(const ModelNode &modelNode); - QVariant previewImageDataForImageNode(const ModelNode &modelNode); - void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image); - private: Model *m_q; MetaInfo m_metaInfo;