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

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