diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index fffecc3609c..92fc069690a 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -218,6 +218,7 @@ public: virtual void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors); virtual QImage grabWindow() = 0; + virtual QImage grabItem(QQuickItem *item) = 0; public slots: void refreshLocalFileProperty(const QString &path); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 384f6405e5b..ad9de63f57e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -761,7 +761,8 @@ void Qt5InformationNodeInstanceServer::updateNodesRecursive(QQuickItem *item) const auto childItems = item->childItems(); for (QQuickItem *childItem : childItems) updateNodesRecursive(childItem); - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { + + if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { if (item->flags() & QQuickItem::ItemHasContents) item->update(); } else { @@ -773,7 +774,7 @@ QQuickItem *Qt5InformationNodeInstanceServer::getContentItemForRendering(QQuickI { QQuickItem *contentItem = QQmlProperty::read(rootItem, "contentItem").value(); if (contentItem) { - if (!Internal::QuickItemNodeInstance::unifiedRenderPath()) + if (!Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) designerSupport()->refFromEffectItem(contentItem, false); QmlDesigner::Internal::QmlPrivateGate::disableNativeTextRendering(contentItem); } @@ -899,7 +900,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() instanceObj = instance.internalObject(); } QSize renderSize = m_modelNodePreviewImageCommand.size(); - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { + if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { // Requested size is already adjusted for target pixel ratio, so we have to adjust // back if ratio is not default for our window. double ratio = m_modelNode3DImageViewData.window->devicePixelRatio(); @@ -1106,7 +1107,7 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer() if (m_editView3DData.rootItem) QMetaObject::invokeMethod(m_editView3DData.rootItem, "aboutToShutDown", Qt::DirectConnection); - if (!Internal::QuickItemNodeInstance::unifiedRenderPath()) { + if (!Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { if (m_editView3DData.contentItem) designerSupport()->derefFromEffectItem(m_editView3DData.contentItem); if (m_modelNode3DImageViewData.contentItem) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index 93e6e786cd5..6c914bf3ea8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -64,11 +64,11 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : const bool qt6 = true; #endif - const bool unifiedRenderPath = qt6 || qEnvironmentVariableIsSet("QMLPUPPET_UNIFIED_RENDER_PATH"); + const bool unifiedRenderPath = qEnvironmentVariableIsSet("QMLPUPPET_UNIFIED_RENDER_PATH"); if (unifiedRenderPath) Internal::QuickItemNodeInstance::enableUnifiedRenderPath(true); - else + else if (!qt6) DesignerSupport::activateDesignerWindowManager(); if (QCoreApplication::arguments().at(1) == QLatin1String("--readcapturedstream")) { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index da9f7ed8233..13cdb4b9b93 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -44,10 +44,13 @@ #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include -#include -#include +#include #include #include +#include +#include +#include +#include #endif namespace QmlDesigner { @@ -332,6 +335,80 @@ QImage Qt5NodeInstanceServer::grabWindow() return {}; } +QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item) +{ + QImage renderImage; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!m_viewData.rootItem || (m_viewData.bufferDirty && !initRhi(m_viewData))) + return {}; + + QQuickItemPrivate *pItem = QQuickItemPrivate::get(item); + pItem->refFromEffectItem(false); + + ServerNodeInstance instance = instanceForObject(item); + const auto childInstances = instance.childItems(); + + // Hide immediate children that have instances and are QQuickItems so we get only + // the parent item's content, as compositing is handled on creator side. + for (const auto &childInstance : childInstances) { + QQuickItem *childItem = qobject_cast(childInstance.internalObject()); + if (childItem) { + QQuickItemPrivate *pChild = QQuickItemPrivate::get(childItem); + pChild->refFromEffectItem(true); + } + } + + m_viewData.renderControl->polishItems(); + m_viewData.renderControl->beginFrame(); + m_viewData.renderControl->sync(); + + // Connection to afterRendering is necessary, as this needs to be done before + // call to endNextRhiFrame which happens inside QQuickRenderControl::render() + QMetaObject::Connection connection = QObject::connect(m_viewData.window.data(), + &QQuickWindow::afterRendering, + this, [&]() { + // To get only the single item, we need to make a layer out of it, which enables + // us to render it to a texture that we can grab to an image. + QSGRenderContext *rc = QQuickWindowPrivate::get(m_viewData.window.data())->context; + QSGLayer *layer = rc->sceneGraphContext()->createLayer(rc); + layer->setItem(pItem->itemNode()); + QSizeF itemSize = QSizeF(item->width(), item->height()); + layer->setRect(QRectF(0, itemSize.height(), itemSize.width(), -itemSize.height())); + const QSize minSize = rc->sceneGraphContext()->minimumFBOSize(); + layer->setSize(QSize(qMax(minSize.width(), int(itemSize.width())), + qMax(minSize.height(), int(itemSize.height())))); + layer->scheduleUpdate(); + + if (layer->updateTexture()) + renderImage = layer->toImage(); + else + qWarning() << __FUNCTION__ << "Failed to update layer texture"; + + delete layer; + layer = nullptr; + }); + + m_viewData.renderControl->render(); + + QObject::disconnect(connection); + + m_viewData.renderControl->endFrame(); + + // Restore visibility of immediate children that have instances and are QQuickItems + for (const auto &childInstance : childInstances) { + QQuickItem *childItem = qobject_cast(childInstance.internalObject()); + if (childItem) { + QQuickItemPrivate *pChild = QQuickItemPrivate::get(childItem); + pChild->derefFromEffectItem(true); + } + } + pItem->derefFromEffectItem(false); +#else + Q_UNUSED(item) +#endif + return renderImage; +} + void Qt5NodeInstanceServer::refreshBindings() { DesignerSupport::refreshExpressions(context()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index 9a7d43635df..190a0012754 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -69,6 +69,7 @@ public: void reparentInstances(const ReparentInstancesCommand &command) override; QImage grabWindow() override; + QImage grabItem(QQuickItem *item) override; protected: void initializeView() override; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 63dba600d50..97e015b2e74 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -102,9 +102,6 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() clearChangedPropertyList(); if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - /* QQuickItem::grabToImage render path */ - /* TODO implement QQuickItem::grabToImage based rendering */ - /* sheduleRootItemRender(); */ if (windowDirty) nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()})); } else { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp index ee74ab856c9..51b1dcd1c06 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -171,10 +171,15 @@ void QuickItemNodeInstance::enableUnifiedRenderPath(bool unifiedRenderPath) bool QuickItemNodeInstance::checkIfRefFromEffect(qint32 id) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (s_unifiedRenderPath) return false; return (s_createEffectItem || id == 0); +#else + Q_UNUSED(id) + return false; +#endif } void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, @@ -267,9 +272,13 @@ QStringList QuickItemNodeInstance::allStates() const void QuickItemNodeInstance::updateDirtyNode(QQuickItem *item) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (s_unifiedRenderPath) return; DesignerSupport::updateDirtyNode(item); +#else + Q_UNUSED(item) +#endif } bool QuickItemNodeInstance::unifiedRenderPath() @@ -277,6 +286,15 @@ bool QuickItemNodeInstance::unifiedRenderPath() return s_unifiedRenderPath; } +bool QuickItemNodeInstance::unifiedRenderPathOrQt6() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return true; +#else + return s_unifiedRenderPath; +#endif +} + QRectF QuickItemNodeInstance::contentItemBoundingBox() const { if (contentItem()) { @@ -415,16 +433,15 @@ QImage QuickItemNodeInstance::renderImage() const updateDirtyNodesRecursive(quickItem()); QRectF renderBoundingRect = boundingRect(); + QImage renderImage; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QSize size = renderBoundingRect.size().toSize(); static double devicePixelRatio = qgetenv("FORMEDITOR_DEVICE_PIXEL_RATIO").toDouble(); if (size.width() * size.height() > 4000 * 4000) size = QSize(0,0); size *= devicePixelRatio; - QImage renderImage; - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (s_unifiedRenderPath) { renderImage = nodeInstanceServer()->quickWindow()->grabWindow(); } else { @@ -438,8 +455,12 @@ QImage QuickItemNodeInstance::renderImage() const } renderImage.setDevicePixelRatio(devicePixelRatio); #else - renderImage = nodeInstanceServer()->grabWindow(); + if (s_unifiedRenderPath) + renderImage = nodeInstanceServer()->grabWindow(); + else + renderImage = nodeInstanceServer()->grabItem(quickItem()); renderImage = renderImage.copy(renderBoundingRect.toRect()); + /* When grabbing an offscren window the device pixel ratio is 1 */ renderImage.setDevicePixelRatio(1); #endif diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h index b71b9909864..7e9bd53341f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h @@ -104,6 +104,7 @@ public: static void updateDirtyNode(QQuickItem *item); static bool unifiedRenderPath(); + static bool unifiedRenderPathOrQt6(); protected: explicit QuickItemNodeInstance(QQuickItem*);