diff --git a/share/qtcreator/qml/qmlpuppet/commands/commands.pri b/share/qtcreator/qml/qmlpuppet/commands/commands.pri index 680abb2744f..270c326a33b 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/commands.pri +++ b/share/qtcreator/qml/qmlpuppet/commands/commands.pri @@ -34,7 +34,8 @@ HEADERS += $$PWD/synchronizecommand.h \ \ $$PWD/update3dviewstatecommand.h \ $$PWD/puppettocreatorcommand.h \ $$PWD/inputeventcommand.h \ - $$PWD/view3dactioncommand.h + $$PWD/view3dactioncommand.h \ + $$PWD/requestmodelnodepreviewimagecommand.h SOURCES += $$PWD/synchronizecommand.cpp \ $$PWD/changepreviewimagesizecommand.cpp \ @@ -68,4 +69,5 @@ SOURCES += $$PWD/synchronizecommand.cpp \ $$PWD/update3dviewstatecommand.cpp \ $$PWD/puppettocreatorcommand.cpp \ $$PWD/inputeventcommand.cpp \ - $$PWD/view3dactioncommand.cpp + $$PWD/view3dactioncommand.cpp \ + $$PWD/requestmodelnodepreviewimagecommand.cpp diff --git a/share/qtcreator/qml/qmlpuppet/commands/puppettocreatorcommand.h b/share/qtcreator/qml/qmlpuppet/commands/puppettocreatorcommand.h index a713656bde8..dc125d4ead1 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/puppettocreatorcommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/puppettocreatorcommand.h @@ -34,7 +34,7 @@ namespace QmlDesigner { class PuppetToCreatorCommand { public: - enum Type { Edit3DToolState, Render3DView, ActiveSceneChanged, None }; + enum Type { Edit3DToolState, Render3DView, ActiveSceneChanged, RenderModelNodePreviewImage, None }; PuppetToCreatorCommand(Type type, const QVariant &data); PuppetToCreatorCommand() = default; diff --git a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp new file mode 100644 index 00000000000..c29ec216c83 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "requestmodelnodepreviewimagecommand.h" + +#include +#include + +namespace QmlDesigner { + +RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand() = default; + +RequestModelNodePreviewImageCommand::RequestModelNodePreviewImageCommand(qint32 id, const QSize &size) + : m_instanceId(id) + , m_size(size) +{ +} + +qint32 RequestModelNodePreviewImageCommand::instanceId() const +{ + return m_instanceId; +} + +QSize QmlDesigner::RequestModelNodePreviewImageCommand::size() const +{ + return m_size; +} + +QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command) +{ + out << int(command.instanceId()); + out << command.size(); + + return out; +} + +QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &command) +{ + in >> command.m_instanceId; + in >> command.m_size; + return in; +} + +QDebug operator <<(QDebug debug, const RequestModelNodePreviewImageCommand &command) +{ + return debug.nospace() << "RequestModelNodePreviewImageCommand(" + << "instanceId: " << command.instanceId() << ", " + << "size: " << command.size() << ")"; +} + +} // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h new file mode 100644 index 00000000000..f18055695b9 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "instancecontainer.h" + +namespace QmlDesigner { + +class RequestModelNodePreviewImageCommand +{ + friend QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &command); + friend QDebug operator <<(QDebug debug, const RequestModelNodePreviewImageCommand &command); + +public: + RequestModelNodePreviewImageCommand(); + explicit RequestModelNodePreviewImageCommand(qint32 id, const QSize &size); + + qint32 instanceId() const; + QSize size() const; + +private: + qint32 m_instanceId; + QSize m_size; +}; + +QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command); +QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &command); + +QDebug operator <<(QDebug debug, const RequestModelNodePreviewImageCommand &command); + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::RequestModelNodePreviewImageCommand) diff --git a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp index 0c0f4fec781..ded21b71cf4 100644 --- a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp +++ b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp @@ -75,6 +75,7 @@ #include "update3dviewstatecommand.h" #include "valueschangedcommand.h" #include "view3dactioncommand.h" +#include "requestmodelnodepreviewimagecommand.h" namespace QmlDesigner { @@ -339,6 +340,11 @@ void NodeInstanceClientProxy::view3DAction(const View3DActionCommand &command) nodeInstanceServer()->view3DAction(command); } +void NodeInstanceClientProxy::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) +{ + nodeInstanceServer()->requestModelNodePreviewImage(command); +} + void NodeInstanceClientProxy::changeLanguage(const ChangeLanguageCommand &command) { nodeInstanceServer()->changeLanguage(command); @@ -516,6 +522,7 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command) static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand"); static const int inputEventCommandType = QMetaType::type("InputEventCommand"); static const int view3DActionCommandType = QMetaType::type("View3DActionCommand"); + static const int requestModelNodePreviewImageCommandType = QMetaType::type("RequestModelNodePreviewImageCommand"); static const int changeLanguageCommand = QMetaType::type("ChangeLanguageCommand"); static const int changePreviewImageSizeCommand = QMetaType::type( "ChangePreviewImageSizeCommand"); @@ -562,6 +569,8 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command) redirectToken(command.value()); else if (commandType == view3DActionCommandType) view3DAction(command.value()); + else if (commandType == requestModelNodePreviewImageCommandType) + requestModelNodePreviewImage(command.value()); else if (commandType == synchronizeCommandType) { SynchronizeCommand synchronizeCommand = command.value(); m_synchronizeId = synchronizeCommand.synchronizeId(); diff --git a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h index 11658c5dd58..f4471b3470c 100644 --- a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h +++ b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h @@ -63,6 +63,7 @@ class ChangeSelectionCommand; class PuppetToCreatorCommand; class InputEventCommand; class View3DActionCommand; +class RequestModelNodePreviewImageCommand; class ChangeLanguageCommand; class ChangePreviewImageSizeCommand; @@ -123,6 +124,7 @@ protected: static QVariant readCommandFromIOStream(QIODevice *ioDevice, quint32 *readCommandCounter, quint32 *blockSize); void inputEvent(const InputEventCommand &command); void view3DAction(const View3DActionCommand &command); + void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command); void changeLanguage(const ChangeLanguageCommand &command); void changePreviewImageSize(const ChangePreviewImageSizeCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp index 638308af110..3e3351e507f 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp @@ -67,6 +67,7 @@ #include "update3dviewstatecommand.h" #include "valueschangedcommand.h" #include "view3dactioncommand.h" +#include "requestmodelnodepreviewimagecommand.h" #include @@ -213,6 +214,9 @@ void NodeInstanceServerInterface::registerCommands() qRegisterMetaType("View3DActionCommand"); qRegisterMetaTypeStreamOperators("View3DActionCommand"); + qRegisterMetaType("RequestModelNodePreviewImageCommand"); + qRegisterMetaTypeStreamOperators("RequestModelNodePreviewImageCommand"); + qRegisterMetaType>("QPairIntInt"); qRegisterMetaTypeStreamOperators>("QPairIntInt"); diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h index 39eb2618d67..841cd8561ed 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h @@ -53,6 +53,7 @@ class RemoveSharedMemoryCommand; class ChangeSelectionCommand; class InputEventCommand; class View3DActionCommand; +class RequestModelNodePreviewImageCommand; class ChangeLanguageCommand; class ChangePreviewImageSizeCommand; @@ -82,6 +83,7 @@ public: virtual void changeSelection(const ChangeSelectionCommand &command) = 0; virtual void inputEvent(const InputEventCommand &command) = 0; virtual void view3DAction(const View3DActionCommand &command) = 0; + virtual void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) = 0; virtual void changeLanguage(const ChangeLanguageCommand &command) = 0; virtual void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) = 0; diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EffectNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EffectNodeView.qml new file mode 100644 index 00000000000..9259e0d08fd --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EffectNodeView.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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 QtQuick3D 1.15 +import QtQuick3D.Effects 1.15 + +View3D { + id: root + anchors.fill: parent + environment: sceneEnv + + property Effect previewEffect + + SceneEnvironment { + id: sceneEnv + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + effects: previewEffect + } + + Node { + DirectionalLight { + eulerRotation.x: -30 + eulerRotation.y: -30 + } + + PerspectiveCamera { + z: 120 + clipFar: 1000 + clipNear: 1 + } + + Model { + id: model + source: "#Sphere" + materials: [ + DefaultMaterial { + diffuseColor: "green" + } + ] + } + } +} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/MaterialNodeView.qml new file mode 100644 index 00000000000..f52bcf4bee9 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/MaterialNodeView.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 QtQuick3D 1.15 + +View3D { + id: root + anchors.fill: parent + environment: sceneEnv + + property Material previewMaterial + + SceneEnvironment { + id: sceneEnv + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + Node { + DirectionalLight { + eulerRotation.x: -30 + eulerRotation.y: -30 + } + + PerspectiveCamera { + z: 120 + clipFar: 1000 + clipNear: 1 + } + + Model { + id: model + source: "#Sphere" + materials: previewMaterial + } + } +} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml new file mode 100644 index 00000000000..b0ed2114ea4 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml @@ -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 +import QtQuick3D.Effects 1.15 + +Item { + id: root + width: 150 + height: 150 + visible: true + + property View3D view: null + property alias contentItem: contentItem + + property var materialViewComponent + property var effectViewComponent + + function destroyView() + { + if (view) { + // Destroy is async, so make sure we don't get any more updates for the old view + _generalHelper.enableItemUpdate(view, false); + view.destroy(); + } + } + + function createViewForObject(obj, w, h) + { + width = w; + height = h; + + if (obj instanceof Material) + createViewForMaterial(obj); + else if (obj instanceof Effect) + createViewForEffect(obj); + } + + function createViewForMaterial(material) + { + if (!materialViewComponent) + materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); + + // Always recreate the view to ensure material is up to date + if (materialViewComponent.status === Component.Ready) + view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); + } + + function createViewForEffect(effect) + { + if (!effectViewComponent) + effectViewComponent = Qt.createComponent("EffectNodeView.qml"); + + // Always recreate the view to ensure effect is up to date + if (effectViewComponent.status === Component.Ready) + view = effectViewComponent.createObject(viewRect, {"previewEffect": effect}); + } + + Item { + id: contentItem + anchors.fill: parent + + Rectangle { + id: viewRect + anchors.fill: parent + + gradient: Gradient { + GradientStop { position: 1.0; color: "#222222" } + GradientStop { position: 0.0; color: "#999999" } + } + } + } +} diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 6bbaebe0864..45d5fce845d 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -1417,6 +1418,11 @@ void NodeInstanceServer::view3DAction(const View3DActionCommand &command) Q_UNUSED(command) } +void NodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) +{ + Q_UNUSED(command) +} + void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command) { setTranslationLanguage(command.language); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index 0c5810b3ec6..bd6349d29a1 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -152,6 +152,7 @@ public: void changeSelection(const ChangeSelectionCommand &command) override; void inputEvent(const InputEventCommand &command) override; void view3DAction(const View3DActionCommand &command) override; + void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) override; void changeLanguage(const ChangeLanguageCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp index a06a156e15e..dd9c42c5bdb 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp @@ -158,6 +158,12 @@ void NodeInstanceServerDispatcher::view3DAction(const View3DActionCommand &comma server->view3DAction(command); } +void NodeInstanceServerDispatcher::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) +{ + for (std::unique_ptr &server : m_servers) + server->requestModelNodePreviewImage(command); +} + void NodeInstanceServerDispatcher::changeLanguage(const ChangeLanguageCommand &command) { for (std::unique_ptr &server : m_servers) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.h index c09c629573a..03323a511d3 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.h @@ -58,6 +58,7 @@ public: void changeSelection(const ChangeSelectionCommand &command); void inputEvent(const InputEventCommand &command); void view3DAction(const View3DActionCommand &command); + void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command); void changeLanguage(const ChangeLanguageCommand &command); void changePreviewImageSize(const ChangePreviewImageSizeCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 6b6cbefa5e5..54e36f7420b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -62,6 +62,7 @@ #include "puppettocreatorcommand.h" #include "inputeventcommand.h" #include "view3dactioncommand.h" +#include "requestmodelnodepreviewimagecommand.h" #include "dummycontextobject.h" #include "../editor3d/generalhelper.h" @@ -145,13 +146,35 @@ void Qt5InformationNodeInstanceServer::createEditView3D() this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout); QObject::connect(&m_selectionChangeTimer, &QTimer::timeout, this, &Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout); - QObject::connect(&m_renderTimer, &QTimer::timeout, + QObject::connect(&m_render3DEditViewTimer, &QTimer::timeout, this, &Qt5InformationNodeInstanceServer::doRender3DEditView); + QObject::connect(&m_renderModelNode3DImageViewTimer, &QTimer::timeout, + this, &Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView); helper->setParent(m_editView3DRootItem); #endif } +void Qt5InformationNodeInstanceServer::create3DPreviewView() +{ +#ifdef QUICK3D_MODULE + // This function assumes createEditView3D() has been called previously + m_ModelNode3DImageView = new QQuickView(quickView()->engine(), quickView()); + m_ModelNode3DImageView->setFormat(quickView()->format()); + DesignerSupport::createOpenGLContext(m_ModelNode3DImageView.data()); + QQmlComponent component(engine()); + component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/ModelNode3DImageView.qml")); + m_ModelNode3DImageViewRootItem = qobject_cast(component.create()); + + if (!m_ModelNode3DImageViewRootItem) { + qWarning() << "Could not create ModelNode preview image 3D view: " << component.errors(); + return; + } + + DesignerSupport::setRootItem(m_ModelNode3DImageView, m_ModelNode3DImageViewRootItem); +#endif +} + // The selection has changed in the edit view 3D. Empty list indicates selection is cleared. void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs) { @@ -440,31 +463,37 @@ ServerNodeInstance Qt5InformationNodeInstanceServer::active3DSceneInstance() con return sceneInstance; } +void Qt5InformationNodeInstanceServer::updateNodesRecursive(QQuickItem *item) +{ + for (QQuickItem *childItem : item->childItems()) + updateNodesRecursive(childItem); + DesignerSupport::updateDirtyNode(item); +} + +QQuickItem *Qt5InformationNodeInstanceServer::getContentItemForRendering(QQuickItem *rootItem) +{ + QQuickItem *contentItem = QQmlProperty::read(rootItem, "contentItem").value(); + if (contentItem) { + designerSupport()->refFromEffectItem(contentItem, false); + QmlDesigner::Internal::QmlPrivateGate::disableNativeTextRendering(contentItem); + } + return contentItem; +} + void Qt5InformationNodeInstanceServer::render3DEditView(int count) { - m_needRender = qMax(count, m_needRender); - if (!m_renderTimer.isActive()) - m_renderTimer.start(0); + m_need3DEditViewRender = qMax(count, m_need3DEditViewRender); + if (!m_render3DEditViewTimer.isActive()) + m_render3DEditViewTimer.start(0); } // render the 3D edit view and send the result to creator process void Qt5InformationNodeInstanceServer::doRender3DEditView() { if (m_editView3DRootItem) { - if (!m_editView3DContentItem) { - m_editView3DContentItem = QQmlProperty::read(m_editView3DRootItem, "contentItem").value(); - if (m_editView3DContentItem) { - designerSupport()->refFromEffectItem(m_editView3DContentItem, false); - QmlDesigner::Internal::QmlPrivateGate::disableNativeTextRendering(m_editView3DContentItem); - } - } + if (!m_editView3DContentItem) + m_editView3DContentItem = getContentItemForRendering(m_editView3DRootItem); - std::function updateNodesRecursive; - updateNodesRecursive = [&updateNodesRecursive](QQuickItem *item) { - for (QQuickItem *childItem : item->childItems()) - updateNodesRecursive(childItem); - DesignerSupport::updateDirtyNode(item); - }; updateNodesRecursive(m_editView3DContentItem); // Fake render loop signaling to update things like QML items as 3D textures @@ -480,26 +509,69 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView() // There's no instance related to image, so instance id is -1. // Key number is selected so that it is unlikely to conflict other ImageContainer use. - const qint32 edit3DKey = 2100000000; - auto imgContainer = ImageContainer(-1, renderImage, edit3DKey); + auto imgContainer = ImageContainer(-1, renderImage, 2100000000); // send the rendered image to creator process nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::Render3DView, QVariant::fromValue(imgContainer)}); - if (m_needRender > 0) { - m_renderTimer.start(0); - --m_needRender; + if (m_need3DEditViewRender > 0) { + m_render3DEditViewTimer.start(0); + --m_need3DEditViewRender; } } } +void Qt5InformationNodeInstanceServer::renderModelNode3DImageView() +{ + if (!m_renderModelNode3DImageViewTimer.isActive()) + m_renderModelNode3DImageViewTimer.start(0); +} + +void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() +{ + if (m_ModelNode3DImageViewRootItem) { + 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()))); + + updateNodesRecursive(m_ModelNode3DImageViewContentItem); + + // Fake render loop signaling to update things like QML items as 3D textures + m_editView3D->beforeSynchronizing(); + m_editView3D->beforeRendering(); + + QSizeF size = qobject_cast(m_ModelNode3DImageViewContentItem)->size(); + QRectF renderRect(QPointF(0., 0.), size); + QImage renderImage = designerSupport()->renderImageForItem(m_ModelNode3DImageViewContentItem, + renderRect, size.toSize()); + m_editView3D->afterRendering(); + + 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); + + // send the rendered image to creator process + nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::RenderModelNodePreviewImage, + QVariant::fromValue(imgContainer)}); + } +} + Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : Qt5NodeInstanceServer(nodeInstanceClient) { m_propertyChangeTimer.setInterval(100); m_propertyChangeTimer.setSingleShot(true); m_selectionChangeTimer.setSingleShot(true); - m_renderTimer.setSingleShot(true); + m_render3DEditViewTimer.setSingleShot(true); + m_renderModelNode3DImageViewTimer.setSingleShot(true); } void Qt5InformationNodeInstanceServer::sendTokenBack() @@ -815,6 +887,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList(m_3dHelper); @@ -1237,6 +1310,15 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c render3DEditView(renderCount); } +void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) +{ + m_modelNodelPreviewImageCommand = command; + + ServerNodeInstance instance = instanceForId(m_modelNodelPreviewImageCommand.instanceId()); + if (instance.isSubclassOf("QQuick3DObject")) + renderModelNode3DImageView(); +} + void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) { Qt5NodeInstanceServer::changeAuxiliaryValues(command); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 40668da55d6..f85d974e0fa 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -29,6 +29,7 @@ #include "tokencommand.h" #include "valueschangedcommand.h" #include "changeselectioncommand.h" +#include "requestmodelnodepreviewimagecommand.h" #include #include @@ -59,6 +60,7 @@ public: void removeInstances(const RemoveInstancesCommand &command) override; void inputEvent(const InputEventCommand &command) override; void view3DAction(const View3DActionCommand &command) override; + void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) override; void changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) override; void changePropertyBindings(const ChangeBindingsCommand &command) override; void changeIds(const ChangeIdsCommand &command) override; @@ -89,6 +91,7 @@ private: void handleObjectPropertyChangeTimeout(); void handleSelectionChangeTimeout(); void createEditView3D(); + void create3DPreviewView(); void setup3DEditView(const QList &instanceList, const QHash &toolStates); void createCameraAndLightGizmos(const QList &instanceList) const; @@ -110,12 +113,20 @@ private: void removeNode3D(QObject *node); void resolveSceneRoots(); ServerNodeInstance active3DSceneInstance() const; + void updateNodesRecursive(QQuickItem *item); + QQuickItem *getContentItemForRendering(QQuickItem *rootItem); void render3DEditView(int count = 1); void doRender3DEditView(); + void renderModelNode3DImageView(); + void doRenderModelNode3DImageView(); QPointer m_editView3D; QQuickItem *m_editView3DRootItem = nullptr; QQuickItem *m_editView3DContentItem = nullptr; + QPointer m_ModelNode3DImageView; + QQuickItem *m_ModelNode3DImageViewRootItem = nullptr; + QQuickItem *m_ModelNode3DImageViewContentItem = nullptr; + RequestModelNodePreviewImageCommand m_modelNodelPreviewImageCommand; QSet m_view3Ds; QMultiHash m_3DSceneMap; // key: scene root, value: node QObject *m_active3DView = nullptr; @@ -126,12 +137,13 @@ private: QList m_tokenList; QTimer m_propertyChangeTimer; QTimer m_selectionChangeTimer; - QTimer m_renderTimer; + QTimer m_render3DEditViewTimer; + QTimer m_renderModelNode3DImageViewTimer; QVariant m_changedNode; PropertyName m_changedProperty; ChangeSelectionCommand m_lastSelectionChangeCommand; QObject *m_3dHelper = nullptr; - int m_needRender = 0; + int m_need3DEditViewRender = 0; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc index f1af244e384..02878b81b6e 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc @@ -40,6 +40,9 @@ mockfiles/AxisHelper.qml mockfiles/AxisHelperArm.qml mockfiles/Line3D.qml + mockfiles/ModelNode3DImageView.qml + mockfiles/MaterialNodeView.qml + mockfiles/EffectNodeView.qml mockfiles/meshes/arrow.mesh mockfiles/meshes/scalerod.mesh mockfiles/meshes/ring.mesh diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6a8ac1924ca..cf5ee686bfa 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -166,6 +166,7 @@ extend_qtc_plugin(QmlDesigner puppettocreatorcommand.cpp puppettocreatorcommand.h inputeventcommand.cpp inputeventcommand.h view3dactioncommand.cpp view3dactioncommand.h + requestmodelnodepreviewimagecommand.cpp requestmodelnodepreviewimagecommand.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index d9cc1521816..89151b7ea67 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -194,6 +194,54 @@ void DesignerActionManager::registerAddResourceHandler(const AddResourceHandler m_addResourceHandler.append(handler); } +QHash DesignerActionManager::modelNodePreviewHandlers() const +{ + return m_modelNodePreviewImageHandlers; +} + +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 + for (const auto &handler : qAsConst(m_modelNodePreviewImageHandlers)) { + if (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; + } + if (!op) + m_noModelNodePreviewImageHandlers.insert(node.type()); + } + return op; +} + class VisiblityModelNodeAction : public ModelNodeContextMenuAction { public: @@ -1309,6 +1357,24 @@ void DesignerActionManager::createDefaultAddResourceHandler() ModelNodeOperations::addFontToProject)); } +void DesignerActionManager::createDefaultModelNodePreviewImageHandlers() +{ + registerModelNodePreviewHandler( + ModelNodePreviewImageHandler("QtQuick.Image", + ModelNodeOperations::previewImageDataForImageNode)); + registerModelNodePreviewHandler( + ModelNodePreviewImageHandler("QtQuick3D.Texture", + ModelNodeOperations::previewImageDataForImageNode)); + registerModelNodePreviewHandler( + ModelNodePreviewImageHandler("QtQuick3D.Material", + ModelNodeOperations::previewImageDataFor3DNode)); + + // TODO - Disabled until QTBUG-86616 is fixed +// registerModelNodePreviewHandler( +// ModelNodePreviewImageHandler("QtQuick3D.Effect", +// ModelNodeOperations::previewImageDataFor3DNode)); +} + void DesignerActionManager::addDesignerAction(ActionInterface *newAction) { m_designerActions.append(QSharedPointer(newAction)); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index dd61140d19f..13d964c866c 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -27,11 +27,13 @@ #include #include "actioninterface.h" +#include "modelnode.h" #include #include #include +#include QT_BEGIN_NAMESPACE class QGraphicsItem; @@ -42,7 +44,8 @@ namespace QmlDesigner { class DesignerActionManagerView; -using AddResourceOperation = std::function; +using AddResourceOperation = std::function; +using ModelNodePreviewImageOperation = std::function; struct AddResourceHandler { @@ -64,6 +67,23 @@ public: int piority; }; +struct ModelNodePreviewImageHandler +{ +public: + ModelNodePreviewImageHandler(const TypeName &t, + ModelNodePreviewImageOperation op, + int prio = 0) + : type(t) + , operation(op) + , priority(prio) + { + } + + TypeName type; + ModelNodePreviewImageOperation operation = nullptr; + int priority = 0; +}; + class DesignerActionToolBar : public Utils::StyledBar { public: @@ -87,6 +107,8 @@ public: void createDefaultDesignerActions(); void createDefaultAddResourceHandler(); + void createDefaultModelNodePreviewImageHandlers(); + DesignerActionManagerView *view(); DesignerActionToolBar *createToolBar(QWidget *parent = nullptr) const; @@ -102,6 +124,11 @@ public: QList addResourceHandler() const; void registerAddResourceHandler(const AddResourceHandler &handler); + QHash modelNodePreviewHandlers() const; + void registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler); + bool hasModelNodePreviewHandler(const ModelNode &node) const; + ModelNodePreviewImageOperation modelNodePreviewOperation(const ModelNode &node) const; + private: void addTransitionEffectAction(const TypeName &typeName); void addCustomTransitionEffectAction(); @@ -109,6 +136,8 @@ private: QList > m_designerActions; DesignerActionManagerView *m_designerActionManagerView; QList m_addResourceHandler; + QMultiHash m_modelNodePreviewImageHandlers; + mutable QSet m_noModelNodePreviewImageHandlers; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 7453807a835..129917dba1d 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include @@ -78,6 +80,7 @@ #include #include #include +#include #include #include @@ -1518,6 +1521,110 @@ void removeGroup(const SelectionContext &selectionContext) }); } +struct ImageData { + QDateTime time; + QImage image; + QString type; + QString id; + QString info; +}; +static QHash> imageDataMap; + +static QVariant imageDataToVariant(const ImageData &imageData) +{ + if (!imageData.image.isNull()) { + QVariantMap map; + map.insert("type", imageData.type); + map.insert("image", QVariant::fromValue(imageData.image)); + map.insert("id", imageData.id); + map.insert("info", imageData.info); + return map; + } + return {}; +} + +QVariant previewImageDataForImageNode(const ModelNode &modelNode) +{ + // Images on file system can be cached globally as they are found by absolute paths + QHash &localDataMap = imageDataMap[{}]; + + VariantProperty prop = modelNode.variantProperty("source"); + QString imageSource = prop.value().toString(); + QFileInfo imageFi(imageSource); + if (imageFi.isRelative()) + imageSource = QmlDesignerPlugin::instance()->documentManager().currentFilePath().toFileInfo().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 previewImageDataFor3DNode(const ModelNode &modelNode) +{ + QFileInfo docFi = QmlDesignerPlugin::instance()->documentManager().currentFilePath().toFileInfo(); + QHash &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); + } + modelNode.model()->nodeInstanceView()->requestModelNodePreviewImage(modelNode); + + return imageDataToVariant(imageData); +} + +void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image) +{ + QFileInfo docFi = QmlDesignerPlugin::instance()->documentManager().currentFilePath().toFileInfo(); + QString docPath = docFi.absoluteFilePath(); + if (imageDataMap.contains(docPath)) { + QHash &localDataMap = imageDataMap[docPath]; + if (localDataMap.contains(modelNode.id())) + localDataMap[modelNode.id()].image = image; + } +} + } // namespace ModelNodeOperations } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 96dedf352ad..e23e29d75a9 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -27,6 +27,8 @@ #include "selectioncontext.h" +QT_FORWARD_DECLARE_CLASS(QImage) + namespace QmlDesigner { namespace ModelNodeOperations { @@ -84,5 +86,10 @@ void selectFlowEffect(const SelectionContext &selectionContext); void mergeWithTemplate(const SelectionContext &selectionContext); void removeGroup(const SelectionContext &selectionContext); +// ModelNodePreviewImageOperations +QVariant previewImageDataFor3DNode(const ModelNode &modelNode); +QVariant previewImageDataForImageNode(const ModelNode &modelNode); +void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image); + } // namespace ModelNodeOperationso } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigator.qrc b/src/plugins/qmldesigner/components/navigator/navigator.qrc index aa28a4aaeec..fca836a09be 100644 --- a/src/plugins/qmldesigner/components/navigator/navigator.qrc +++ b/src/plugins/qmldesigner/components/navigator/navigator.qrc @@ -12,5 +12,6 @@ export_checked@2x.png export_unchecked.png export_unchecked@2x.png + tooltip_placeholder.png diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index f5f420f0f7e..3312a16adb8 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include @@ -53,7 +54,6 @@ #include #include #include -#include #include @@ -181,6 +181,7 @@ static void reparentModelNodeToNodeProperty(NodeAbstractProperty &parentProperty NavigatorTreeModel::NavigatorTreeModel(QObject *parent) : QAbstractItemModel(parent) { + m_actionManager = &QmlDesignerPlugin::instance()->viewManager().designerActionManager(); } NavigatorTreeModel::~NavigatorTreeModel() = default; @@ -201,13 +202,6 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const if (role == ItemIsVisibleRole) //independent of column return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; - auto hasImageToolTip = [modelNode]() -> bool { - if (modelNode.isValid() && modelNode.metaInfo().isValid()) - return modelNode.type() == "QtQuick.Image" || modelNode.type() == "QtQuick3D.Texture"; - else - return false; - }; - if (index.column() == 0) { if (role == Qt::DisplayRole) { return modelNode.displayName(); @@ -229,7 +223,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const } if (modelNode.metaInfo().isValid()) { - if (hasImageToolTip()) + if (m_actionManager->hasModelNodePreviewHandler(modelNode)) return {}; // Images have special tooltip popup, so suppress regular one else return modelNode.type(); @@ -239,62 +233,9 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const } else if (role == ToolTipImageRole) { if (currentQmlObjectNode.hasError()) // Error already shown on regular tooltip return {}; - - if (hasImageToolTip()) { - VariantProperty prop = modelNode.variantProperty("source"); - QString imageSource = prop.value().toString(); - QFileInfo fi(imageSource); - if (fi.isRelative()) - imageSource = QmlDesignerPlugin::instance()->documentManager().currentFilePath().toFileInfo().dir().absoluteFilePath(imageSource); - fi = QFileInfo(imageSource); - QDateTime modified = fi.lastModified(); - - struct ImageData { - QDateTime time; - QImage image; - QString type; - QString id; - QString info; - }; - - static QHash toolTipImageMap; - - ImageData imageData; - bool reload = true; - if (toolTipImageMap.contains(imageSource)) { - imageData = toolTipImageMap[imageSource]; - if (modified == imageData.time) - reload = false; - } - - if (reload) { - QImage originalImage; - originalImage.load(imageSource); - if (!originalImage.isNull()) { - imageData.image = originalImage.scaled(150, 150, Qt::KeepAspectRatio); - double imgSize = double(fi.size()); - imageData.type = QStringLiteral("%1 (%2)").arg(QString::fromLatin1(modelNode.type())).arg(fi.suffix()); - imageData.id = modelNode.id(); - static QStringList units({tr("B"), tr("KB"), tr("MB"), 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]); - toolTipImageMap.insert(imageSource, imageData); - } - } - if (!imageData.image.isNull()) { - QVariantMap map; - map.insert("type", imageData.type); - map.insert("image", QVariant::fromValue(imageData.image)); - map.insert("id", imageData.id); - map.insert("info", imageData.info); - return map; - } - } + auto op = m_actionManager->modelNodePreviewOperation(modelNode); + if (op) + return op(modelNode); } else if (role == ModelNodeRole) { return QVariant::fromValue(modelNode); } @@ -898,4 +839,9 @@ void NavigatorTreeModel::resetModel() endResetModel(); } +void NavigatorTreeModel::updateToolTipImage(const ModelNode &node, const QImage &image) +{ + emit toolTipImageUpdated(node.id(), image); +} + } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index 3289a19b31a..6ab81960ca7 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -32,12 +32,14 @@ #include #include +#include namespace QmlDesigner { class Model; class NavigatorView; class ModelNode; +class DesignerActionManager; class NavigatorTreeModel : public QAbstractItemModel, public NavigatorModelInterface { @@ -91,6 +93,11 @@ public: void setFilter(bool showOnlyVisibleItems) override; void resetModel() override; + void updateToolTipImage(const ModelNode &node, const QImage &image); + +signals: + void toolTipImageUpdated(const QString &id, const QImage &image) const; + private: void moveNodesInteractive(NodeAbstractProperty &parentProperty, const QList &modelNodes, int targetIndex, bool executeInTransaction = true); @@ -102,6 +109,7 @@ private: QPointer m_view; mutable QHash m_nodeIndexHash; bool m_showOnlyVisibleItems = true; + DesignerActionManager *m_actionManager = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp index 33cf991e36d..183686a26b4 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp @@ -168,6 +168,12 @@ NavigatorTreeView::NavigatorTreeView(QWidget *parent) setMinimumWidth(240); setRootIsDecorated(false); setIndentation(indentation() * 0.5); + + m_toolTipHideTimer.setSingleShot(true); + connect(&m_toolTipHideTimer, &QTimer::timeout, [this]() { + if (m_previewToolTip && m_previewToolTip->isVisible()) + m_previewToolTip->hide(); + }); } void NavigatorTreeView::drawSelectionBackground(QPainter *painter, const QStyleOption &option) @@ -187,22 +193,30 @@ bool NavigatorTreeView::viewportEvent(QEvent *event) QVariantMap imgMap = navModel->data(index, ToolTipImageRole).toMap(); if (!imgMap.isEmpty()) { - if (!m_previewToolTip) + m_toolTipHideTimer.stop(); + if (!m_previewToolTip) { m_previewToolTip = new PreviewToolTip(QApplication::activeWindow()); + connect(navModel, &NavigatorTreeModel::toolTipImageUpdated, + [this](const QString &id, const QImage &image) { + if (m_previewToolTip && m_previewToolTip->id() == id) + m_previewToolTip->setImage(image); + }); + } m_previewToolTip->setId(imgMap["id"].toString()); m_previewToolTip->setType(imgMap["type"].toString()); m_previewToolTip->setInfo(imgMap["info"].toString()); m_previewToolTip->setImage(imgMap["image"].value()); - m_previewToolTip->move(helpEvent->pos()); + m_previewToolTip->move(m_previewToolTip->parentWidget()->mapFromGlobal(helpEvent->globalPos()) + + QPoint(15, 15)); if (!m_previewToolTip->isVisible()) m_previewToolTip->show(); } else if (m_previewToolTip) { - m_previewToolTip->hide(); + m_toolTipHideTimer.start(0); } } } else if (event->type() == QEvent::Leave) { if (m_previewToolTip) - m_previewToolTip->hide(); + m_toolTipHideTimer.start(500); } return QTreeView::viewportEvent(event); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.h b/src/plugins/qmldesigner/components/navigator/navigatortreeview.h index 2d2a11f899c..443840f9b31 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.h @@ -26,6 +26,7 @@ #pragma once #include +#include namespace QmlDesigner { @@ -42,5 +43,6 @@ public: private: PreviewToolTip *m_previewToolTip = nullptr; + QTimer m_toolTipHideTimer; }; } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 2058aef198b..1cc16d73f40 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -229,6 +230,11 @@ void NavigatorView::enableWidget() m_widget->enableNavigator(); } +void NavigatorView::modelNodePreviewImageChanged(const ModelNode &node, const QImage &image) +{ + m_treeModel->updateToolTipImage(node, image); +} + ModelNode NavigatorView::modelNodeForIndex(const QModelIndex &modelIndex) const { return modelIndex.model()->data(modelIndex, ModelNodeRole).value(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index c35900059fd..49605e145c3 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -38,6 +38,7 @@ class QTreeView; class QItemSelection; class QModelIndex; class QAbstractItemModel; +class QImage; QT_END_NAMESPACE namespace QmlDesigner { @@ -95,6 +96,8 @@ public: void disableWidget() override; void enableWidget() override; + void modelNodePreviewImageChanged(const ModelNode &node, const QImage &image) override; + private: ModelNode modelNodeForIndex(const QModelIndex &modelIndex) const; void changeSelection(const QItemSelection &selected, const QItemSelection &deselected); diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp index fffd9b8992c..a1ec983a6ea 100644 --- a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp +++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp @@ -67,4 +67,9 @@ void PreviewToolTip::setImage(const QImage &image) m_ui->imageLabel->setPixmap(QPixmap::fromImage(image)); } +QString PreviewToolTip::id() const +{ + return m_ui->idLabel->text(); +} + } diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.h b/src/plugins/qmldesigner/components/navigator/previewtooltip.h index f9ee98d2c96..081d496adbe 100644 --- a/src/plugins/qmldesigner/components/navigator/previewtooltip.h +++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.h @@ -46,6 +46,8 @@ public: void setInfo(const QString &info); void setImage(const QImage &image); + QString id() const; + private: Ui::PreviewToolTip *m_ui; }; diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui index 99263375a5f..7b970ebe0a2 100644 --- a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui +++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui @@ -7,7 +7,7 @@ 0 0 400 - 160 + 166 @@ -18,8 +18,8 @@ - 200 - 150 + 300 + 140 @@ -90,8 +90,8 @@ - 140 - 140 + 150 + 150 diff --git a/src/plugins/qmldesigner/components/navigator/tooltip_placeholder.png b/src/plugins/qmldesigner/components/navigator/tooltip_placeholder.png new file mode 100644 index 00000000000..19625bd6913 Binary files /dev/null and b/src/plugins/qmldesigner/components/navigator/tooltip_placeholder.png differ diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 2412ed9700b..b4475603385 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -186,6 +186,7 @@ public: void emitInstanceToken(const QString &token, int number, const QVector &nodeVector); void emitRenderImage3DChanged(const QImage &image); void emitUpdateActiveScene3D(const QVariantMap &sceneState); + void emitModelNodelPreviewImageChanged(const ModelNode &node, const QImage &image); void sendTokenToInstances(const QString &token, int number, const QVector &nodeVector); @@ -245,6 +246,7 @@ public: virtual void renderImage3DChanged(const QImage &image); virtual void updateActiveScene3D(const QVariantMap &sceneState); + virtual void modelNodePreviewImageChanged(const ModelNode &node, const QImage &image); void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index eaf4fc08e2a..1372f47b3b7 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -138,6 +138,7 @@ public: void sendInputEvent(QInputEvent *e) const; void view3DAction(const View3DActionCommand &command); + void requestModelNodePreviewImage(const ModelNode &node); void edit3DViewResized(const QSize &size) const; void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index 026d5872339..173adc1b001 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -293,6 +294,11 @@ void NodeInstanceServerProxy::view3DAction(const View3DActionCommand &command) writeCommand(QVariant::fromValue(command)); } +void NodeInstanceServerProxy::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) +{ + writeCommand(QVariant::fromValue(command)); +} + void NodeInstanceServerProxy::changeLanguage(const ChangeLanguageCommand &command) { writeCommand(QVariant::fromValue(command)); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h index 2efc7ea8a46..0177bd6a14a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h @@ -81,6 +81,7 @@ public: void benchmark(const QString &message) override; void inputEvent(const InputEventCommand &command) override; void view3DAction(const View3DActionCommand &command) override; + void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) override; void changeLanguage(const ChangeLanguageCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index dc379bd312f..a7de6ae5de1 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -71,6 +71,7 @@ #include "valueschangedcommand.h" #include "variantproperty.h" #include "view3dactioncommand.h" +#include "requestmodelnodepreviewimagecommand.h" #include #include @@ -1487,6 +1488,16 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand } else if (command.type() == PuppetToCreatorCommand::ActiveSceneChanged) { const auto sceneState = qvariant_cast(command.data()); emitUpdateActiveScene3D(sceneState); + } else if (command.type() == PuppetToCreatorCommand::RenderModelNodePreviewImage) { + ImageContainer container = qvariant_cast(command.data()); + QImage image = container.image(); + if (hasModelNodeForInternalId(container.instanceId()) && !image.isNull()) { + auto node = modelNodeForInternalId(container.instanceId()); + if (node.isValid()) { + image.setDevicePixelRatio(2.); + emitModelNodelPreviewImageChanged(node, image); + } + } } } @@ -1511,6 +1522,20 @@ void NodeInstanceView::view3DAction(const View3DActionCommand &command) m_nodeInstanceServer->view3DAction(command); } +void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node) +{ + if (node.isValid()) { + auto instance = instanceForModelNode(node); + if (instance.isValid()) { + m_nodeInstanceServer->requestModelNodePreviewImage( + RequestModelNodePreviewImageCommand( + instance.instanceId(), + QSize(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS, + Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS))); + } + } +} + void NodeInstanceView::edit3DViewResized(const QSize &size) const { m_nodeInstanceServer->update3DViewState(Update3dViewStateCommand(size)); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 2c53d5a505c..6224ab50c0d 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -382,6 +382,10 @@ void AbstractView::updateActiveScene3D(const QVariantMap & /*sceneState*/) { } +void AbstractView::modelNodePreviewImageChanged(const ModelNode & /*node*/, const QImage & /*image*/) +{ +} + QList AbstractView::toModelNodeList(const QList &nodeList) const { return QmlDesigner::toModelNodeList(nodeList, const_cast(this)); @@ -766,6 +770,12 @@ void AbstractView::emitUpdateActiveScene3D(const QVariantMap &sceneState) model()->d->notifyUpdateActiveScene3D(sceneState); } +void AbstractView::emitModelNodelPreviewImageChanged(const ModelNode &node, const QImage &image) +{ + if (model()) + model()->d->notifyModelNodePreviewImageChanged(node, image); +} + void AbstractView::emitRewriterEndTransaction() { if (model()) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 8ff23b5248e..751b7a5136d 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -26,6 +26,7 @@ #include "model.h" #include "model_p.h" #include +#include #include "internalnode_p.h" #include "invalidpropertyexception.h" #include "invalidargumentexception.h" @@ -700,6 +701,16 @@ void ModelPrivate::notifyUpdateActiveScene3D(const QVariantMap &sceneState) } } +void ModelPrivate::notifyModelNodePreviewImageChanged(const ModelNode &node, const QImage &image) +{ + ModelNodeOperations::updatePreviewImageForNode(node, image); + + for (const QPointer &view : qAsConst(m_viewList)) { + Q_ASSERT(view != nullptr); + view->modelNodePreviewImageChanged(node, image); + } +} + void ModelPrivate::notifyRewriterBeginTransaction() { bool resetModel = false; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index dc74ca5a062..2d6669aeb9e 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -160,6 +160,7 @@ public: void notifyRenderImage3DChanged(const QImage &image); void notifyUpdateActiveScene3D(const QVariantMap &sceneState); + void notifyModelNodePreviewImageChanged(const ModelNode &node, const QImage &image); void setDocumentMessages(const QList &errors, const QList &warnings); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index bd7d09af316..e57a329239e 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -224,6 +224,7 @@ void DesignModeWidget::setup() auto &actionManager = viewManager().designerActionManager(); actionManager.createDefaultDesignerActions(); actionManager.createDefaultAddResourceHandler(); + actionManager.createDefaultModelNodePreviewImageHandlers(); actionManager.polishActions(); auto settings = Core::ICore::settings(QSettings::UserScope); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 0f5b06c5161..950cfa43122 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -75,6 +75,8 @@ const char QT_QUICK_3D_MODULE_NAME[] = "QtQuick3D"; // Menus const char M_WINDOW_WORKSPACES[] = "QmlDesigner.Menu.Window.Workspaces"; +const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150; + namespace Internal { enum { debug = 0 }; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index cb40c589352..5c7ec4efc67 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -188,6 +188,8 @@ Project { "commands/inputeventcommand.h", "commands/view3dactioncommand.cpp", "commands/view3dactioncommand.h", + "commands/requestmodelnodepreviewimagecommand.cpp", + "commands/requestmodelnodepreviewimagecommand.h", "container/addimportcontainer.cpp", "container/addimportcontainer.h", "container/idcontainer.cpp", diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index f3f6a66303f..36be18f8297 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -51,6 +51,7 @@ extend_qtc_executable(qml2puppet puppettocreatorcommand.cpp puppettocreatorcommand.h inputeventcommand.cpp inputeventcommand.h view3dactioncommand.cpp view3dactioncommand.h + requestmodelnodepreviewimagecommand.cpp requestmodelnodepreviewimagecommand.h valueschangedcommand.cpp captureddatacommand.h scenecreatedcommand.h diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs index 4d135b0cde0..f38a6c8215e 100644 --- a/src/tools/qml2puppet/qml2puppet.qbs +++ b/src/tools/qml2puppet/qml2puppet.qbs @@ -116,6 +116,8 @@ QtcTool { "commands/inputeventcommand.h", "commands/view3dactioncommand.cpp", "commands/view3dactioncommand.h", + "commands/requestmodelnodepreviewimagecommand.cpp", + "commands/requestmodelnodepreviewimagecommand.h", "commands/captureddatacommand.h", "commands/scenecreatedcommand.h", "container/addimportcontainer.cpp",