From 90d9cb36f3ae756fe08ddfbf74756ada62df6918 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 10 Sep 2020 16:02:31 +0300 Subject: [PATCH] QmlDesigner: Add navigator preview tooltip for materials Request 3D preview image for material instances from puppet to show on tooltip. Support for effect previews is also done, but handler for it is not registered, as effects cannot be shared between windows due to issue QTBUG-86616. Also refactored the preview image support out of navigator to make it more accessible by other components. Change-Id: Ie08ba218f929660c2e43d39578997a5a1a883efd Reviewed-by: Thomas Hartmann --- .../qml/qmlpuppet/commands/commands.pri | 6 +- .../commands/puppettocreatorcommand.h | 2 +- .../requestmodelnodepreviewimagecommand.cpp | 73 ++++++++++ .../requestmodelnodepreviewimagecommand.h | 60 ++++++++ .../instances/nodeinstanceclientproxy.cpp | 9 ++ .../instances/nodeinstanceclientproxy.h | 2 + .../nodeinstanceserverinterface.cpp | 4 + .../interfaces/nodeinstanceserverinterface.h | 2 + .../qmlpuppet/mockfiles/EffectNodeView.qml | 65 +++++++++ .../qmlpuppet/mockfiles/MaterialNodeView.qml | 59 ++++++++ .../mockfiles/ModelNode3DImageView.qml | 96 +++++++++++++ .../instances/nodeinstanceserver.cpp | 6 + .../qml2puppet/instances/nodeinstanceserver.h | 1 + .../nodeinstanceserverdispatcher.cpp | 6 + .../instances/nodeinstanceserverdispatcher.h | 1 + .../qt5informationnodeinstanceserver.cpp | 128 ++++++++++++++---- .../qt5informationnodeinstanceserver.h | 16 ++- share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc | 3 + src/plugins/qmldesigner/CMakeLists.txt | 1 + .../componentcore/designeractionmanager.cpp | 66 +++++++++ .../componentcore/designeractionmanager.h | 31 ++++- .../componentcore/modelnodeoperations.cpp | 107 +++++++++++++++ .../componentcore/modelnodeoperations.h | 7 + .../components/navigator/navigator.qrc | 1 + .../navigator/navigatortreemodel.cpp | 76 ++--------- .../components/navigator/navigatortreemodel.h | 8 ++ .../navigator/navigatortreeview.cpp | 22 ++- .../components/navigator/navigatortreeview.h | 2 + .../components/navigator/navigatorview.cpp | 6 + .../components/navigator/navigatorview.h | 3 + .../components/navigator/previewtooltip.cpp | 5 + .../components/navigator/previewtooltip.h | 2 + .../components/navigator/previewtooltip.ui | 10 +- .../navigator/tooltip_placeholder.png | Bin 0 -> 4525 bytes .../designercore/include/abstractview.h | 2 + .../designercore/include/nodeinstanceview.h | 1 + .../instances/nodeinstanceserverproxy.cpp | 6 + .../instances/nodeinstanceserverproxy.h | 1 + .../instances/nodeinstanceview.cpp | 25 ++++ .../designercore/model/abstractview.cpp | 10 ++ .../qmldesigner/designercore/model/model.cpp | 11 ++ .../qmldesigner/designercore/model/model_p.h | 1 + src/plugins/qmldesigner/designmodewidget.cpp | 1 + .../qmldesigner/qmldesignerconstants.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + src/tools/qml2puppet/CMakeLists.txt | 1 + src/tools/qml2puppet/qml2puppet.qbs | 2 + 47 files changed, 848 insertions(+), 103 deletions(-) create mode 100644 share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.cpp create mode 100644 share/qtcreator/qml/qmlpuppet/commands/requestmodelnodepreviewimagecommand.h create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/EffectNodeView.qml create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/MaterialNodeView.qml create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/ModelNode3DImageView.qml create mode 100644 src/plugins/qmldesigner/components/navigator/tooltip_placeholder.png 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 0000000000000000000000000000000000000000..19625bd6913d0013c4a3ba44e48e6d4875c1fa08 GIT binary patch literal 4525 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 &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",