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(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

View File

@@ -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);

View File

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

View File

@@ -36,7 +36,7 @@ View3D {
property real prevZoomFactor: -1
property Model sourceModel
function fitModel()
function fitToViewPort()
{
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,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<QQuickItem *>(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<bool>();
}
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<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",
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<QQuickItem *>(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<bool>();
}
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();
}

View File

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

View File

@@ -44,6 +44,7 @@
<file>mockfiles/MaterialNodeView.qml</file>
<file>mockfiles/EffectNodeView.qml</file>
<file>mockfiles/ModelNodeView.qml</file>
<file>mockfiles/NodeNodeView.qml</file>
<file>mockfiles/meshes/arrow.mesh</file>
<file>mockfiles/meshes/scalerod.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)
{
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(

View File

@@ -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<AddResourceHandler> m_addResourceHandler;
QMultiHash<TypeName, ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
mutable QSet<TypeName> m_noModelNodePreviewImageHandlers;
};
} //QmlDesigner

View File

@@ -46,6 +46,7 @@
#include <nodelistproperty.h>
#include <nodeproperty.h>
#include <signalhandlerproperty.h>
#include <nodeinstanceview.h>
#include <componentcore_constants.h>
#include <stylesheetmerger.h>
@@ -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 {};
}

View File

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

View File

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

View File

@@ -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<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_p.h"
#include <modelnode.h>
#include <qmldesignerconstants.h>
#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<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)
{
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<AbstractView> &view : qAsConst(m_viewList)) {
Q_ASSERT(view != nullptr);
view->modelNodePreviewImageChanged(node, image);
@@ -2183,16 +2075,6 @@ QList<ModelNode> 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.

View File

@@ -243,10 +243,6 @@ private: //functions
QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &internalNodeVector, AbstractView *view) 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:
Model *m_q;
MetaInfo m_metaInfo;