QmlDesigner: Add navigator tooltip for 3D Components with Node root

This enables showing preview tooltip for all imported 3D models.

Also moved the preview image handling out of the model and into
NodeInstanceView, where it fits more naturally.

Change-Id: I48135d06aa8d9313525dae618e22692563da78fd
Fixes: QDS-2807
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-09-22 11:30:05 +03:00
parent 762addfbd0
commit 31ec38dba5
16 changed files with 324 additions and 189 deletions

View File

@@ -32,9 +32,10 @@ namespace QmlDesigner {
RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand() = default; RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand() = default;
RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand(qint32 id, const QSize &size) RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand(qint32 id, const QSize &size, const QString &componentPath)
: m_instanceId(id) : m_instanceId(id)
, m_size(size) , m_size(size)
, m_componentPath(componentPath)
{ {
} }
@@ -48,10 +49,16 @@ QSize QmlDesigner::RequestModelNodePreviewImageCommand::size() const
return m_size; return m_size;
} }
QString RequestModelNodePreviewImageCommand::componentPath() const
{
return m_componentPath;
}
QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command) QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command)
{ {
out << int(command.instanceId()); out << int(command.instanceId());
out << command.size(); out << command.size();
out << command.componentPath();
return out; return out;
} }
@@ -60,6 +67,7 @@ QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &co
{ {
in >> command.m_instanceId; in >> command.m_instanceId;
in >> command.m_size; in >> command.m_size;
in >> command.m_componentPath;
return in; return in;
} }
@@ -67,7 +75,8 @@ QDebug operator <<(QDebug debug, const RequestModelNodePreviewImageCommand &comm
{ {
return debug.nospace() << "RequestModelNodePreviewImageCommand(" return debug.nospace() << "RequestModelNodePreviewImageCommand("
<< "instanceId: " << command.instanceId() << ", " << "instanceId: " << command.instanceId() << ", "
<< "size: " << command.size() << ")"; << "size: " << command.size() << ", "
<< "componentPath: " << command.componentPath() << ")";
} }
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -40,14 +40,16 @@ class RequestModelNodePreviewImageCommand
public: public:
RequestModelNodePreviewImageCommand(); RequestModelNodePreviewImageCommand();
explicit RequestModelNodePreviewImageCommand(qint32 id, const QSize &size); explicit RequestModelNodePreviewImageCommand(qint32 id, const QSize &size, const QString &componentPath);
qint32 instanceId() const; qint32 instanceId() const;
QSize size() const; QSize size() const;
QString componentPath() const;
private: private:
qint32 m_instanceId; qint32 m_instanceId;
QSize m_size; QSize m_size;
QString m_componentPath;
}; };
QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command); QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command);

View File

@@ -41,6 +41,7 @@ Item {
property var materialViewComponent property var materialViewComponent
property var effectViewComponent property var effectViewComponent
property var modelViewComponent property var modelViewComponent
property var nodeViewComponent
property bool ready: false property bool ready: false
@@ -65,6 +66,8 @@ Item {
createViewForEffect(obj); createViewForEffect(obj);
else if (obj instanceof Model) else if (obj instanceof Model)
createViewForModel(obj); createViewForModel(obj);
else if (obj instanceof Node)
createViewForNode(obj);
previewObject = obj; previewObject = obj;
} }
@@ -99,10 +102,20 @@ Item {
view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); 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() function afterRender()
{ {
if (previewObject instanceof Model) { if (previewObject instanceof Node) {
view.fitModel(); view.fitToViewPort();
ready = view.ready; ready = view.ready;
} else { } else {
ready = true; ready = true;

View File

@@ -36,7 +36,7 @@ View3D {
property real prevZoomFactor: -1 property real prevZoomFactor: -1
property Model sourceModel property Model sourceModel
function fitModel() function fitToViewPort()
{ {
cameraControl.focusObject(model, theCamera.eulerRotation, true, false); cameraControl.focusObject(model, theCamera.eulerRotation, true, false);

View File

@@ -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
}
}

View File

@@ -533,15 +533,34 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
if (!m_ModelNode3DImageViewContentItem) if (!m_ModelNode3DImageViewContentItem)
m_ModelNode3DImageViewContentItem = getContentItemForRendering(m_ModelNode3DImageViewRootItem); m_ModelNode3DImageViewContentItem = getContentItemForRendering(m_ModelNode3DImageViewRootItem);
ServerNodeInstance instance = instanceForId(m_modelNodelPreviewImageCommand.instanceId()); // Key number is selected so that it is unlikely to conflict other ImageContainer use.
QObject *instanceObj = instance.internalObject(); auto imgContainer = ImageContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
QSize renderSize = m_modelNodelPreviewImageCommand.size() * 2; 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<QQuick3DObject *>(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", QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "createViewForObject",
Q_ARG(QVariant, objectToVariant(instanceObj)), Q_ARG(QVariant, objectToVariant(instanceObj)),
Q_ARG(QVariant, QVariant::fromValue(renderSize.width())), Q_ARG(QVariant, QVariant::fromValue(renderSize.width())),
Q_ARG(QVariant, QVariant::fromValue(renderSize.height()))); Q_ARG(QVariant, QVariant::fromValue(renderSize.height())));
QImage renderImage;
bool ready = false; bool ready = false;
int count = 0; // Ensure we don't ever get stuck in an infinite loop int count = 0; // Ensure we don't ever get stuck in an infinite loop
while (!ready && ++count < 10) { while (!ready && ++count < 10) {
@@ -561,9 +580,13 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
ready = QQmlProperty::read(m_ModelNode3DImageViewRootItem, "ready").value<bool>(); ready = QQmlProperty::read(m_ModelNode3DImageViewRootItem, "ready").value<bool>();
} }
QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "destroyView"); 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);
}
}
// Key number is selected so that it is unlikely to conflict other ImageContainer use. imgContainer.setImage(renderImage);
auto imgContainer = ImageContainer(m_modelNodelPreviewImageCommand.instanceId(), renderImage, 2100000001);
// send the rendered image to creator process // send the rendered image to creator process
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::RenderModelNodePreviewImage, nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::RenderModelNodePreviewImage,
@@ -1319,9 +1342,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) 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")) if (instance.isSubclassOf("QQuick3DObject"))
renderModelNode3DImageView(); renderModelNode3DImageView();
} }

View File

@@ -34,6 +34,7 @@
#include <QTimer> #include <QTimer>
#include <QVariant> #include <QVariant>
#include <QPointer> #include <QPointer>
#include <QImage>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDragMoveEvent; class QDragMoveEvent;
@@ -126,7 +127,8 @@ private:
QPointer<QQuickView> m_ModelNode3DImageView; QPointer<QQuickView> m_ModelNode3DImageView;
QQuickItem *m_ModelNode3DImageViewRootItem = nullptr; QQuickItem *m_ModelNode3DImageViewRootItem = nullptr;
QQuickItem *m_ModelNode3DImageViewContentItem = nullptr; QQuickItem *m_ModelNode3DImageViewContentItem = nullptr;
RequestModelNodePreviewImageCommand m_modelNodelPreviewImageCommand; RequestModelNodePreviewImageCommand m_modelNodePreviewImageCommand;
QHash<QString, QImage> m_modelNodePreviewImageCache;
QSet<QObject *> m_view3Ds; QSet<QObject *> m_view3Ds;
QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node
QObject *m_active3DView = nullptr; QObject *m_active3DView = nullptr;

View File

@@ -44,6 +44,7 @@
<file>mockfiles/MaterialNodeView.qml</file> <file>mockfiles/MaterialNodeView.qml</file>
<file>mockfiles/EffectNodeView.qml</file> <file>mockfiles/EffectNodeView.qml</file>
<file>mockfiles/ModelNodeView.qml</file> <file>mockfiles/ModelNodeView.qml</file>
<file>mockfiles/NodeNodeView.qml</file>
<file>mockfiles/meshes/arrow.mesh</file> <file>mockfiles/meshes/arrow.mesh</file>
<file>mockfiles/meshes/scalerod.mesh</file> <file>mockfiles/meshes/scalerod.mesh</file>
<file>mockfiles/meshes/ring.mesh</file> <file>mockfiles/meshes/ring.mesh</file>

View File

@@ -207,42 +207,31 @@ QMultiHash<TypeName, ModelNodePreviewImageHandler> DesignerActionManager::modelN
void DesignerActionManager::registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler) void DesignerActionManager::registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler)
{ {
m_modelNodePreviewImageHandlers.insert(handler.type, 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 bool DesignerActionManager::hasModelNodePreviewHandler(const ModelNode &node) const
{ {
if (m_modelNodePreviewImageHandlers.contains(node.type())) const bool isComponent = node.isComponent();
return true;
if (m_noModelNodePreviewImageHandlers.contains(node.type()))
return false;
// Node may be a subclass of a registered type
for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) {
if (node.isSubclassOf(handler.type)) { if ((isComponent || !handler.componentOnly) && node.isSubclassOf(handler.type)) {
ModelNodePreviewImageHandler subClassHandler = handler; ModelNodePreviewImageHandler subClassHandler = handler;
return true; return true;
} }
} }
m_noModelNodePreviewImageHandlers.insert(node.type());
return false; return false;
} }
ModelNodePreviewImageOperation DesignerActionManager::modelNodePreviewOperation(const ModelNode &node) const ModelNodePreviewImageOperation DesignerActionManager::modelNodePreviewOperation(const ModelNode &node) const
{ {
ModelNodePreviewImageOperation op = nullptr; ModelNodePreviewImageOperation op = nullptr;
if (!m_noModelNodePreviewImageHandlers.contains(node.type())) {
int prio = -1; int prio = -1;
const bool isComponent = node.isComponent();
for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) {
if (node.isSubclassOf(handler.type) && handler.priority > prio) if ((isComponent || !handler.componentOnly) && handler.priority > prio
&& node.isSubclassOf(handler.type)) {
op = handler.operation; op = handler.operation;
prio = handler.priority;
} }
if (!op)
m_noModelNodePreviewImageHandlers.insert(node.type());
} }
return op; return op;
} }
@@ -1415,6 +1404,10 @@ void DesignerActionManager::createDefaultModelNodePreviewImageHandlers()
registerModelNodePreviewHandler( registerModelNodePreviewHandler(
ModelNodePreviewImageHandler("QtQuick3D.Model", ModelNodePreviewImageHandler("QtQuick3D.Model",
ModelNodeOperations::previewImageDataFor3DNode)); ModelNodeOperations::previewImageDataFor3DNode));
registerModelNodePreviewHandler(
ModelNodePreviewImageHandler("QtQuick3D.Node",
ModelNodeOperations::previewImageDataFor3DNode,
true));
// TODO - Disabled until QTBUG-86616 is fixed // TODO - Disabled until QTBUG-86616 is fixed
// registerModelNodePreviewHandler( // registerModelNodePreviewHandler(

View File

@@ -72,15 +72,18 @@ struct ModelNodePreviewImageHandler
public: public:
ModelNodePreviewImageHandler(const TypeName &t, ModelNodePreviewImageHandler(const TypeName &t,
ModelNodePreviewImageOperation op, ModelNodePreviewImageOperation op,
bool compOnly = false,
int prio = 0) int prio = 0)
: type(t) : type(t)
, operation(op) , operation(op)
, componentOnly(compOnly)
, priority(prio) , priority(prio)
{ {
} }
TypeName type; TypeName type;
ModelNodePreviewImageOperation operation = nullptr; ModelNodePreviewImageOperation operation = nullptr;
bool componentOnly = false;
int priority = 0; int priority = 0;
}; };
@@ -137,7 +140,6 @@ private:
DesignerActionManagerView *m_designerActionManagerView; DesignerActionManagerView *m_designerActionManagerView;
QList<AddResourceHandler> m_addResourceHandler; QList<AddResourceHandler> m_addResourceHandler;
QMultiHash<TypeName, ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers; QMultiHash<TypeName, ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
mutable QSet<TypeName> m_noModelNodePreviewImageHandlers;
}; };
} //QmlDesigner } //QmlDesigner

View File

@@ -46,6 +46,7 @@
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <nodeproperty.h> #include <nodeproperty.h>
#include <signalhandlerproperty.h> #include <signalhandlerproperty.h>
#include <nodeinstanceview.h>
#include <componentcore_constants.h> #include <componentcore_constants.h>
#include <stylesheetmerger.h> #include <stylesheetmerger.h>
@@ -1521,14 +1522,14 @@ void removeGroup(const SelectionContext &selectionContext)
QVariant previewImageDataFor3DNode(const ModelNode &modelNode) QVariant previewImageDataFor3DNode(const ModelNode &modelNode)
{ {
if (modelNode.isValid()) if (modelNode.isValid())
return modelNode.model()->previewImageDataFor3DNode(modelNode); return modelNode.model()->nodeInstanceView()->previewImageDataFor3DNode(modelNode);
return {}; return {};
} }
QVariant previewImageDataForImageNode(const ModelNode &modelNode) QVariant previewImageDataForImageNode(const ModelNode &modelNode)
{ {
if (modelNode.isValid()) if (modelNode.isValid())
return modelNode.model()->previewImageDataForImageNode(modelNode); return modelNode.model()->nodeInstanceView()->previewImageDataForImageNode(modelNode);
return {}; return {};
} }

View File

@@ -118,9 +118,6 @@ public:
QList<ModelNode> selectedNodes(AbstractView *view) const; QList<ModelNode> selectedNodes(AbstractView *view) const;
QVariant previewImageDataFor3DNode(const ModelNode &modelNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);
protected: protected:
Model(); Model();

View File

@@ -143,6 +143,9 @@ public:
void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override; void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override;
QVariant previewImageDataFor3DNode(const ModelNode &modelNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);
protected: protected:
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
@@ -201,6 +204,8 @@ private: // functions
// puppet to creator command handlers // puppet to creator command handlers
void handlePuppetKeyPress(int key, Qt::KeyboardModifiers modifiers); void handlePuppetKeyPress(int key, Qt::KeyboardModifiers modifiers);
void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image);
private: private:
NodeInstance m_rootNodeInstance; NodeInstance m_rootNodeInstance;
NodeInstance m_activeStateInstance; NodeInstance m_activeStateInstance;

View File

@@ -1495,7 +1495,7 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
auto node = modelNodeForInternalId(container.instanceId()); auto node = modelNodeForInternalId(container.instanceId());
if (node.isValid()) { if (node.isValid()) {
image.setDevicePixelRatio(2.); image.setDevicePixelRatio(2.);
emitModelNodelPreviewImageChanged(node, image); updatePreviewImageForNode(node, image);
} }
} }
} }
@@ -1527,11 +1527,15 @@ void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node)
if (node.isValid()) { if (node.isValid()) {
auto instance = instanceForModelNode(node); auto instance = instanceForModelNode(node);
if (instance.isValid()) { if (instance.isValid()) {
QString componentPath;
if (node.isComponent())
componentPath = node.metaInfo().componentFileName();
m_nodeInstanceServer->requestModelNodePreviewImage( m_nodeInstanceServer->requestModelNodePreviewImage(
RequestModelNodePreviewImageCommand( RequestModelNodePreviewImageCommand(
instance.instanceId(), instance.instanceId(),
QSize(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS, 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(); restartProcess();
} }
struct ImageData {
QDateTime time;
QImage image;
QString type;
QString id;
QString info;
};
static QHash<QString, QHash<QString, ImageData>> imageDataMap;
static QVariant imageDataToVariant(const ImageData &imageData)
{
if (!imageData.image.isNull()) {
QVariantMap map;
map.insert("type", imageData.type);
map.insert("image", QVariant::fromValue<QImage>(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<QString, ImageData> &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<QString, ImageData> &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<QString, ImageData> &localDataMap = imageDataMap[docPath];
if (localDataMap.contains(modelNode.id()))
localDataMap[modelNode.id()].image = image;
}
emitModelNodelPreviewImageChanged(modelNode, image);
}
} }

View File

@@ -26,7 +26,6 @@
#include "model.h" #include "model.h"
#include "model_p.h" #include "model_p.h"
#include <modelnode.h> #include <modelnode.h>
#include <qmldesignerconstants.h>
#include "internalnode_p.h" #include "internalnode_p.h"
#include "invalidpropertyexception.h" #include "invalidpropertyexception.h"
#include "invalidargumentexception.h" #include "invalidargumentexception.h"
@@ -286,111 +285,6 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &internalNodePo
m_internalIdNodeHash.remove(internalNodePointer->internalId()); m_internalIdNodeHash.remove(internalNodePointer->internalId());
} }
struct ImageData {
QDateTime time;
QImage image;
QString type;
QString id;
QString info;
};
static QHash<QString, QHash<QString, ImageData>> imageDataMap;
static QVariant imageDataToVariant(const ImageData &imageData)
{
if (!imageData.image.isNull()) {
QVariantMap map;
map.insert("type", imageData.type);
map.insert("image", QVariant::fromValue<QImage>(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<QString, ImageData> &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<QString, Internal::ImageData> &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<QString, ImageData> &localDataMap = imageDataMap[docPath];
if (localDataMap.contains(modelNode.id()))
localDataMap[modelNode.id()].image = image;
}
}
void ModelPrivate::removeAllSubNodes(const InternalNode::Pointer &internalNodePointer) void ModelPrivate::removeAllSubNodes(const InternalNode::Pointer &internalNodePointer)
{ {
foreach (const InternalNodePointer &subNode, internalNodePointer->allSubNodes()) { 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) void ModelPrivate::notifyModelNodePreviewImageChanged(const ModelNode &node, const QImage &image)
{ {
updatePreviewImageForNode(node, image);
for (const QPointer<AbstractView> &view : qAsConst(m_viewList)) { for (const QPointer<AbstractView> &view : qAsConst(m_viewList)) {
Q_ASSERT(view != nullptr); Q_ASSERT(view != nullptr);
view->modelNodePreviewImageChanged(node, image); view->modelNodePreviewImageChanged(node, image);
@@ -2183,16 +2075,6 @@ QList<ModelNode> Model::selectedNodes(AbstractView *view) const
return d->toModelNodeList(d->selectedNodes(), view); 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. \brief Returns the URL against which relative URLs within the model should be resolved.
\return The base URL. \return The base URL.

View File

@@ -243,10 +243,6 @@ private: //functions
QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &internalNodeVector, AbstractView *view) const; QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &internalNodeVector, AbstractView *view) const;
QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &internalNodeVector) const; QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &internalNodeVector) const;
QVariant previewImageDataFor3DNode(const ModelNode &modelNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);
void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image);
private: private:
Model *m_q; Model *m_q;
MetaInfo m_metaInfo; MetaInfo m_metaInfo;