diff --git a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h index 1eb6ba5018d..c69d478875d 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include "instancecontainer.h" @@ -58,7 +60,8 @@ public: const QString &language, QSize captureImageMinimumSize, QSize captureImageMaximumSize, - qint32 stateInstanceId) + qint32 stateInstanceId, + const QList &edit3dBackgroundColor) : instances(instanceContainer) , reparentInstances(reparentContainer) , ids(idVector) @@ -74,6 +77,7 @@ public: , captureImageMinimumSize(captureImageMinimumSize) , captureImageMaximumSize(captureImageMaximumSize) , stateInstanceId{stateInstanceId} + , edit3dBackgroundColor{edit3dBackgroundColor} {} friend QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) @@ -93,6 +97,7 @@ public: out << command.stateInstanceId; out << command.captureImageMinimumSize; out << command.captureImageMaximumSize; + out << command.edit3dBackgroundColor; return out; } @@ -114,6 +119,7 @@ public: in >> command.stateInstanceId; in >> command.captureImageMinimumSize; in >> command.captureImageMaximumSize; + in >> command.edit3dBackgroundColor; return in; } @@ -134,6 +140,7 @@ public: QSize captureImageMinimumSize; QSize captureImageMaximumSize; qint32 stateInstanceId = 0; + QList edit3dBackgroundColor; }; QDebug operator<<(QDebug debug, const CreateSceneCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp index 211fe6d54bd..c32f7b0ffe9 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp +++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp @@ -30,24 +30,27 @@ namespace QmlDesigner { -View3DActionCommand::View3DActionCommand(Type type, bool enable) +View3DActionCommand::View3DActionCommand(Type type, const QVariant &value) : m_type(type) - , m_enabled(enable) - , m_position(0) + , m_value(value) { } View3DActionCommand::View3DActionCommand(int pos) : m_type(ParticlesSeek) - , m_enabled(true) - , m_position(pos) + , m_value(pos) { } bool View3DActionCommand::isEnabled() const { - return m_enabled; + return m_value.toBool(); +} + +QVariant View3DActionCommand::value() const +{ + return m_value; } View3DActionCommand::Type View3DActionCommand::type() const @@ -57,29 +60,32 @@ View3DActionCommand::Type View3DActionCommand::type() const int View3DActionCommand::position() const { - return m_position; + bool ok = false; + int result = m_value.toInt(&ok); + if (!ok) { + qWarning() << "View3DActionCommand: returning a position that is not int; command type = " + << m_type; + } + + return result; } QDataStream &operator<<(QDataStream &out, const View3DActionCommand &command) { - out << qint32(command.isEnabled()); + out << command.value(); out << qint32(command.type()); - out << qint32(command.position()); return out; } QDataStream &operator>>(QDataStream &in, View3DActionCommand &command) { - qint32 enabled; + QVariant value; qint32 type; - qint32 pos; - in >> enabled; + in >> value; in >> type; - in >> pos; - command.m_enabled = bool(enabled); + command.m_value = value; command.m_type = View3DActionCommand::Type(type); - command.m_position = pos; return in; } @@ -88,7 +94,7 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command) { return debug.nospace() << "View3DActionCommand(type: " << command.m_type << "," - << command.m_enabled << ")"; + << command.m_value << ")\n"; } } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h index cb34c253f9e..cc3611df764 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h @@ -26,6 +26,7 @@ #pragma once #include +#include namespace QmlDesigner { @@ -55,20 +56,22 @@ public: ParticlesPlay, ParticlesRestart, ParticlesSeek, + SelectBackgroundColor, + ResetBackgroundColor, }; - explicit View3DActionCommand(Type type, bool enable); + explicit View3DActionCommand(Type type, const QVariant &value); View3DActionCommand() = default; bool isEnabled() const; + QVariant value() const; Type type() const; int position() const; private: Type m_type = Empty; - bool m_enabled = false; - int m_position = 0; + QVariant m_value; protected: View3DActionCommand(int pos); diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc index e8d66548c32..05459423349 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc @@ -32,7 +32,6 @@ mockfiles/qt6/FadeHandle.qml mockfiles/qt6/HelperGrid.qml mockfiles/qt6/IconGizmo.qml - mockfiles/qt6/IconRenderer3D.qml mockfiles/qt6/LightGizmo.qml mockfiles/qt6/LightIconGizmo.qml mockfiles/qt6/LightModel.qml diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp index 413906267ac..a8b3c319408 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp @@ -142,6 +142,7 @@ void NodeInstanceServerInterface::registerCommands() registerCommand("View3DActionCommand"); registerCommand("RequestModelNodePreviewImageCommand"); registerCommand>("QPairIntInt"); + registerCommand>("QColorList"); registerCommand("ChangeLanguageCommand"); registerCommand("ChangePreviewImageSizeCommand"); registerCommand("CapturedDataCommand"); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml index 904a062a623..9a95ca34b98 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml @@ -45,6 +45,8 @@ Item { property bool usePerspective: true property bool globalOrientation: false property alias contentItem: contentItem + property color backgroundGradientColorStart: "#222222" + property color backgroundGradientColorEnd: "#999999" enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -212,6 +214,15 @@ Item { cameraControl.alignView(selectedNodes); } + function updateViewStates(viewStates) + { + if ("selectBackgroundColor" in viewStates) { + var color = viewStates.selectBackgroundColor + backgroundGradientColorStart = color[0]; + backgroundGradientColorEnd = color[1]; + } + } + // If resetToDefault is true, tool states not specifically set to anything will be reset to // their default state. function updateToolStates(toolStates, resetToDefault) @@ -329,6 +340,15 @@ Item { function handleObjectClicked(object, multi) { + if (object instanceof View3D) { + // View3D can be the resolved pick target in case the 3D editor is showing content + // of a component that has View3D as root. In that case locking is resolved on C++ side + // and we ignore multiselection. + selectObjects([]); + selectionChanged([object]); + return; + } + var clickedObject; // Click on locked object is treated same as click on empty space @@ -721,8 +741,8 @@ Item { anchors.fill: parent gradient: Gradient { - GradientStop { position: 1.0; color: "#222222" } - GradientStop { position: 0.0; color: "#999999" } + GradientStop { position: 1.0; color: backgroundGradientColorStart } + GradientStop { position: 0.0; color: backgroundGradientColorEnd } } MouseArea { @@ -740,7 +760,7 @@ Item { handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), mouse.modifiers & Qt.ControlModifier); - if (pickResult.objectHit) { + if (pickResult.objectHit && pickResult.objectHit instanceof Node) { if (transformMode === EditView3D.TransformMode.Move) freeDraggerArea = moveGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Rotate) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index 06320524bf3..c36d8c227c3 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -32,7 +32,7 @@ View3D { property Material previewMaterial - function fitToViewPort() + function fitToViewPort(closeUp) { // No need to zoom this view, this is here just to avoid runtime warnings } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index de3e3ee431c..a6d5c6b1db5 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -41,6 +41,8 @@ Item { property var modelViewComponent property var nodeViewComponent + property bool closeUp: false + function destroyView() { previewObject = null; @@ -96,7 +98,15 @@ Item { function fitToViewPort() { - view.fitToViewPort(); + view.fitToViewPort(closeUp); + } + + // Enables/disables icon mode. When in icon mode, camera is zoomed bit closer to reduce margins + // and the background is removed, in order to generate a preview suitable for library icons. + function setIconMode(enable) + { + closeUp = enable; + backgroundRect.visible = !enable; } View3D { @@ -108,10 +118,15 @@ Item { id: contentItem anchors.fill: parent - Rectangle { + Item { id: viewRect anchors.fill: parent + } + Rectangle { + id: backgroundRect + anchors.fill: parent + z: -1 gradient: Gradient { GradientStop { position: 1.0; color: "#222222" } GradientStop { position: 0.0; color: "#999999" } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml index e61a9a8fb25..5c8b9e1cd72 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml @@ -34,11 +34,11 @@ View3D { property Model sourceModel - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin - _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root, + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml index 91008abb251..e8931719d7f 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml @@ -32,11 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml index 25297f8fc67..228154a9a49 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml @@ -46,6 +46,8 @@ Item { property bool usePerspective: true property bool globalOrientation: false property alias contentItem: contentItem + property color backgroundGradientColorStart: "#222222" + property color backgroundGradientColorEnd: "#999999" enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -206,6 +208,15 @@ Item { cameraControl.alignView(selectedNodes); } + function updateViewStates(viewStates) + { + if ("selectBackgroundColor" in viewStates) { + var color = viewStates.selectBackgroundColor + backgroundGradientColorStart = color[0]; + backgroundGradientColorEnd = color[1]; + } + } + // If resetToDefault is true, tool states not specifically set to anything will be reset to // their default state. function updateToolStates(toolStates, resetToDefault) @@ -329,6 +340,15 @@ Item { function handleObjectClicked(object, multi) { + if (object instanceof View3D) { + // View3D can be the resolved pick target in case the 3D editor is showing content + // of a component that has View3D as root. In that case locking is resolved on C++ side + // and we ignore multiselection. + selectObjects([]); + selectionChanged([object]); + return; + } + var clickedObject; // Click on locked object is treated same as click on empty space @@ -866,8 +886,8 @@ Item { anchors.fill: parent gradient: Gradient { - GradientStop { position: 1.0; color: "#222222" } - GradientStop { position: 0.0; color: "#999999" } + GradientStop { position: 1.0; color: backgroundGradientColorStart } + GradientStop { position: 0.0; color: backgroundGradientColorEnd } } MouseArea { @@ -892,7 +912,7 @@ Item { handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier); - if (pickResult.objectHit) { + if (pickResult.objectHit && pickResult.objectHit instanceof Node) { if (transformMode === EditView3D.TransformMode.Move) freeDraggerArea = moveGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Rotate) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml deleted file mode 100644 index 623bf21cdec..00000000000 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** 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 6.0 -import QtQuick3D 6.0 - -Item { - id: viewRoot - width: 1024 - height: 1024 - visible: true - - property alias view3D: view3D - property alias camPos: viewCamera.position - - function setSceneToBox() - { - selectionBox.targetNode = view3D.importScene; - } - - function fitAndHideBox() - { - cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true, true); - if (cameraControl._zoomFactor < 0.1) - view3D.importScene.scale = view3D.importScene.scale.times(10); - if (cameraControl._zoomFactor > 10) - view3D.importScene.scale = view3D.importScene.scale.times(0.1); - - selectionBox.visible = false; - } - - View3D { - id: view3D - camera: viewCamera - environment: sceneEnv - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.VeryHigh - } - - PerspectiveCamera { - id: viewCamera - position: Qt.vector3d(-200, 200, 200) - eulerRotation: Qt.vector3d(-45, -45, 0) - } - - DirectionalLight { - rotation: viewCamera.rotation - } - - SelectionBox { - id: selectionBox - view3D: view3D - geometryName: "SB" - } - - EditCameraController { - id: cameraControl - camera: view3D.camera - view3d: view3D - ignoreToolState: true - } - } -} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index aae5891e082..ed5cfea56f4 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -32,7 +32,7 @@ View3D { property Material previewMaterial - function fitToViewPort() + function fitToViewPort(closeUp) { // No need to zoom this view, this is here just to avoid runtime warnings } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 4bd07e57d12..5caac0047c4 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -41,6 +41,8 @@ Item { property var modelViewComponent property var nodeViewComponent + property bool closeUp: false + function destroyView() { previewObject = null; @@ -96,17 +98,30 @@ Item { function fitToViewPort() { - view.fitToViewPort(); + view.fitToViewPort(closeUp); + } + + // Enables/disables icon mode. When in icon mode, camera is zoomed bit closer to reduce margins + // and the background is removed, in order to generate a preview suitable for library icons. + function setIconMode(enable) + { + closeUp = enable; + backgroundRect.visible = !enable; } Item { id: contentItem anchors.fill: parent - Rectangle { + Item { id: viewRect anchors.fill: parent + } + Rectangle { + id: backgroundRect + anchors.fill: parent + z: -1 gradient: Gradient { GradientStop { position: 1.0; color: "#222222" } GradientStop { position: 0.0; color: "#999999" } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml index 19b3c4c35d7..1762e3c9a4d 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml @@ -34,11 +34,11 @@ View3D { property Model sourceModel - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml index a48eb665191..7bbfbf281ac 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml @@ -32,11 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index 3b774c3f4a6..dd21622b5dc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -308,7 +308,7 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul // and recalculating bounds for every frame is not a problem. void GeneralHelper::calculateNodeBoundsAndFocusCamera( QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance) + float defaultLookAtDistance, bool closeUp) { QVector3D minBounds; QVector3D maxBounds; @@ -317,7 +317,9 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( QVector3D extents = maxBounds - minBounds; QVector3D lookAt = minBounds + (extents / 2.f); - float maxExtent = qMax(extents.x(), qMax(extents.y(), extents.z())); + float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x()) + + qreal(extents.y()) * qreal(extents.y()) + + qreal(extents.z()) * qreal(extents.z())); // Reset camera position to default zoom QMatrix4x4 m = camera->sceneTransform(); @@ -328,9 +330,27 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( camera->setPosition(lookAt + newLookVector); - float newZoomFactor = maxExtent / 725.f; // Divisor taken from focusNodesToCamera function + // CloseUp divisor is used for icon generation, where we can allow some extreme models to go + // slightly out of bounds for better results generally. The other divisor is used for other + // previews, where the image is larger to begin with and we would also like some margin + // between preview edge and the rendered model, so we can be more conservative with the zoom. + // The divisor values are empirically selected to provide nice result. + float divisor = closeUp ? 1250.f : 1050.f; + float newZoomFactor = maxExtent / divisor; zoomCamera(viewPort, camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); + + if (auto perspectiveCamera = qobject_cast(camera)) { + // Fix camera near/far clips in case we are dealing with extreme zooms + const float cameraDist = qAbs((camera->position() - lookAt).length()); + const float minDist = cameraDist - (maxExtent / 2.f); + const float maxDist = cameraDist + (maxExtent / 2.f); + if (minDist < perspectiveCamera->clipNear() || maxDist > perspectiveCamera->clipFar()) { + perspectiveCamera->setClipNear(minDist * 0.99); + perspectiveCamera->setClipFar(maxDist * 1.01); + } + + } } // Aligns any cameras found in nodes list to a camera. @@ -418,15 +438,15 @@ QQuick3DPickResult GeneralHelper::pickViewAt(QQuick3DViewport *view, float posX, return QQuick3DPickResult(); } -QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode) +QObject *GeneralHelper::resolvePick(QQuick3DNode *pickNode) { if (pickNode) { - // Check if the picked node actually specifies another node as the pick target + // Check if the picked node actually specifies another object as the pick target QVariant componentVar = pickNode->property("_pickTarget"); if (componentVar.isValid()) { - auto componentNode = componentVar.value(); - if (componentNode) - return componentNode; + auto componentObj = componentVar.value(); + if (componentObj) + return componentObj; } } return pickNode; @@ -823,12 +843,13 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec if (auto childNode = qobject_cast(child)) { QVector3D newMinBounds = minBounds; QVector3D newMaxBounds = maxBounds; - hasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true); + bool childHasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true); // Ignore any subtrees that do not have Model in them as we don't need those // for visual bounds calculations - if (hasModel) { + if (childHasModel) { minBoundsVec << newMinBounds; maxBoundsVec << newMaxBounds; + hasModel = true; } } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index 562848c7720..5bb1fa1662f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -77,7 +77,7 @@ public: bool closeUp = false); Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance); + float defaultLookAtDistance, bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint); @@ -85,7 +85,7 @@ public: Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property, const QVariant& value); Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY); - Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode); + Q_INVOKABLE QObject *resolvePick(QQuick3DNode *pickNode); Q_INVOKABLE bool isLocked(QQuick3DNode *node) const; Q_INVOKABLE bool isHidden(QQuick3DNode *node) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp index 5f448b90da5..1f24aaca8f8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp @@ -77,9 +77,8 @@ void import3D(const QString &sourceAsset, const QString &outDir, const QString & } } - // Allow little time for file operations to finish - QTimer::singleShot(2000, nullptr, []() { - qApp->exit(0); + QTimer::singleShot(0, nullptr, []() { + qApp->quit(); }); } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 8400c3d4474..41c67f70b68 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1561,6 +1561,10 @@ void NodeInstanceServer::handleInstanceHidden(const ServerNodeInstance &/*instan { } +void NodeInstanceServer::handlePickTarget(const ServerNodeInstance &/*instance*/) +{ +} + void NodeInstanceServer::setupState(qint32 stateInstanceId) { if (hasInstanceForId(stateInstanceId)) { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index ba51f5e5888..42f345ee4e5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -226,6 +226,7 @@ public: virtual void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors); virtual void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors); + virtual void handlePickTarget(const ServerNodeInstance &instance); virtual QImage grabWindow() = 0; virtual QImage grabItem(QQuickItem *item) = 0; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 8059a240a6c..c6acc5e5f9d 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -553,7 +553,18 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob auto obj = object.value(); if (obj) { ServerNodeInstance instance = instanceForObject(obj); - instanceList << instance; + // If instance is a View3D, make sure it is not locked + bool locked = false; + if (instance.isSubclassOf("QQuick3DViewport")) { + locked = instance.internalInstance()->isLockedInEditor(); + auto parentInst = instance.parent(); + while (!locked && parentInst.isValid()) { + locked = parentInst.internalInstance()->isLockedInEditor(); + parentInst = parentInst.parent(); + } + } + if (!locked) + instanceList << instance; #ifdef QUICK3D_PARTICLES_MODULE if (!skipSystemDeselect) { auto particleSystem = parentParticleSystem(instance.internalObject()); @@ -1452,8 +1463,9 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout() void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() { -#ifdef QUICK3D_MODULE for (auto obj : std::as_const(m_dynamicObjectConstructors)) { +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) +#ifdef QUICK3D_MODULE auto handleHiding = [this](QQuick3DNode *node) -> bool { if (node && hasInstanceForObject(node)) { ServerNodeInstance instance = instanceForObject(node); @@ -1468,8 +1480,22 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() if (auto pickTarget = obj->property("_pickTarget").value()) handleHiding(pickTarget); } - } #endif +#else + auto handlePicking = [this](QObject *object) -> bool { + if (object && hasInstanceForObject(object)) { + ServerNodeInstance instance = instanceForObject(object); + handlePickTarget(instance); + return true; + } + return false; + }; + if (!handlePicking(obj)) { + if (auto pickTarget = obj->property("_pickTarget").value()) + handlePicking(pickTarget); + } +#endif + } m_dynamicObjectConstructors.clear(); } @@ -1932,6 +1958,12 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com #ifdef IMPORT_QUICK3D_ASSETS QTimer::singleShot(0, this, &Qt5InformationNodeInstanceServer::resolveImportSupport); #endif + + if (!command.edit3dBackgroundColor.isEmpty()) { + View3DActionCommand backgroundColorCommand(View3DActionCommand::SelectBackgroundColor, + QVariant::fromValue(command.edit3dBackgroundColor)); + view3DAction(backgroundColorCommand); + } } void Qt5InformationNodeInstanceServer::sendChildrenChangedCommand(const QList &childList) @@ -2147,18 +2179,19 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c if (!m_editView3DSetupDone) return; - QVariantMap updatedState; + QVariantMap updatedToolState; + QVariantMap updatedViewState; int renderCount = 1; switch (command.type()) { case View3DActionCommand::MoveTool: - updatedState.insert("transformMode", 0); + updatedToolState.insert("transformMode", 0); break; case View3DActionCommand::RotateTool: - updatedState.insert("transformMode", 1); + updatedToolState.insert("transformMode", 1); break; case View3DActionCommand::ScaleTool: - updatedState.insert("transformMode", 2); + updatedToolState.insert("transformMode", 2); break; case View3DActionCommand::FitToView: QMetaObject::invokeMethod(m_editView3DData.rootItem, "fitToView"); @@ -2170,38 +2203,42 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignViewToCamera"); break; case View3DActionCommand::SelectionModeToggle: - updatedState.insert("selectionMode", command.isEnabled() ? 1 : 0); + updatedToolState.insert("selectionMode", command.isEnabled() ? 1 : 0); break; case View3DActionCommand::CameraToggle: - updatedState.insert("usePerspective", command.isEnabled()); + updatedToolState.insert("usePerspective", command.isEnabled()); // It can take a couple frames to properly update icon gizmo positions renderCount = 2; break; case View3DActionCommand::OrientationToggle: - updatedState.insert("globalOrientation", command.isEnabled()); + updatedToolState.insert("globalOrientation", command.isEnabled()); break; case View3DActionCommand::EditLightToggle: - updatedState.insert("showEditLight", command.isEnabled()); + updatedToolState.insert("showEditLight", command.isEnabled()); break; case View3DActionCommand::ShowGrid: - updatedState.insert("showGrid", command.isEnabled()); + updatedToolState.insert("showGrid", command.isEnabled()); break; case View3DActionCommand::ShowSelectionBox: - updatedState.insert("showSelectionBox", command.isEnabled()); + updatedToolState.insert("showSelectionBox", command.isEnabled()); break; case View3DActionCommand::ShowIconGizmo: - updatedState.insert("showIconGizmo", command.isEnabled()); + updatedToolState.insert("showIconGizmo", command.isEnabled()); break; case View3DActionCommand::ShowCameraFrustum: - updatedState.insert("showCameraFrustum", command.isEnabled()); + updatedToolState.insert("showCameraFrustum", command.isEnabled()); break; + case View3DActionCommand::SelectBackgroundColor: { + updatedViewState.insert("selectBackgroundColor", command.value()); + break; + } #ifdef QUICK3D_PARTICLES_MODULE case View3DActionCommand::ShowParticleEmitter: - updatedState.insert("showParticleEmitter", command.isEnabled()); + updatedToolState.insert("showParticleEmitter", command.isEnabled()); break; case View3DActionCommand::ParticlesPlay: m_particleAnimationPlaying = command.isEnabled(); - updatedState.insert("particlePlay", command.isEnabled()); + updatedToolState.insert("particlePlay", command.isEnabled()); if (m_particleAnimationPlaying) { m_particleAnimationDriver->play(); m_particleAnimationDriver->setSeekerEnabled(false); @@ -2227,12 +2264,17 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c break; } - if (!updatedState.isEmpty()) { + if (!updatedToolState.isEmpty()) { QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateToolStates", - Q_ARG(QVariant, updatedState), + Q_ARG(QVariant, updatedToolState), Q_ARG(QVariant, QVariant::fromValue(false))); } + if (!updatedViewState.isEmpty()) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateViewStates", + Q_ARG(QVariant, updatedViewState)); + } + render3DEditView(renderCount); } @@ -2332,9 +2374,9 @@ void Qt5InformationNodeInstanceServer::handleInstanceLocked(const ServerNodeInst } } #else - Q_UNUSED(instance); - Q_UNUSED(enable); - Q_UNUSED(checkAncestors); + Q_UNUSED(instance) + Q_UNUSED(enable) + Q_UNUSED(checkAncestors) #endif } @@ -2388,6 +2430,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst // Don't override explicit hide in children handleInstanceHidden(quick3dInstance, edit3dHidden || isInstanceHidden, false); } else { +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) // Children of components do not have instances, but will still need to be pickable std::function checkChildren; checkChildren = [&](QQuick3DNode *checkNode) { @@ -2402,9 +2445,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst value = QVariant::fromValue(node); // Specify the actual pick target with dynamic property checkModel->setProperty("_pickTarget", value); -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) checkModel->setPickable(!edit3dHidden); -#endif } else { auto checkRepeater = qobject_cast(checkNode); auto checkLoader = qobject_cast(checkNode); @@ -2436,13 +2477,102 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst }; if (auto childNode = qobject_cast(childItem)) checkChildren(childNode); +#endif } } } #else - Q_UNUSED(instance); - Q_UNUSED(enable); - Q_UNUSED(checkAncestors); + Q_UNUSED(instance) + Q_UNUSED(enable) + Q_UNUSED(checkAncestors) +#endif +} + +void Qt5InformationNodeInstanceServer::handlePickTarget(const ServerNodeInstance &instance) +{ +#if defined(QUICK3D_MODULE) && (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) + // Picking is dependent on hidden status prior to global picking support (<6.2.1), so it is + // handled in handleInstanceHidden() method in those builds + + if (!ViewConfig::isQuick3DMode()) + return; + + QObject *obj = instance.internalObject(); + QList childItems; + if (auto node = qobject_cast(obj)) { + childItems = node->childItems(); + } else if (auto view = qobject_cast(obj)) { + // We only need to handle views that are components + // View is a component if its scene is not an instance and scene has node children that + // have no instances + QQuick3DNode *node = view->scene(); + if (node) { + if (hasInstanceForObject(node)) + return; + childItems = node->childItems(); + bool allHaveInstance = true; + for (const auto &childItem : childItems) { + if (qobject_cast(childItem) && !hasInstanceForObject(childItem)) { + allHaveInstance = false; + break; + } + } + if (allHaveInstance) + return; + } + } else { + return; + } + + for (auto childItem : qAsConst(childItems)) { + if (!hasInstanceForObject(childItem)) { + // Children of components do not have instances, but will still need to be pickable + // and redirect their pick to the component + std::function checkChildren; + checkChildren = [&](QQuick3DNode *checkNode) { + const auto childItems = checkNode->childItems(); + for (auto child : childItems) { + if (auto childNode = qobject_cast(child)) + checkChildren(childNode); + } + if (auto checkModel = qobject_cast(checkNode)) { + // Specify the actual pick target with dynamic property + checkModel->setProperty("_pickTarget", QVariant::fromValue(obj)); + } else { + auto checkRepeater = qobject_cast(checkNode); + auto checkLoader = qobject_cast(checkNode); +#if defined(QUICK3D_ASSET_UTILS_MODULE) + auto checkRunLoader = qobject_cast(checkNode); + if (checkRepeater || checkLoader || checkRunLoader) { +#else + if (checkRepeater || checkLoader) { +#endif + // Repeaters/loaders may not yet have created their children, so we set + // _pickTarget on them and connect the notifier. + if (checkNode->property("_pickTarget").isNull()) { + if (checkRepeater) { + QObject::connect(checkRepeater, &QQuick3DRepeater::objectAdded, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); +#if defined(QUICK3D_ASSET_UTILS_MODULE) + } else if (checkRunLoader) { + QObject::connect(checkRunLoader, &QQuick3DRuntimeLoader::statusChanged, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); +#endif + } else { + QObject::connect(checkLoader, &QQuick3DLoader::loaded, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); + } + } + checkNode->setProperty("_pickTarget", QVariant::fromValue(obj)); + } + } + }; + if (auto childNode = qobject_cast(childItem)) + checkChildren(childNode); + } + } +#else + Q_UNUSED(instance) #endif } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index b6375076b66..291b2c3eecd 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -80,6 +80,7 @@ public: void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override; void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override; + void handlePickTarget(const ServerNodeInstance &instance) override; bool isInformationServer() const override; void handleDynamicAddObject(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp index 589f57cb5f1..cae64ad04c1 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp @@ -57,6 +57,9 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer // In case this is the scene root, we need to create a dummy View3D for the scene // in preview puppets if (instanceId() == 0 && (!nodeInstanceServer()->isInformationServer())) { + nodeInstanceServer()->quickWindow()->setDefaultAlphaBuffer(true); + nodeInstanceServer()->quickWindow()->setColor(Qt::transparent); + auto helper = new QmlDesigner::Internal::GeneralHelper(); engine()->rootContext()->setContextProperty("_generalHelper", helper); @@ -199,6 +202,14 @@ QQuickItem *Quick3DRenderableNodeInstance::contentItem() const return m_dummyRootView; } +void Quick3DRenderableNodeInstance::setPropertyVariant(const PropertyName &name, + const QVariant &value) +{ + if (m_dummyRootView && name == "isLibraryIcon") + QMetaObject::invokeMethod(m_dummyRootView, "setIconMode", Q_ARG(QVariant, value)); + ObjectNodeInstance::setPropertyVariant(name, value); +} + Qt5NodeInstanceServer *Quick3DRenderableNodeInstance::qt5NodeInstanceServer() const { return qobject_cast(nodeInstanceServer()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h index ae03cb1b90f..208144cb52f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h @@ -52,6 +52,7 @@ public: QList stateInstances() const override; QQuickItem *contentItem() const override; + void setPropertyVariant(const PropertyName &name, const QVariant &value) override; protected: explicit Quick3DRenderableNodeInstance(QObject *node); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp index a4fdde97ec2..2f4be3e8e9b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp @@ -323,8 +323,12 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags()); +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) // Handle hidden state to initialize pickable state nodeInstanceServer->handleInstanceHidden(instance, false, false); +#else + nodeInstanceServer->handlePickTarget(instance); +#endif return instance; } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 48ec9fa470a..b9d9f323169 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -41,6 +41,8 @@ StudioControls.ComboBox { onModelChanged: colorLogic.invalidate() + hasActiveDrag: comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag + // This is available in all editors. onValueTypeChanged: { @@ -83,16 +85,15 @@ StudioControls.ComboBox { onEntered: (drag) => { dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0] - - drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.isSupportedDrop(dropArea.assetPath) - comboBox.hasActiveDrag = drag.accepted + drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag + comboBox.hasActiveHoverDrag = drag.accepted } - onExited: comboBox.hasActiveDrag = false + onExited: comboBox.hasActiveHoverDrag = false onDropped: { comboBox.backendValue.commitDrop(dropArea.assetPath) - comboBox.hasActiveDrag = false + comboBox.hasActiveHoverDrag = false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml index fbd4f530626..70cbdf000ec 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml @@ -39,7 +39,8 @@ T.ComboBox { && myComboBox.enabled property bool edit: myComboBox.activeFocus && myComboBox.editable property bool open: comboBoxPopup.opened - property bool hasActiveDrag: false + property bool hasActiveDrag: false // an item that can be dropped on the combobox is being dragged + property bool hasActiveHoverDrag: false // an item that can be dropped on the combobox is being hovered on the combobox property bool dirty: false // user modification flag @@ -49,6 +50,9 @@ T.ComboBox { property alias textInput: comboBoxInput + property int borderWidth: myComboBox.hasActiveHoverDrag ? StudioTheme.Values.borderHover + : StudioTheme.Values.border + signal compressedActivated(int index, int reason) enum ActivatedReason { EditingFinished, Other } @@ -57,7 +61,7 @@ T.ComboBox { height: StudioTheme.Values.defaultControlHeight leftPadding: actionIndicator.width - rightPadding: popupIndicator.width + StudioTheme.Values.border + rightPadding: popupIndicator.width + myComboBox.borderWidth font.pixelSize: StudioTheme.Values.myFontSize wheelEnabled: false @@ -87,6 +91,7 @@ T.ComboBox { myControl: myComboBox text: myComboBox.editText + borderWidth: myComboBox.borderWidth onEditingFinished: { comboBoxInput.deselect() @@ -108,16 +113,16 @@ T.ComboBox { myControl: myComboBox myPopup: myComboBox.popup x: comboBoxInput.x + comboBoxInput.width - y: StudioTheme.Values.border - width: StudioTheme.Values.checkIndicatorWidth - StudioTheme.Values.border - height: StudioTheme.Values.checkIndicatorHeight - (StudioTheme.Values.border * 2) + y: myComboBox.borderWidth + width: StudioTheme.Values.checkIndicatorWidth - myComboBox.borderWidth + height: StudioTheme.Values.checkIndicatorHeight - myComboBox.borderWidth * 2 } background: Rectangle { id: comboBoxBackground color: StudioTheme.Values.themeControlBackground border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border + border.width: myComboBox.borderWidth x: actionIndicator.width width: myComboBox.width - actionIndicator.width height: myComboBox.height @@ -144,7 +149,7 @@ T.ComboBox { width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding - (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth + 2 : 0) // TODO Magic number - height: StudioTheme.Values.height - 2 * StudioTheme.Values.border + height: StudioTheme.Values.height - 2 * myComboBox.borderWidth padding: 0 enabled: model.enabled === undefined ? true : model.enabled @@ -198,9 +203,9 @@ T.ComboBox { popup: T.Popup { id: comboBoxPopup - x: actionIndicator.width + StudioTheme.Values.border + x: actionIndicator.width + myComboBox.borderWidth y: myComboBox.height - width: myComboBox.width - actionIndicator.width - (StudioTheme.Values.border * 2) + width: myComboBox.width - actionIndicator.width - myComboBox.borderWidth * 2 // TODO Setting the height on the popup solved the problem with the popup of height 0, // but it has the problem that it sometimes extend over the border of the actual window // and is then cut off. @@ -208,7 +213,7 @@ T.ComboBox { + comboBoxPopup.bottomPadding, myComboBox.Window.height - topMargin - bottomMargin, StudioTheme.Values.maxComboBoxPopupHeight) - padding: StudioTheme.Values.border + padding: myComboBox.borderWidth margins: 0 // If not defined margin will be -1 closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside @@ -252,8 +257,9 @@ T.ComboBox { PropertyChanges { target: comboBoxBackground color: StudioTheme.Values.themeControlBackground - border.color: hasActiveDrag ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeControlOutline + border.color: myComboBox.hasActiveDrag ? StudioTheme.Values.themeInteraction + : StudioTheme.Values.themeControlOutline + border.width: myComboBox.borderWidth } }, // This state is intended for ComboBoxes which aren't editable, but have focus e.g. via diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml index de7dcc1f8c3..c6b91dc1ad0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml @@ -34,6 +34,7 @@ TextInput { property bool edit: textInput.activeFocus property bool hover: mouseArea.containsMouse && textInput.enabled + property int borderWidth: StudioTheme.Values.border z: 2 font: myControl.font @@ -55,11 +56,11 @@ TextInput { Rectangle { id: textInputBackground - x: StudioTheme.Values.border - y: StudioTheme.Values.border + x: textInput.borderWidth + y: textInput.borderWidth z: -1 width: textInput.width - height: StudioTheme.Values.height - (StudioTheme.Values.border * 2) + height: StudioTheme.Values.height - textInput.borderWidth * 2 color: StudioTheme.Values.themeControlBackground border.width: 0 } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 75d21a8e29b..d9bf43db746 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -86,6 +86,7 @@ QtObject { property real marginTopBottom: 4 property real border: 1 + property real borderHover: 3 property real maxComboBoxPopupHeight: Math.round(300 * values.scaleFactor) property real maxTextAreaPopupHeight: Math.round(150 * values.scaleFactor) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index ae3719243e6..9d60367997a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -17,21 +17,23 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, { "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": @@ -243,21 +245,24 @@ "trKey": "Qt 5", "value": "({ - 'TargetQuickVersion': '2.15' + 'TargetQuickVersion': '2.15', + 'TargetQuick3DVersion': '1.15' })" }, { "trKey": "Qt 6.2", "value": "({ - 'TargetQuickVersion': '6.2' + 'TargetQuickVersion': '6.2', + 'TargetQuick3DVersion': '6.2' })" }, { "trKey": "Qt 6.3", "value": "({ - 'TargetQuickVersion': '6.3' + 'TargetQuickVersion': '6.3', + 'TargetQuick3DVersion': '6.3' })" } ] diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 51acc18af26..3e7ef11beb7 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -20,20 +20,20 @@ { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('QtQuickVersion') === '' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "DefaultStyle", "value": "%{JS: value('QtQuickVersion') === '' ? 'Basic' : 'Default'}" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "DefaultStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? 'Basic' : 'Default'}" }, + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index f687b2ce385..20a3baba9c7 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -20,17 +20,19 @@ { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": @@ -235,7 +237,6 @@ "data": { "index": 1, - "items": "items": [ { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index d12fc38bdc4..3117c7e0544 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -18,19 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, - { "key": "UIClassName", "value": "Screen01" }, - { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index 1b76c290359..cb083e84f4b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -18,17 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index 19e5865fb83..1c1137afddb 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -18,17 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp index 4de1a778d83..1677fd892cc 100644 --- a/src/libs/utils/launchersocket.cpp +++ b/src/libs/utils/launchersocket.cpp @@ -67,10 +67,7 @@ public: , m_stdErr(stdErr) {} QByteArray stdOut() const { return m_stdOut; } QByteArray stdErr() const { return m_stdErr; } - void mergeWith(LauncherReadyReadSignal *newSignal) { - m_stdOut += newSignal->stdOut(); - m_stdErr += newSignal->stdErr(); - } + private: QByteArray m_stdOut; QByteArray m_stdErr; @@ -203,31 +200,6 @@ void CallerHandle::appendSignal(LauncherSignal *newSignal) QMutexLocker locker(&m_mutex); QTC_ASSERT(isCalledFromLaunchersThread(), return); - - // TODO: we might assert if the caller's state is proper, e.g. - // start signal can't appear if we are in Running or NotRunning state, - // or finish signal can't appear if we are in NotRunning or Starting state, - // or readyRead signal can't appear if we are in NotRunning or Starting state, - // or error signal can't appear if we are in NotRunning state - // or FailedToStart error signal can't appear if we are in Running state - // or other than FailedToStart error signal can't appear if we are in Starting state. - if (!m_signals.isEmpty()) { - LauncherSignal *lastSignal = m_signals.last(); - - QTC_ASSERT(lastSignal->signalType() != SignalType::Done, - qWarning() << "Buffering new signal for process" << m_command - << "while the last done() signal wasn't flushed yet."); - - // Merge ReadyRead signals into one. - if (lastSignal->signalType() == SignalType::ReadyRead - && newSignal->signalType() == SignalType::ReadyRead) { - LauncherReadyReadSignal *lastRead = static_cast(lastSignal); - LauncherReadyReadSignal *newRead = static_cast(newSignal); - lastRead->mergeWith(newRead); - delete newRead; - return; - } - } m_signals.append(newSignal); } diff --git a/src/plugins/languageclient/languageclientfunctionhint.cpp b/src/plugins/languageclient/languageclientfunctionhint.cpp index 319aad22338..1e0cd62cfe3 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.cpp +++ b/src/plugins/languageclient/languageclientfunctionhint.cpp @@ -103,6 +103,7 @@ IAssistProposal *FunctionHintProcessor::perform(const AssistInterface *interface void FunctionHintProcessor::cancel() { + QTC_ASSERT(m_client, return); if (running()) { m_client->cancelRequest(*m_currentRequest); m_client->removeAssistProcessor(this); @@ -112,6 +113,7 @@ void FunctionHintProcessor::cancel() void FunctionHintProcessor::handleSignatureResponse(const SignatureHelpRequest::Response &response) { + QTC_ASSERT(m_client, setAsyncProposalAvailable(nullptr); return); m_currentRequest.reset(); if (auto error = response.error()) m_client->log(*error); diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index eb6db977357..654da4d43a4 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -165,6 +165,7 @@ extend_qtc_plugin(QmlDesigner edit3dcanvas.cpp edit3dcanvas.h edit3dactions.cpp edit3dactions.h edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h + backgroundcolorselection.cpp backgroundcolorselection.h edit3d.qrc ) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 221f9ec0f4c..7cf91158d65 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -81,30 +81,20 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event) if (obj == m_assetsWidget.data()) QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut"); } else if (event->type() == QMouseEvent::MouseMove) { - if (!m_assetsToDrag.isEmpty()) { + if (!m_assetsToDrag.isEmpty() && !m_model.isNull()) { QMouseEvent *me = static_cast(event); if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) { - auto drag = new QDrag(this); - drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); QMimeData *mimeData = new QMimeData; mimeData->setData(Constants::MIME_TYPE_ASSETS, m_assetsToDrag.join(',').toUtf8()); - drag->setMimeData(mimeData); - drag->exec(); - drag->deleteLater(); - + m_model->startDrag(mimeData, + m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); m_assetsToDrag.clear(); } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { m_assetsToDrag.clear(); - QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); - if (view) { - NavigatorWidget *navView = qobject_cast(view); - if (navView) { - navView->setDragType(""); - navView->update(); - } - } + if (m_model) + m_model->endDrag(); } return QObject::eventFilter(obj, event); diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp new file mode 100644 index 00000000000..76db41a177e --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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 "backgroundcolorselection.h" + +#include +#include +#include +#include + +using namespace QmlDesigner; + +namespace { +QList readBackgroundColorConfiguration() +{ + QVariant var = QmlDesigner::DesignerSettings::getValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); + + if (!var.isValid()) + return {}; + + auto colorNameList = var.value>(); + QTC_ASSERT(colorNameList.size() == 2, return {}); + + return {colorNameList[0], colorNameList[1]}; +} + +void setBackgroundColorConfiguration(const QList &colorConfig) +{ + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor, + QVariant::fromValue(colorConfig)); + view->view3DAction(cmd); +} + +void saveBackgroundColorConfiguration(const QList &colorConfig) +{ + QList colorsSaved = {colorConfig[0].name(), colorConfig[1].name()}; + QmlDesigner::DesignerSettings::setValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, + QVariant::fromValue(colorsSaved)); +} + +} // namespace + +QColorDialog *BackgroundColorSelection::createDialog(QWidget *parent) +{ + auto dialog = new QColorDialog(parent); + + dialog->setModal(true); + dialog->setAttribute(Qt::WA_DeleteOnClose); + + const QList oldColorConfig = readBackgroundColorConfiguration(); + + dialog->show(); + + QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, [](const QColor &color) { + setBackgroundColorConfiguration({color, color}); + }); + + QObject::connect(dialog, &QColorDialog::colorSelected, dialog, [](const QColor &color) { + saveBackgroundColorConfiguration({color, color}); + }); + + if (!oldColorConfig.isEmpty()) { + QObject::connect(dialog, &QColorDialog::rejected, dialog, [oldColorConfig]() { + setBackgroundColorConfiguration(oldColorConfig); + }); + } + + return dialog; +} + +void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *parent) +{ + if (m_dialog) + return; + + m_dialog = BackgroundColorSelection::createDialog(parent); + QTC_ASSERT(m_dialog, return); + + QObject::connect(m_dialog, &QWidget::destroyed, m_dialog, [&]() { + m_dialog = nullptr; + }); +} diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h new file mode 100644 index 00000000000..d8832f40fda --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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 + +namespace QmlDesigner { +class BackgroundColorSelection : public QObject +{ + Q_OBJECT + +public: + explicit BackgroundColorSelection(QObject *parent = nullptr) + : QObject{parent} + {} + + static void showBackgroundColorSelectionWidget(QWidget *parent); + +private: + static QColorDialog *createDialog(QWidget *parent); + inline static QColorDialog *m_dialog = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3d.qrc b/src/plugins/qmldesigner/components/edit3d/edit3d.qrc index bf042b645b2..5784ac0e446 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3d.qrc +++ b/src/plugins/qmldesigner/components/edit3d/edit3d.qrc @@ -44,5 +44,7 @@ images/align_camera_on@2x.png images/align_view_on.png images/align_view_on@2x.png + images/color_palette.png + images/color_palette@2x.png diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index c5240a0cbd6..76343584c9c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -48,9 +48,10 @@ Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, void Edit3DActionTemplate::actionTriggered(bool b) { - if (m_type != View3DActionCommand::Empty) { - QmlDesignerPlugin::instance()->viewManager().nodeInstanceView() - ->view3DAction(View3DActionCommand(m_type, b)); + if (m_type != View3DActionCommand::Empty && m_type != View3DActionCommand::SelectBackgroundColor) { + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(m_type, b); + view->view3DAction(cmd); } if (m_action) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 5fccdbea9c5..eca26840aa4 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -42,6 +42,8 @@ #include #include +#include + #include #include @@ -336,6 +338,32 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_G), true, true, {}, {}, nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); + SelectionContextOperation showBackgroundColorSelection = [this](const SelectionContext &) { + BackgroundColorSelection::showBackgroundColorSelectionWidget(edit3DWidget()); + }; + + m_backgroundColorSelectionAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, View3DActionCommand::SelectBackgroundColor, + QCoreApplication::translate("SelectBackgroundColorAction", "Select Background color"), + {}, false, false, {}, {}, showBackgroundColorSelection, + QCoreApplication::translate("SelectBackgroundColorAction", "Choose a color for the background.")); + + m_resetBackgroundColorAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, View3DActionCommand::ResetBackgroundColor, + QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color"), + {}, false, false, {}, {}, [](const SelectionContext &) { + QList colors = {QRgb(0x222222), QRgb(0x999999)}; + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor, QVariant::fromValue(colors)); + view->view3DAction(cmd); + + QList colorsToSave = {colors[0].name(), colors[1].name()}; + QmlDesigner::DesignerSettings::setValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, + QVariant::fromValue(colorsToSave)); + }, + QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color to the default value.")); + m_showSelectionBoxAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionCommand::ShowSelectionBox, QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"), @@ -438,6 +466,29 @@ void Edit3DView::createEdit3DActions() QKeySequence(), false, false, Utils::Icons::EYE_OPEN_TOOLBAR.icon(), {}, visibilityTogglesTrigger); + SelectionContextOperation backgroundColorActionsTrigger = [this](const SelectionContext &) { + if (!edit3DWidget()->backgroundColorMenu()) + return; + + QPoint pos; + const auto &actionWidgets = m_backgrondColorMenuAction->action()->associatedWidgets(); + for (auto actionWidget : actionWidgets) { + if (auto button = qobject_cast(actionWidget)) { + pos = button->mapToGlobal(QPoint(0, 0)); + break; + } + } + + edit3DWidget()->showBackgroundColorMenu(!edit3DWidget()->backgroundColorMenu()->isVisible(), + pos); + }; + + m_backgrondColorMenuAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, View3DActionCommand::Empty, + QCoreApplication::translate("BackgroundColorMenuActions", "Background Color Actions"), + QKeySequence(), false, false, Icons::COLOR_PALETTE.icon(), + {}, backgroundColorActionsTrigger); + m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -455,6 +506,8 @@ void Edit3DView::createEdit3DActions() m_leftActions << m_alignViewAction; m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction; + m_leftActions << nullptr; + m_leftActions << m_backgrondColorMenuAction; m_rightActions << m_particleViewModeAction; m_rightActions << m_particlesPlayAction; @@ -467,6 +520,9 @@ void Edit3DView::createEdit3DActions() m_visibilityToggleActions << m_showIconGizmoAction; m_visibilityToggleActions << m_showCameraFrustumAction; m_visibilityToggleActions << m_showParticleEmitterAction; + + m_backgroundColorActions << m_backgroundColorSelectionAction; + m_backgroundColorActions << m_resetBackgroundColorAction; } QVector Edit3DView::leftActions() const @@ -484,6 +540,11 @@ QVector Edit3DView::visibilityToggleActions() const return m_visibilityToggleActions; } +QVector Edit3DView::backgroundColorActions() const +{ + return m_backgroundColorActions; +} + void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 40e21708734..e5cb2aba51b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -74,6 +74,7 @@ public: QVector leftActions() const; QVector rightActions() const; QVector visibilityToggleActions() const; + QVector backgroundColorActions() const; void setSeeker(SeekerSlider *slider); void addQuick3DImport(); @@ -88,6 +89,7 @@ private: QVector m_leftActions; QVector m_rightActions; QVector m_visibilityToggleActions; + QVector m_backgroundColorActions; Edit3DAction *m_selectionModeAction = nullptr; Edit3DAction *m_moveToolAction = nullptr; Edit3DAction *m_rotateToolAction = nullptr; @@ -99,6 +101,8 @@ private: Edit3DAction *m_orientationModeAction = nullptr; Edit3DAction *m_editLightAction = nullptr; Edit3DAction *m_showGridAction = nullptr; + Edit3DAction *m_backgroundColorSelectionAction = nullptr; + Edit3DAction *m_resetBackgroundColorAction = nullptr; Edit3DAction *m_showSelectionBoxAction = nullptr; Edit3DAction *m_showIconGizmoAction = nullptr; Edit3DAction *m_showCameraFrustumAction = nullptr; @@ -108,6 +112,7 @@ private: Edit3DAction *m_particlesPlayAction = nullptr; Edit3DAction *m_particlesRestartAction = nullptr; Edit3DAction *m_visibilityTogglesAction = nullptr; + Edit3DAction *m_backgrondColorMenuAction = nullptr; SeekerSlider *m_seeker = nullptr; int particlemode; ModelCache m_canvasCache; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 72cb0ec21c8..6f685123cfd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -141,6 +141,9 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : m_visibilityTogglesMenu = new Edit3DVisibilityTogglesMenu(this); handleActions(view->visibilityToggleActions(), m_visibilityTogglesMenu, false); + m_backgroundColorMenu = new Edit3DVisibilityTogglesMenu(this); + handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); + view->setSeeker(seeker); seeker->setToolTip(QLatin1String("Seek particle system time when paused.")); @@ -201,6 +204,21 @@ void Edit3DWidget::showVisibilityTogglesMenu(bool show, const QPoint &pos) m_visibilityTogglesMenu->close(); } +QMenu *Edit3DWidget::backgroundColorMenu() const +{ + return m_backgroundColorMenu.data(); +} + +void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) +{ + if (m_backgroundColorMenu.isNull()) + return; + if (show) + m_backgroundColorMenu->popup(pos); + else + m_backgroundColorMenu->close(); +} + void Edit3DWidget::linkActivated(const QString &link) { Q_UNUSED(link) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 5a4ed48e28e..7d40fd54715 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -51,6 +51,9 @@ public: QMenu *visibilityTogglesMenu() const; void showVisibilityTogglesMenu(bool show, const QPoint &pos); + QMenu *backgroundColorMenu() const; + void showBackgroundColorMenu(bool show, const QPoint &pos); + protected: void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; void dropEvent(QDropEvent *dropEvent) override; @@ -65,6 +68,7 @@ private: QPointer m_toolBox; Core::IContext *m_context = nullptr; QPointer m_visibilityTogglesMenu; + QPointer m_backgroundColorMenu; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/images/color_palette.png b/src/plugins/qmldesigner/components/edit3d/images/color_palette.png new file mode 100644 index 00000000000..8d5035b3658 Binary files /dev/null and b/src/plugins/qmldesigner/components/edit3d/images/color_palette.png differ diff --git a/src/plugins/qmldesigner/components/edit3d/images/color_palette@2x.png b/src/plugins/qmldesigner/components/edit3d/images/color_palette@2x.png new file mode 100644 index 00000000000..4eebbce313c Binary files /dev/null and b/src/plugins/qmldesigner/components/edit3d/images/color_palette@2x.png differ diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 7e22efe016f..e3aae0cb7d5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -401,7 +401,6 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) qmlInfo.append(outDir.relativeFilePath(qmlIt.filePath())); qmlInfo.append('\n'); - // Generate item library icon for qml file based on root component QFile qmlFile(qmlIt.filePath()); if (qmlFile.open(QIODevice::ReadOnly)) { QString iconFileName = outDir.path() + '/' @@ -410,6 +409,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) QString iconFileName2x = iconFileName + "@2x"; QByteArray content = qmlFile.readAll(); int braceIdx = content.indexOf('{'); + QString impVersionStr; + int impVersionMajor = -1; if (braceIdx != -1) { int nlIdx = content.lastIndexOf('\n', braceIdx); QByteArray rootItem = content.mid(nlIdx, braceIdx - nlIdx).trimmed(); @@ -423,8 +424,9 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) out << "canBeDroppedInView3D: true" << Qt::endl; file.close(); - // Add quick3D import unless it is already added - if (m_requiredImports.first().url() != "QtQuick3D") { + // Assume that all assets import the same QtQuick3D version, + // since they are being imported with same kit + if (impVersionMajor == -1) { QByteArray import3dStr{"import QtQuick3D"}; int importIdx = content.indexOf(import3dStr); if (importIdx != -1 && importIdx < braceIdx) { @@ -433,15 +435,25 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) QByteArray versionStr = content.mid(importIdx, nlIdx - importIdx).trimmed(); // There could be 'as abc' after version, so just take first part QList parts = versionStr.split(' '); - QString impVersion; - if (parts.size() >= 1) - impVersion = QString::fromUtf8(parts[0]); - m_requiredImports.prepend(Import::createLibraryImport( - "QtQuick3D", impVersion)); + if (parts.size() >= 1) { + impVersionStr = QString::fromUtf8(parts[0]); + if (impVersionStr.isEmpty()) + impVersionMajor = 6; + else + impVersionMajor = impVersionStr.left(1).toInt(); + } } } + + // Add quick3D import unless it is already added + if (impVersionMajor > 0 + && m_requiredImports.first().url() != "QtQuick3D") { + m_requiredImports.prepend(Import::createLibraryImport( + "QtQuick3D", impVersionStr)); + } } - if (startIconProcess(24, iconFileName, qmlIt.filePath())) { + if (impVersionMajor > 0 && impVersionMajor < 6 + && startIconProcess(24, iconFileName, qmlIt.filePath())) { // Since icon is generated by external process, the file won't be // ready for asset gathering below, so assume its generation succeeds // and add it now. diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index e7dbbee0857..5144a5220fb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -92,7 +92,9 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS } }, Qt::QueuedConnection); - }); + }, + "libIcon", + ImageCache::LibraryIconAuxiliaryData{true}); return response.release(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 8af21584e3b..e94c5108b13 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -30,12 +30,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -44,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -52,38 +45,9 @@ namespace QmlDesigner { -namespace { -ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) -{ - if (project) - return project->activeTarget(); - - return {}; -} -} // namespace - -class ItemLibraryView::ImageCacheData -{ -public: - Sqlite::Database database{Utils::PathString{ - Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, - Sqlite::JournalMode::Wal, - Sqlite::LockingMode::Normal}; - ImageCacheStorage storage{database}; - ImageCacheConnectionManager connectionManager; - ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}}; - ImageCacheFontCollector fontCollector; - ImageCacheGenerator generator{collector, storage}; - ImageCacheGenerator fontGenerator{fontCollector, storage}; - TimeStampProvider timeStampProvider; - AsynchronousImageCache cache{storage, generator, timeStampProvider}; - AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider}; - SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector}; -}; - -ItemLibraryView::ItemLibraryView(QObject* parent) - : AbstractView(parent) - +ItemLibraryView::ItemLibraryView(AsynchronousImageCache &imageCache) + : AbstractView() + , m_imageCache(imageCache) {} ItemLibraryView::~ItemLibraryView() @@ -97,11 +61,8 @@ bool ItemLibraryView::hasWidget() const WidgetInfo ItemLibraryView::widgetInfo() { - if (m_widget.isNull()) { - m_widget = new ItemLibraryWidget{imageCacheData()->cache, - imageCacheData()->asynchronousFontImageCache, - imageCacheData()->synchronousFontImageCache}; - } + if (m_widget.isNull()) + m_widget = new ItemLibraryWidget{m_imageCache}; return createWidgetInfo(m_widget.data(), "Components", WidgetInfo::LeftPane, 0, tr("Components")); } @@ -177,43 +138,6 @@ void ItemLibraryView::usedImportsChanged(const QList &usedImports) m_widget->updateUsedImports(usedImports); } -ItemLibraryView::ImageCacheData *ItemLibraryView::imageCacheData() -{ - std::call_once(imageCacheFlag, [this]() { - m_imageCacheData = std::make_unique(); - auto setTargetInImageCache = - [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { - if (target == imageCacheData->collector.target()) - return; - - if (target) - imageCacheData->cache.clean(); - - imageCacheData->collector.setTarget(target); - }; - - if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { - m_imageCacheData->collector.setTarget(project->activeTarget()); - connect(project, - &ProjectExplorer::Project::activeTargetChanged, - this, - setTargetInImageCache); - } - connect(ProjectExplorer::SessionManager::instance(), - &ProjectExplorer::SessionManager::startupProjectChanged, - this, - [=](ProjectExplorer::Project *project) { - setTargetInImageCache(activeTarget(project)); - }); - }); - return m_imageCacheData.get(); -} - -AsynchronousImageCache &ItemLibraryView::imageCache() -{ - return imageCacheData()->cache; -} - void ItemLibraryView::documentMessagesChanged(const QList &errors, const QList &) { if (m_hasErrors && errors.isEmpty()) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h index b7b91eaf3f8..4a0925064fb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h @@ -29,19 +29,16 @@ #include -#include - namespace QmlDesigner { class ItemLibraryWidget; -class AsynchronousImageCache; class ItemLibraryView : public AbstractView { Q_OBJECT public: - ItemLibraryView(QObject* parent = nullptr); + ItemLibraryView(class AsynchronousImageCache &imageCache); ~ItemLibraryView() override; bool hasWidget() const override; @@ -58,17 +55,11 @@ public: void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; - AsynchronousImageCache &imageCache(); - protected: void updateImports(); private: - class ImageCacheData; - ImageCacheData *imageCacheData(); - - std::once_flag imageCacheFlag; - std::unique_ptr m_imageCacheData; + AsynchronousImageCache &m_imageCache; QPointer m_widget; bool m_hasErrors = false; QVariantMap m_importableExtensions3DMap; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 511ef37f196..75a564d7384 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -140,18 +140,13 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event) isHorizontalLayout = event->size().width() >= HORIZONTAL_LAYOUT_WIDTH_LIMIT; } -ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, - AsynchronousImageCache &asynchronousFontImageCache, - SynchronousImageCache &synchronousFontImageCache) +ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) : m_itemIconSize(24, 24) - , m_fontImageCache(synchronousFontImageCache) , m_itemLibraryModel(new ItemLibraryModel(this)) , m_addModuleModel(new ItemLibraryAddImportModel(this)) , m_itemsWidget(new QQuickWidget(this)) , m_imageCache{imageCache} { - Q_UNUSED(asynchronousFontImageCache) - m_compressionTimer.setInterval(200); m_compressionTimer.setSingleShot(true); ItemLibraryModel::registerQmlTypes(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index a7f23180730..be905f181ae 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -56,7 +56,6 @@ class Model; class ItemLibraryModel; class ItemLibraryAddImportModel; class ItemLibraryResourceView; -class SynchronousImageCache; class AsynchronousImageCache; class ImageCacheCollector; @@ -67,9 +66,7 @@ class ItemLibraryWidget : public QFrame public: Q_PROPERTY(bool subCompEditMode READ subCompEditMode NOTIFY subCompEditModeChanged) - ItemLibraryWidget(AsynchronousImageCache &imageCache, - AsynchronousImageCache &asynchronousFontImageCache, - SynchronousImageCache &synchronousFontImageCache); + ItemLibraryWidget(AsynchronousImageCache &imageCache); ~ItemLibraryWidget(); void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo); @@ -115,7 +112,6 @@ private: QTimer m_compressionTimer; QSize m_itemIconSize; - SynchronousImageCache &m_fontImageCache; QPointer m_itemLibraryInfo; QPointer m_itemLibraryModel; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 8257ae6857b..e2736068053 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -29,6 +29,7 @@ #include "materialeditorcontextobject.h" #include "propertyeditorvalue.h" #include "materialeditortransaction.h" +#include "assetslibrarywidget.h" #include #include @@ -809,6 +810,39 @@ void MaterialEditorView::customNotification(const AbstractView *view, const QStr } } +void QmlDesigner::MaterialEditorView::highlightSupportedProperties(bool highlight) +{ + DesignerPropertyMap &propMap = m_qmlBackEnd->backendValuesPropertyMap(); + const QStringList propNames = propMap.keys(); + + for (const QString &propName : propNames) { + if (propName.endsWith("Map")) { + QObject *propEditorValObj = propMap.value(propName).value(); + PropertyEditorValue *propEditorVal = qobject_cast(propEditorValObj); + propEditorVal->setHasActiveDrag(highlight); + } + } +} + +void MaterialEditorView::dragStarted(QMimeData *mimeData) +{ + if (!mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) + return; + + const QString assetPath = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',')[0]; + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; + + if (assetType != Constants::MIME_TYPE_ASSET_IMAGE) // currently only image assets have dnd-supported properties + return; + + highlightSupportedProperties(); +} + +void MaterialEditorView::dragEnded() +{ + highlightSupportedProperties(false); +} + // from model to material editor void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value) { diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index 8ab30a63f44..3e8632d26f6 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -73,6 +73,9 @@ public: void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; + void dragStarted(QMimeData *mimeData) override; + void dragEnded() override; + void changeValue(const QString &name); void changeExpression(const QString &name); void exportPropertyAsAlias(const QString &name); @@ -93,6 +96,7 @@ private: static QString materialEditorResourcesPath(); void reloadQml(); + void highlightSupportedProperties(bool highlight = true); QString generateIdFromName(const QString &name); void ensureMaterialLibraryNode(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index ff131f28660..6ea079b6f16 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -274,6 +274,19 @@ bool PropertyEditorValue::isTranslated() const return false; } +bool PropertyEditorValue::hasActiveDrag() const +{ + return m_hasActiveDrag; +} + +void PropertyEditorValue::setHasActiveDrag(bool val) +{ + if (m_hasActiveDrag != val) { + m_hasActiveDrag = val; + emit hasActiveDragChanged(); + } +} + static bool isAllowedSubclassType(const QString &type, const QmlDesigner::NodeMetaInfo &metaInfo) { if (!metaInfo.isValid()) @@ -371,18 +384,6 @@ void PropertyEditorValue::setEnumeration(const QString &scope, const QString &na setValueWithEmit(QVariant::fromValue(newEnumeration)); } -bool PropertyEditorValue::isSupportedDrop(const QString &path) -{ - QString suffix = "*." + QFileInfo(path).suffix().toLower(); - - if (m_modelNode.isSubclassOf("QtQuick3D.Material") && nameAsQString().endsWith("Map")) - return QmlDesigner::AssetsLibraryModel::supportedImageSuffixes().contains(suffix); - - // TODO: handle support for other object properties dnd here (like image source) - - return false; -} - void PropertyEditorValue::exportPropertyAsAlias() { emit exportPropertyAsAliasRequested(nameAsQString()); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 2a2b09d5c8c..4ae156dc921 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -82,6 +82,7 @@ class PropertyEditorValue : public QObject Q_PROPERTY(bool isBound READ isBound NOTIFY isBoundChanged FINAL) Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged FINAL) Q_PROPERTY(bool isTranslated READ isTranslated NOTIFY expressionChanged FINAL) + Q_PROPERTY(bool hasActiveDrag READ hasActiveDrag WRITE setHasActiveDrag NOTIFY hasActiveDragChanged FINAL) Q_PROPERTY(bool isIdList READ isIdList NOTIFY expressionChanged FINAL) Q_PROPERTY(QStringList expressionAsList READ getExpressionAsList NOTIFY expressionChanged FINAL) @@ -117,6 +118,9 @@ public: bool isTranslated() const; + bool hasActiveDrag() const; + void setHasActiveDrag(bool val); + bool isAvailable() const; QmlDesigner::PropertyName name() const; @@ -148,7 +152,6 @@ public: public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); - bool isSupportedDrop(const QString &path); signals: void valueChanged(const QString &name, const QVariant&); @@ -164,6 +167,7 @@ signals: void isBoundChanged(); void isValidChanged(); void isExplicitChanged(); + void hasActiveDragChanged(); private: QStringList generateStringList(const QString &string) const; @@ -176,6 +180,7 @@ private: bool m_isInSubState; bool m_isInModel; bool m_isBound; + bool m_hasActiveDrag = false; bool m_isValid; // if the property value belongs to a non-existing complexProperty it is invalid PropertyEditorNodeWrapper *m_complexNode; }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 530724315a0..7890086bd5f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -74,7 +74,7 @@ ImageCacheCollector::~ImageCacheCollector() = default; void ImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView state, - const ImageCache::AuxiliaryData &, + const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback) { @@ -96,13 +96,22 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); + bool is3DRoot = !rewriterView.inErrorState() + && (rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") + || rewriterView.rootModelNode().isSubclassOf("Quick3D.Material")); + if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() - && !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) { + && !is3DRoot)) { if (abortCallback) abortCallback(ImageCache::AbortReason::Failed); return; } + if (is3DRoot) { + if (auto libIcon = Utils::get_if(&auxiliaryData)) + rewriterView.rootModelNode().setAuxiliaryData("isLibraryIcon@NodeInstance", libIcon->enable); + } + ModelNode stateNode = rewriterView.modelNodeForId(QString{state}); if (stateNode.isValid()) diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index ae87681626a..61660271fc9 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -54,7 +54,16 @@ public: QString text; }; -using AuxiliaryData = Utils::variant; +class LibraryIconAuxiliaryData +{ +public: + bool enable; +}; + +using AuxiliaryData = Utils::variant; enum class AbortReason : char { Abort, Failed }; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 9a2bc14fe63..6b8dfab4314 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -35,6 +35,7 @@ #include QT_BEGIN_NAMESPACE +class QPixmap; class QUrl; QT_END_NAMESPACE @@ -44,7 +45,7 @@ namespace Internal { class ModelPrivate; class WriteLocker; class NodeMetaInfoPrivate; -} //Internal +} // namespace Internal class AnchorLine; class ModelNode; @@ -130,7 +131,7 @@ public: QString generateNewId(const QString &prefixName) const; QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; - void startDrag(QMimeData *mimeData, const QString iconPath = {}); + void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); protected: @@ -140,4 +141,4 @@ private: Internal::ModelPrivate *d; }; -} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/viewmanager.h b/src/plugins/qmldesigner/designercore/include/viewmanager.h index 04f25b84f81..dd77c07f459 100644 --- a/src/plugins/qmldesigner/designercore/include/viewmanager.h +++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h @@ -47,12 +47,11 @@ class Edit3DView; namespace Internal { class DesignModeWidget; } class ViewManagerData; -class AsynchronousImageCache; class QMLDESIGNERCORE_EXPORT ViewManager { public: - ViewManager(); + ViewManager(class AsynchronousImageCache &imageCache); ~ViewManager(); void attachRewriterView(); @@ -107,8 +106,6 @@ public: void disableStandardViews(); void enableStandardViews(); - AsynchronousImageCache &imageCache(); - private: // functions Q_DISABLE_COPY(ViewManager) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 68519490837..5eef5550da7 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -600,11 +600,9 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP // Reset puppet when particle emitter/affector is reparented to work around issue in // autodetecting the particle system it belongs to. QTBUG-101157 - // Reset is also needed when particle shapes are reparented. QTBUG-101882 - if (((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") + if ((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") || node.isSubclassOf("QtQuick.Particles3D.Affector3D")) - && node.property("system").toBindingProperty().expression().isEmpty()) - || node.isSubclassOf("QQuick3DParticleAbstractShape")) { + && node.property("system").toBindingProperty().expression().isEmpty()) { resetPuppet(); } } @@ -987,6 +985,17 @@ QList filterNodesForSkipItems(const QList &nodeList) return filteredNodeList; } +QList readBackgroundColorConfiguration(const QVariant &var) +{ + if (!var.isValid()) + return {}; + + auto colorNameList = var.value>(); + QTC_ASSERT(colorNameList.size() == 2, return {}); + + return {colorNameList[0], colorNameList[1]}; +} + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList nodeList = allModelNodes(); @@ -1141,6 +1150,17 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0)) stateInstanceId = stateNode.internalId(); + auto value +#ifndef QMLDESIGNER_TEST + = QmlDesigner::DesignerSettings::getValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); +#else + = QColor(); +#endif + QList edit3dBackgroundColor; + if (value.isValid()) + edit3dBackgroundColor = readBackgroundColorConfiguration(value); + return CreateSceneCommand( instanceContainerList, reparentContainerList, @@ -1161,7 +1181,8 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, - stateInstanceId); + stateInstanceId, + edit3dBackgroundColor); } ClearSceneCommand NodeInstanceView::createClearSceneCommand() const @@ -2190,9 +2211,8 @@ void NodeInstanceView::maybeResetOnPropertyChange(const PropertyName &name, cons { bool reset = false; if (flags & AbstractView::PropertiesAdded - && name == "model" && (node.isSubclassOf("QtQuick.Repeater") - || node.isSubclassOf("QtQuick3D.Repeater3D"))) { - // TODO: This is a workaround for QTBUG-97583 (2D) and QTBUG-97586 (3D): + && name == "model" && node.isSubclassOf("QtQuick.Repeater")) { + // TODO: This is a workaround for QTBUG-97583: // Reset puppet when repeater model is first added, if there is already a delegate if (node.hasProperty("delegate")) reset = true; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 8ddf6d448d1..5ece130a573 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -452,10 +452,9 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const QString iconPath = qmlIt.fileInfo().absolutePath() + '/' + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name + Constants::QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX; - if (!QFileInfo::exists(iconPath)) - iconPath = defaultIconPath; - itemLibraryEntry.setLibraryEntryIconPath(iconPath); - itemLibraryEntry.setTypeIcon(QIcon(iconPath)); + if (QFileInfo::exists(iconPath)) + itemLibraryEntry.setLibraryEntryIconPath(iconPath); + itemLibraryEntry.setTypeIcon(QIcon(defaultIconPath)); // load hints file if exists QFile hintsFile(qmlIt.fileInfo().absolutePath() + '/' + name + ".hints"); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d879271c0d4..4e38fb3cfb3 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1510,12 +1510,12 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP return newId; } -void Model::startDrag(QMimeData *mimeData, const QString iconPath) +void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) { d->notifyDragStarted(mimeData); auto drag = new QDrag(this); - drag->setPixmap(iconPath); + drag->setPixmap(icon); drag->setMimeData(mimeData); drag->exec(); drag->deleteLater(); diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index d72761c91a3..730bfbb0fb5 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -62,6 +62,10 @@ static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData { public: + ViewManagerData(AsynchronousImageCache &imageCache) + : itemLibraryView(imageCache) + {} + InteractiveConnectionManager connectionManager; CapturingConnectionManager capturingConnectionManager; QmlModelState savedState; @@ -90,12 +94,13 @@ static CrumbleBar *crumbleBar() { return QmlDesignerPlugin::instance()->mainWidget()->crumbleBar(); } -ViewManager::ViewManager() - : d(std::make_unique()) +ViewManager::ViewManager(AsynchronousImageCache &imageCache) + : d(std::make_unique(imageCache)) { d->formEditorView.setGotoErrorCallback([this](int line, int column) { d->textEditorView.gotoCursorPosition(line, column); - if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget()) + if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance() + ->mainWidget()) designModeWidget->showDockWidget("TextEditor"); }); } @@ -439,11 +444,6 @@ void ViewManager::enableStandardViews() attachViewsExceptRewriterAndComponetView(); } -AsynchronousImageCache &ViewManager::imageCache() -{ - return d->itemLibraryView.imageCache(); -} - void ViewManager::addView(std::unique_ptr &&view) { d->additionalViews.push_back(std::move(view)); diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index aad91e06b48..9c1aec1b364 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -80,6 +80,7 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); restoreValue(settings, DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER, false); restoreValue(settings, DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QList{"#222222", "#999999"}); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index e3fd4470da0..8c249fc65e0 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -49,6 +49,7 @@ const char WARNING_FOR_QML_FILES_INSTEAD_OF_UIQML_FILES[] = "WarnAboutQmlFilesIn const char WARNING_FOR_DESIGNER_FEATURES_IN_EDITOR[] = "WarnAboutQtQuickDesignerFeaturesInCodeEditor"; const char SHOW_DEBUGVIEW[] = "ShowQtQuickDesignerDebugView"; const char ENABLE_DEBUGVIEW[] = "EnableQtQuickDesignerDebugView"; +const char EDIT3DVIEW_BACKGROUND_COLOR[] = "Edit3DViewBackgroundColor"; const char ALWAYS_SAVE_IN_CRUMBLEBAR[] = "AlwaysSaveInCrumbleBar"; const char USE_DEFAULT_PUPPET[] = "UseDefaultQml2Puppet"; const char PUPPET_TOPLEVEL_BUILD_DIRECTORY[] = "PuppetToplevelBuildDirectory"; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index efefc381a1f..8dc2b947496 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -65,6 +65,8 @@ const char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle"; const char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; const char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; +const char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; +const char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.ResetBackgroundColor"; const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox"; const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo"; const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum"; @@ -74,6 +76,7 @@ const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeTo const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; +const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; diff --git a/src/plugins/qmldesigner/qmldesignericons.h b/src/plugins/qmldesigner/qmldesignericons.h index 75ec4ab2d3f..437e420a48e 100644 --- a/src/plugins/qmldesigner/qmldesignericons.h +++ b/src/plugins/qmldesigner/qmldesignericons.h @@ -91,6 +91,8 @@ const Utils::Icon EDIT3D_ALIGN_CAMERA_ON({ {":/edit3d/images/align_camera_on.png", Utils::Theme::IconsBaseColor}}); const Utils::Icon EDIT3D_ALIGN_VIEW_ON({ {":/edit3d/images/align_view_on.png", Utils::Theme::IconsBaseColor}}); +const Utils::Icon COLOR_PALETTE({ + {":/edit3d/images/color_palette.png", Utils::Theme::IconsBaseColor}}); } // Icons } // QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 6e0ccf5f399..1ce53ee8d71 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -135,7 +135,7 @@ class QmlDesignerPluginPrivate { public: QmlDesignerProjectManager projectManager; - ViewManager viewManager; + ViewManager viewManager{projectManager.asynchronousImageCache()}; DocumentManager documentManager; ShortCutManager shortCutManager; SettingsPage settingsPage; @@ -666,7 +666,7 @@ void QmlDesignerPlugin::emitUsageStatisticsHelpRequested(const QString &identifi AsynchronousImageCache &QmlDesignerPlugin::imageCache() { - return m_instance->d->viewManager.imageCache(); + return m_instance->d->projectManager.asynchronousImageCache(); } void QmlDesignerPlugin::registerPreviewImageProvider(QQmlEngine *engine) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index b48f257c55c..375f9e82233 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -526,6 +526,8 @@ Project { "debugview/debugviewwidget.ui", "edit3d/edit3dview.cpp", "edit3d/edit3dview.h", + "edit3d/backgroundcolorselection.cpp", + "edit3d/backgroundcolorselection.h", "edit3d/edit3dwidget.cpp", "edit3d/edit3dwidget.h", "edit3d/edit3dcanvas.cpp", diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 538c3939f2f..43070221bcf 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,14 @@ namespace QmlDesigner { namespace { +ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) +{ + if (project) + return project->activeTarget(); + + return {}; +} + QString defaultImagePath() { return Core::ICore::resourcePath("qmldesigner/welcomepage/images/newThumbnail.png").toString(); @@ -89,7 +98,22 @@ public: } // namespace -class PreviewImageCacheData +class QmlDesignerProjectManager::ImageCacheData +{ +public: + Sqlite::Database database{Utils::PathString{ + Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, + Sqlite::JournalMode::Wal, + Sqlite::LockingMode::Normal}; + ImageCacheStorage storage{database}; + ImageCacheConnectionManager connectionManager; + ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}}; + ImageCacheGenerator generator{collector, storage}; + TimeStampProvider timeStampProvider; + AsynchronousImageCache asynchronousImageCache{storage, generator, timeStampProvider}; +}; + +class QmlDesignerProjectManager::PreviewImageCacheData { public: Sqlite::Database database{Utils::PathString{ @@ -100,7 +124,7 @@ public: AsynchronousExplicitImageCache cache{storage}; }; -class QmlDesignerProjectManagerProjectData +class QmlDesignerProjectManager::QmlDesignerProjectManagerProjectData { public: QmlDesignerProjectManagerProjectData(ImageCacheStorage &storage) @@ -117,7 +141,7 @@ public: }; QmlDesignerProjectManager::QmlDesignerProjectManager() - : m_imageCacheData{std::make_unique()} + : m_previewImageCacheData{std::make_unique()} { auto editorManager = ::Core::EditorManager::instance(); QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) { @@ -145,12 +169,17 @@ QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; void QmlDesignerProjectManager::registerPreviewImageProvider(QQmlEngine *engine) const { - auto imageProvider = std::make_unique(m_imageCacheData->cache, + auto imageProvider = std::make_unique(m_previewImageCacheData->cache, QImage{defaultImagePath()}); engine->addImageProvider("project_preview", imageProvider.release()); } +AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() +{ + return imageCacheData()->asynchronousImageCache; +} + void QmlDesignerProjectManager::editorOpened(::Core::IEditor *) {} void QmlDesignerProjectManager::currentEditorChanged(::Core::IEditor *) @@ -171,7 +200,7 @@ void QmlDesignerProjectManager::editorsClosed(const QList<::Core::IEditor *> &) void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) { - m_projectData = std::make_unique(m_imageCacheData->storage); + m_projectData = std::make_unique(m_previewImageCacheData->storage); m_projectData->activeTarget = project->activeTarget(); } @@ -183,4 +212,36 @@ void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project void QmlDesignerProjectManager::projectRemoved(::ProjectExplorer::Project *) {} +QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCacheData() +{ + std::call_once(imageCacheFlag, [this]() { + m_imageCacheData = std::make_unique(); + auto setTargetInImageCache = + [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { + if (target == imageCacheData->collector.target()) + return; + + if (target) + imageCacheData->asynchronousImageCache.clean(); + + imageCacheData->collector.setTarget(target); + }; + + if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { + m_imageCacheData->collector.setTarget(project->activeTarget()); + QObject::connect(project, + &ProjectExplorer::Project::activeTargetChanged, + this, + setTargetInImageCache); + } + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + [=](ProjectExplorer::Project *project) { + setTargetInImageCache(activeTarget(project)); + }); + }); + return m_imageCacheData.get(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index 4b993ba74d2..6b94fa9e6f4 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -26,8 +26,10 @@ #pragma once #include +#include #include +#include QT_FORWARD_DECLARE_CLASS(QQmlEngine) @@ -42,17 +44,22 @@ class Target; namespace QmlDesigner { -class QmlDesignerProjectManagerProjectData; -class PreviewImageCacheData; - -class QmlDesignerProjectManager +class QmlDesignerProjectManager : public QObject { + Q_OBJECT + + class QmlDesignerProjectManagerProjectData; + class PreviewImageCacheData; + class ImageCacheData; + public: QmlDesignerProjectManager(); ~QmlDesignerProjectManager(); void registerPreviewImageProvider(QQmlEngine *engine) const; + class AsynchronousImageCache &asynchronousImageCache(); + private: void editorOpened(::Core::IEditor *editor); void currentEditorChanged(::Core::IEditor *); @@ -60,9 +67,12 @@ private: void projectAdded(::ProjectExplorer::Project *project); void aboutToRemoveProject(::ProjectExplorer::Project *project); void projectRemoved(::ProjectExplorer::Project *project); + ImageCacheData *imageCacheData(); private: - std::unique_ptr m_imageCacheData; + std::once_flag imageCacheFlag; + std::unique_ptr m_imageCacheData; + std::unique_ptr m_previewImageCacheData; std::unique_ptr m_projectData; }; } // namespace QmlDesigner diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index b69c66db623..a5f69514db9 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -57,6 +57,12 @@ using namespace Utils; +static bool enableDownload() +{ + const QString lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload"; + return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool(); +} + void ExampleCheckout::registerTypes() { static bool once = []() { @@ -186,6 +192,12 @@ bool FileDownloader::available() const void FileDownloader::probeUrl() { + if (!enableDownload()) { + m_available = false; + emit availableChanged(); + return; + } + auto request = QNetworkRequest(m_url); request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::UserVerifiedRedirectPolicy); @@ -448,6 +460,13 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) void DataModelDownloader::start() { + + if (!enableDownload()) { + m_available = false; + emit availableChanged(); + return; + } + m_fileDownloader.setUrl(QUrl::fromUserInput( "https://download.qt.io/learning/examples/qtdesignstudio/dataImports.zip"));