QmlDesigner: Add preview of pure 3D component in form editor

If the root node is a 3D node we show a preview similar to
the state preview in the form editor. The size of the preview
is hard coded as (640, 480).

Change-Id: If7f96522b093c17422fa38102bffe11ede016063
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Thomas Hartmann
2022-02-15 16:12:13 +01:00
parent 298765c262
commit 3f078ee89d
12 changed files with 144 additions and 7 deletions

View File

@@ -398,6 +398,8 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
QQuickItemPrivate *pItem = QQuickItemPrivate::get(item);
const bool rootIs3dNode = rootNodeInstance().isSubclassOf("QQuick3DNode");
const bool renderEffects = qEnvironmentVariableIsSet("QMLPUPPET_RENDER_EFFECTS");
if (renderEffects) {
@@ -426,6 +428,9 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
QRectF renderBoundingRect;
if (instance.isValid())
renderBoundingRect = instance.boundingRect();
else if (rootIs3dNode)
renderBoundingRect = item->boundingRect();
else
renderBoundingRect = ServerNodeInstance::effectAdjustedBoundingRect(item);

View File

@@ -133,6 +133,14 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
nodeInstanceClient()->synchronizeWithClientProcess();
}
if (rootNodeInstance().isSubclassOf("QQuick3DNode") && rootNodeInstance().contentItem()
&& DesignerSupport::isDirty(rootNodeInstance().contentItem(),
DesignerSupport::ContentUpdateMask)
&& nodeInstanceClient()->bytesToWrite() < 10000) {
Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem());
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
}
inFunction = false;
}
}

View File

@@ -55,6 +55,8 @@
namespace QmlDesigner {
namespace Internal {
const QRectF preview3dBoundingRect(0, 0, 640, 480);
Quick3DNodeInstance::Quick3DNodeInstance(QObject *node)
: ObjectNodeInstance(node)
{
@@ -97,7 +99,7 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
// In case this is the scene root, we need to create a dummy View3D for the scene
// in preview puppets
if (instanceId() == 0 && nodeInstanceServer()->isPreviewServer()) {
if (instanceId() == 0 && (!nodeInstanceServer()->isInformationServer())) {
auto helper = new QmlDesigner::Internal::GeneralHelper();
engine()->rootContext()->setContextProperty("_generalHelper", helper);
@@ -111,8 +113,8 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo
nodeInstanceServer()->setRootItem(m_dummyRootView);
}
#endif
#endif
#endif // QT_VERSION
#endif // QUICK3D_MODULE
ObjectNodeInstance::initialize(objectNodeInstance, flags);
}
@@ -122,7 +124,7 @@ QImage Quick3DNodeInstance::renderImage() const
if (!isRootNodeInstance() || !m_dummyRootView)
return {};
QSize size(640, 480);
QSize size = preview3dBoundingRect.size().toSize();
nodeInstanceServer()->quickWindow()->resize(size);
m_dummyRootView->setSize(size);
@@ -190,13 +192,37 @@ bool Quick3DNodeInstance::isRenderable() const
return m_dummyRootView;
}
bool Quick3DNodeInstance::hasContent() const
{
return true;
}
QRectF Quick3DNodeInstance::boundingRect() const
{
//The information server has no m_dummyRootView therefore we use the hardcoded value
if (nodeInstanceServer()->isInformationServer())
return preview3dBoundingRect;
if (m_dummyRootView)
return m_dummyRootView->boundingRect();
return ObjectNodeInstance::boundingRect();
}
QRectF Quick3DNodeInstance::contentItemBoundingBox() const
{
return boundingRect();
}
QPointF Quick3DNodeInstance::position() const
{
return QPointF(0, 0);
}
QSizeF Quick3DNodeInstance::size() const
{
return boundingRect().size();
}
QList<ServerNodeInstance> Quick3DNodeInstance::stateInstances() const
{
QList<ServerNodeInstance> instanceList;
@@ -212,6 +238,11 @@ QList<ServerNodeInstance> Quick3DNodeInstance::stateInstances() const
return instanceList;
}
QQuickItem *Quick3DNodeInstance::contentItem() const
{
return m_dummyRootView;
}
Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const
{
return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer());

View File

@@ -51,10 +51,16 @@ public:
QImage renderPreviewImage(const QSize &previewImageSize) const override;
bool isRenderable() const override;
bool hasContent() const override;
QRectF boundingRect() const override;
QRectF contentItemBoundingBox() const override;
QPointF position() const override;
QSizeF size() const override;
QList<ServerNodeInstance> stateInstances() const override;
QQuickItem *contentItem() const override;
protected:
explicit Quick3DNodeInstance(QObject *node);

View File

@@ -162,6 +162,11 @@ bool ServerNodeInstance::isComponentWrap() const
return m_nodeInstance->isComponentWrap();
}
QQuickItem *ServerNodeInstance::contentItem() const
{
return m_nodeInstance->contentItem();
}
void ServerNodeInstance::updateDirtyNodeRecursive()
{
m_nodeInstance->updateAllDirtyNodesRecursive();

View File

@@ -188,6 +188,8 @@ public:
bool isComponentWrap() const;
QQuickItem *contentItem() const;
private: // functions
ServerNodeInstance(const QSharedPointer<Internal::ObjectNodeInstance> &abstractInstance);

View File

@@ -2226,4 +2226,39 @@ void FormEditorFlowWildcardItem::paint(QPainter *painter, const QStyleOptionGrap
FormEditorFlowDecisionItem::paint(painter, option, widget);
}
void FormEditor3dPreview::updateGeometry()
{
prepareGeometryChange();
m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
m_boundingRect = qmlItemNode().instanceBoundingRect();
m_paintedBoundingRect = m_boundingRect;
setTransform(QTransform());
}
QPointF FormEditor3dPreview::instancePosition() const
{
return QPointF(0, 0);
}
void FormEditor3dPreview::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
if (!painter->isActive())
return;
painter->save();
bool showPlaceHolder = qmlItemNode().instanceIsRenderPixmapNull();
if (showPlaceHolder)
paintPlaceHolderForInvisbleItem(painter);
else
painter->drawPixmap(m_boundingRect.topLeft(), qmlItemNode().instanceRenderPixmap());
painter->restore();
}
} //QmlDesigner

View File

@@ -171,6 +171,23 @@ private:
QPointF m_oldPos;
};
class FormEditor3dPreview : public FormEditorItem
{
friend FormEditorScene;
public:
void updateGeometry() override;
QPointF instancePosition() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
protected:
FormEditor3dPreview(const QmlItemNode &qmlItemNode, FormEditorScene *scene)
: FormEditorItem(qmlItemNode, scene)
{
setHighlightBoundingRect(true);
}
};
class FormEditorFlowActionItem : public FormEditorItem
{
friend FormEditorScene;

View File

@@ -173,7 +173,9 @@ FormEditorItem *FormEditorScene::addFormEditorItem(const QmlItemNode &qmlItemNod
{
FormEditorItem *formEditorItem = nullptr;
if (type == Flow)
if (type == Preview3d)
formEditorItem = new FormEditor3dPreview(qmlItemNode, this);
else if (type == Flow)
formEditorItem = new FormEditorFlowItem(qmlItemNode, this);
else if (type == FlowAction)
formEditorItem = new FormEditorFlowActionItem(qmlItemNode, this);

View File

@@ -58,7 +58,8 @@ public:
FlowAction,
FlowTransition,
FlowDecision,
FlowWildcard
FlowWildcard,
Preview3d
};
FormEditorScene(FormEditorWidget *widget, FormEditorView *editorView);

View File

@@ -46,6 +46,7 @@
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <rewriterview.h>
#include <qml3dnode.h>
#include <zoomaction.h>
#include <coreplugin/icore.h>
@@ -655,6 +656,14 @@ static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItem
void FormEditorView::instancesCompleted(const QVector<ModelNode> &completedNodeList)
{
if (Qml3DNode::isValidQml3DNode(rootModelNode())) {
if (completedNodeList.contains(rootModelNode())) {
FormEditorItem *item = scene()->itemForQmlItemNode(rootModelNode());
if (item)
scene()->synchronizeTransformation(item);
}
}
const bool isFlow = rootModelNode().isValid() && QmlItemNode(rootModelNode()).isFlowView();
QList<FormEditorItem*> itemNodeList;
for (const ModelNode &node : completedNodeList) {
@@ -724,6 +733,10 @@ void FormEditorView::instancesRenderImageChanged(const QVector<ModelNode> &nodeL
if (QmlItemNode::isValidQmlItemNode(node))
if (FormEditorItem *item = scene()->itemForQmlItemNode(QmlItemNode(node)))
item->update();
if (Qml3DNode::isValidQml3DNode(node)) {
if (FormEditorItem *item = scene()->itemForQmlItemNode(node))
item->update();
}
}
}
@@ -795,6 +808,9 @@ void FormEditorView::setupFormEditorWidget()
if (QmlItemNode::isValidQmlItemNode(rootModelNode()))
setupFormEditorItemTree(rootModelNode());
if (Qml3DNode::isValidQml3DNode(rootModelNode()))
setupFormEditor3DView();
m_formEditorWidget->initialize();
if (!rewriterView()->errors().isEmpty())
@@ -903,7 +919,8 @@ void FormEditorView::checkRootModelNode()
QTC_ASSERT(rootModelNode().isValid(), return);
if (!rootModelNode().metaInfo().isGraphicalItem())
if (!rootModelNode().metaInfo().isGraphicalItem()
&& !Qml3DNode::isValidQml3DNode(rootModelNode()))
m_formEditorWidget->showErrorMessageBox(
{DocumentMessage(tr("%1 is not supported as the root element by Form Editor.")
.arg(rootModelNode().simplifiedTypeName()))});
@@ -911,6 +928,13 @@ void FormEditorView::checkRootModelNode()
m_formEditorWidget->hideErrorMessageBox();
}
void FormEditorView::setupFormEditor3DView()
{
m_scene->addFormEditorItem(rootModelNode(), FormEditorScene::Preview3d);
FormEditorItem *item = m_scene->itemForQmlItemNode(rootModelNode());
item->updateGeometry();
}
void FormEditorView::reset()
{
QTimer::singleShot(200, this, &FormEditorView::delayedReset);

View File

@@ -151,6 +151,7 @@ private:
void resetNodeInstanceView();
void addOrRemoveFormEditorItem(const ModelNode &node);
void checkRootModelNode();
void setupFormEditor3DView();
QPointer<FormEditorWidget> m_formEditorWidget;
QPointer<FormEditorScene> m_scene;