diff --git a/dist/changes-8.0.1.md b/dist/changes-8.0.1.md index cf5fc2f8f02..37247687c2f 100644 --- a/dist/changes-8.0.1.md +++ b/dist/changes-8.0.1.md @@ -13,8 +13,14 @@ the public Git repository. For example: Editing ------- +* Delayed context menu tooltip action creation +* Cached the last QIcon created from a Utils::Icon + ### C++ +* Prevent opening unneeded generated ui header files in clangd +* Fixed documents getting opened in wrong clangd + Projects -------- @@ -42,17 +48,34 @@ Platforms ### Baremetal * Fixed running and debugging applications (QTCREATORBUG-27972) +* Fixed bug when cloning jLink gdb server provider + +Debugging +--------- + +* Fixed bitfield display with Python 3 + Credits for these changes go to: -------------------------------- +Alexander Akulich Alibek Omarov André Pönitz Christian Kandeler +Christian Stenger Cristian Adam +David Schulz +Dmitry Kovalev Eike Ziller Ivan Komissarov Jaroslaw Kobus +Knud Dollereder Mahmoud Badri +Marcus Tillmanns Mats Honkamaa +Miikka Heikkinen +Orgad Shaneh Oswald Buddenhagen +Samuel Ghinet Thomas Hartmann +Vikas Pachdha diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc index a2bebb49fb5..80ee71d960f 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc @@ -165,6 +165,16 @@ To disable the Python language server, deselect \uicontrol {Use Python Language Server}. + \section2 Qml Language Server + + Qt 6.4 ships with the qmlls language server that provides completions and warnings for QML. + It can be set up as a \l {Generic StdIO Language Server}, selecting \c {text/x-qml} and + \c {application/x-qt.ui+qml} as MIME Types, and \c {/bin/qmlls} as executable. + + If the language server is used together with the QmlJSEditor plugin duplicate suggestions and + warnings might be shown. To avoid this you might want to disable it as described in + \l {Enabling and Disabling Plugins}. + \section1 Supported Locator Filters The locator enables you to browse not only files, but any items defined by diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc index bc27d5e3484..b8b787face3 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc @@ -43,10 +43,8 @@ \section1 2D Assets - You can use the Qt Installer to install \QB if you have a commercial - \QDS license. You can also purchase a \QB license separately from the - \l{https://marketplace.qt.io/}{Qt Marketplace} and then install \QB using - the Qt Installer. + You can use the Qt Installer to install \QB if you have a + \QDS enterprise license. \table \row diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc index ba43f329238..8dc0620032f 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc @@ -30,10 +30,10 @@ \title Setting Up \QBPS - You can purchase a \QBPS license from the \l{https://marketplace.qt.io/} - {Qt Marketplace}, and then use the Qt Installer to have the \QBPS - installation package copied to the following path in your Qt installation - folder: + \QBPS is included in the + \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. + You can use the Qt Installer to have the \QBPS plugin package copied to the + following path in your Qt installation folder: \list \li On Windows: \c {Tools\QtDesignStudio\photoshop_bridge} diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc index 6f883a1f859..dd507243507 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc @@ -30,10 +30,10 @@ \title Setting Up \QBSK - You can purchase a \QBSK license from the \l{https://marketplace.qt.io/} - {Qt Marketplace}, and then use the Qt Installer to have the \QBSK - plugin package copied to the following path in your Qt installation - folder: + \QBSK is included in the + \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. + You can use the Qt Installer to have the \QBSK plugin package copied to the + following path in your Qt installation folder: \list \li On Windows: diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc index 8ecead5647e..58959166751 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc @@ -30,10 +30,10 @@ \title Setting Up \QBXD - You can purchase a \QBXD license from the \l{https://marketplace.qt.io/} - {Qt Marketplace}, and then use the Qt Installer to have the \QBXD - plugin package copied to the following path in your Qt installation - folder: + \QBXD is included in the + \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. + You can use the Qt Installer to have the \QBXD plugin package copied to the + following path in your Qt installation folder: \list \li On Windows: diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc index bbe9a910db6..d4127574a69 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc @@ -16,6 +16,8 @@ mockfiles/images/static_floor.png mockfiles/images/spot.png mockfiles/images/spot@2x.png + mockfiles/images/preview_landscape.hdr + mockfiles/images/preview_studio.hdr mockfiles/qt5/AdjustableArrow.qml mockfiles/qt5/AreaLightHandle.qml mockfiles/qt5/Arrow.qml diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc index 05459423349..bb29d683a07 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc @@ -18,6 +18,8 @@ mockfiles/images/floor_tex.png mockfiles/images/spot.png mockfiles/images/spot@2x.png + mockfiles/images/preview_landscape.hdr + mockfiles/images/preview_studio.hdr mockfiles/qt6/AdjustableArrow.qml mockfiles/qt6/AreaLightHandle.qml mockfiles/qt6/Arrow.qml diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr new file mode 100644 index 00000000000..2c6b4372d47 Binary files /dev/null and b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr differ diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr new file mode 100644 index 00000000000..8ab060c1f45 Binary files /dev/null and b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr differ diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index 9fee06e0ad1..067e34928b7 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -29,8 +29,12 @@ View3D { id: root anchors.fill: parent environment: sceneEnv + camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera property Material previewMaterial + property string envMode + property string envValue + property string modelSrc: "#Sphere" function fitToViewPort(closeUp) { @@ -41,28 +45,59 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + backgroundMode: envMode === "Color" ? SceneEnvironment.Color + : envMode === "SkyBox" ? SceneEnvironment.SkyBox + : SceneEnvironment.Transparent + clearColor: envMode === "Color" ? envValue : "#000000" + lightProbe: envMode === "SkyBox" ? skyBoxTex : null + + Texture { + id: skyBoxTex + source: envMode === "SkyBox" ? "../images/" + envValue + ".hdr" + : "" + } } Node { DirectionalLight { eulerRotation.x: -26 - eulerRotation.y: -57 + eulerRotation.y: modelSrc === "#Cube" ? -10 : -50 + brightness: envMode !== "SkyBox" ? 100 : 0 } PerspectiveCamera { - y: 125.331 - z: 120 - eulerRotation.x: -31 + id: defaultCamera + y: 70 + z: 200 + eulerRotation.x: -5.71 clipNear: 1 clipFar: 1000 } - Model { - id: model + PerspectiveCamera { + id: studioCamera + y: 232 + z: 85 + eulerRotation.x: -64.98 + clipNear: 1 + clipFar: 1000 + } + Node { + rotation: root.camera.rotation y: 50 - source: "#Sphere" - materials: previewMaterial + Node { + y: modelSrc === "#Cone" ? -40 : 10 + eulerRotation.x: 35 + Model { + id: model + source: modelSrc ? modelSrc : "#Sphere" + eulerRotation.y: 45 + materials: previewMaterial + scale: !modelSrc || modelSrc === "#Sphere" + ? Qt.vector3d(1.7, 1.7, 1.7) : Qt.vector3d(1.2, 1.2, 1.2) + } + } } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index 70b9dbc4d0e..d32a1ea915d 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -50,25 +50,28 @@ Item { view.destroy(); } - function createViewForObject(obj) + function createViewForObject(obj, env, envValue, model) { if (obj instanceof Material) - createViewForMaterial(obj); + createViewForMaterial(obj, env, envValue, model); else if (obj instanceof Model) createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); } - function createViewForMaterial(material) + function createViewForMaterial(material, env, envValue, model) { if (!materialViewComponent) materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); // Always recreate the view to ensure material is up to date - if (materialViewComponent.status === Component.Ready) - view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); - + if (materialViewComponent.status === Component.Ready) { + view = materialViewComponent.createObject(viewRect, {"previewMaterial": material, + "envMode": env, + "envValue": envValue, + "modelSrc": model}); + } previewObject = material; } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index 94051d5f6e6..12fdd3048eb 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -29,8 +29,12 @@ View3D { id: root anchors.fill: parent environment: sceneEnv + camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera property Material previewMaterial + property string envMode + property string envValue + property string modelSrc: "#Sphere" function fitToViewPort(closeUp) { @@ -41,30 +45,79 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + backgroundMode: envMode === "Color" ? SceneEnvironment.Color + : envMode === "SkyBox" ? SceneEnvironment.SkyBox + : SceneEnvironment.Transparent + clearColor: envMode === "Color" ? envValue : "#000000" + lightProbe: envMode === "SkyBox" ? skyBoxTex : null + + Texture { + id: skyBoxTex + source: envMode === "SkyBox" ? "../images/" + envValue + ".hdr" + : "" + } } Node { DirectionalLight { eulerRotation.x: -26 - eulerRotation.y: -57 + eulerRotation.y: modelSrc === "#Cube" ? -10 : -50 + brightness: envMode !== "SkyBox" ? 1 : 0 } PerspectiveCamera { - y: 125.331 - z: 120 - eulerRotation.x: -31 + id: defaultCamera + y: 70 + z: 200 + eulerRotation.x: -5.71 clipNear: 1 clipFar: 1000 } - Model { - id: model - readonly property bool _edit3dLocked: true // Make this non-pickable - - y: 50 - source: "#Sphere" - materials: previewMaterial + PerspectiveCamera { + id: studioCamera + y: 232 + z: 85 + eulerRotation.x: -64.98 + clipNear: 1 + clipFar: 1000 } + Node { + rotation: root.camera.rotation + y: 50 + Node { + y: modelSrc === "#Cone" ? -40 : 10 + eulerRotation.x: 35 + Model { + id: model + readonly property bool _edit3dLocked: true // Make this non-pickable + source: modelSrc ? modelSrc : "#Sphere" + eulerRotation.y: 45 + materials: previewMaterial + scale: !modelSrc || modelSrc === "#Sphere" + ? Qt.vector3d(1.7, 1.7, 1.7) : Qt.vector3d(1.2, 1.2, 1.2) + } + } + } + Model { + id: floorModel + source: "#Rectangle" + scale.y: 8 + scale.x: 8 + eulerRotation.x: -60 + visible: !envMode || envMode === "Default" + materials: floorMaterial + DefaultMaterial { + id: floorMaterial + diffuseMap: floorTex + Texture { + id: floorTex + source: "../images/floor_tex.png" + scaleU: floorModel.scale.x + scaleV: floorModel.scale.y + } + } + } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 031d01d65fb..e409e35aee0 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -50,25 +50,31 @@ Item { view.destroy(); } - function createViewForObject(obj) + function createViewForObject(obj, env, envValue, model) { + backgroundView3d.visible = true; if (obj instanceof Material) - createViewForMaterial(obj); + createViewForMaterial(obj, env, envValue, model); else if (obj instanceof Model) createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); } - function createViewForMaterial(material) + function createViewForMaterial(material, env, envValue, model) { if (!materialViewComponent) materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); // Always recreate the view to ensure material is up to date - if (materialViewComponent.status === Component.Ready) - view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); - + if (materialViewComponent.status === Component.Ready) { + view = materialViewComponent.createObject(viewRect, {"previewMaterial": material, + "envMode": env, + "envValue": envValue, + "modelSrc": model}); + } + // Floor must be in same view as material so material can reflect it, so hide it in this one + backgroundView3d.visible = false; previewObject = material; } @@ -129,6 +135,7 @@ Item { // Use View3D instead of static image to make background look good on all resolutions View3D { + id: backgroundView3d anchors.fill: parent environment: sceneEnv diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 1161cb94391..13e7063e29d 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -339,6 +339,22 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() #endif } +void Qt5InformationNodeInstanceServer::updateMaterialPreviewData( + const QVector &valueChanges) +{ + const PropertyName matPrevPrefix("matPrev"); + for (const auto &container : valueChanges) { + if (container.instanceId() == 0) { + if (container.name() == "matPrevEnv") + m_materialPreviewData.env = container.value().toString(); + else if (container.name() == "matPrevEnvValue") + m_materialPreviewData.envValue = container.value().toString(); + else if (container.name() == "matPrevModel") + m_materialPreviewData.model = container.value().toString(); + } + } +} + void Qt5InformationNodeInstanceServer::updateRotationBlocks( [[maybe_unused]] const QVector &valueChanges) { @@ -1176,7 +1192,10 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView( } else { QMetaObject::invokeMethod( m_modelNode3DImageViewData.rootItem, "createViewForObject", - Q_ARG(QVariant, objectToVariant(instanceObj))); + Q_ARG(QVariant, objectToVariant(instanceObj)), + Q_ARG(QVariant, m_materialPreviewData.env), + Q_ARG(QVariant, m_materialPreviewData.envValue), + Q_ARG(QVariant, m_materialPreviewData.model)); } // Need to render twice, first render updates spatial nodes @@ -2010,6 +2029,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com if (ViewConfig::isQuick3DMode()) { setup3DEditView(instanceList, command); updateRotationBlocks(command.auxiliaryChanges); + updateMaterialPreviewData(command.auxiliaryChanges); } QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, @@ -2414,6 +2434,7 @@ void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const Reques void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) { updateRotationBlocks(command.auxiliaryChanges); + updateMaterialPreviewData(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index f3448de4add..ea104442a6e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -145,6 +145,7 @@ private: void updateLockedAndHiddenStates(const QSet &instances); void handleInputEvents(); void resolveImportSupport(); + void updateMaterialPreviewData(const QVector &valueChanges); void updateRotationBlocks(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); @@ -191,6 +192,13 @@ private: QObject *m_3dHelper = nullptr; int m_need3DEditViewRender = 0; QSet m_dynamicObjectConstructors; + + struct PreviewData { + QString env; + QString envValue; + QString model; + }; + PreviewData m_materialPreviewData; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 0419b945b9c..a8692f3ef9d 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -39,6 +39,8 @@ Item { property var currentMaterial: null property int currentMaterialIdx: 0 + property var matSectionsModel: [] + // Called also from C++ to close context menu on focus out function closeContextMenu() { @@ -67,8 +69,10 @@ Item { acceptedButtons: Qt.RightButton onClicked: { - root.currentMaterial = null - contextMenu.popup() + if (!materialBrowserModel.hasMaterialRoot) { + root.currentMaterial = null + contextMenu.popup() + } } } @@ -92,27 +96,77 @@ Item { StudioControls.MenuItem { text: qsTr("Apply to selected (replace)") - enabled: currentMaterial && materialBrowserModel.hasModelSelection - onTriggered: materialBrowserModel.applyToSelected(currentMaterial.materialInternalId, false) + enabled: root.currentMaterial && materialBrowserModel.hasModelSelection + onTriggered: materialBrowserModel.applyToSelected(root.currentMaterial.materialInternalId, false) } StudioControls.MenuItem { text: qsTr("Apply to selected (add)") - enabled: currentMaterial && materialBrowserModel.hasModelSelection - onTriggered: materialBrowserModel.applyToSelected(currentMaterial.materialInternalId, true) + enabled: root.currentMaterial && materialBrowserModel.hasModelSelection + onTriggered: materialBrowserModel.applyToSelected(root.currentMaterial.materialInternalId, true) + } + + StudioControls.MenuSeparator { + height: StudioTheme.Values.border + } + + StudioControls.Menu { + title: qsTr("Copy properties") + enabled: root.currentMaterial + + width: parent.width + + onAboutToShow: { + root.matSectionsModel = ["All"]; + + switch (root.currentMaterial.materialType) { + case "DefaultMaterial": + root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.defaultMaterialSections); + break; + + case "PrincipledMaterial": + root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.principledMaterialSections); + break; + + case "CustomMaterial": + root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.customMaterialSections); + break; + } + } + + Repeater { + model: root.matSectionsModel + + StudioControls.MenuItem { + text: modelData + enabled: root.currentMaterial + onTriggered: materialBrowserModel.copyMaterialProperties(root.currentMaterialIdx, modelData) + } + } + } + + StudioControls.MenuItem { + text: qsTr("Paste properties") + enabled: root.currentMaterial && root.currentMaterial.materialType + === materialBrowserModel.copiedMaterialType + onTriggered: materialBrowserModel.pasteMaterialProperties(root.currentMaterialIdx) + } + + StudioControls.MenuSeparator { + height: StudioTheme.Values.border } StudioControls.MenuItem { text: qsTr("Duplicate") - enabled: currentMaterial - onTriggered: materialBrowserModel.duplicateMaterial(currentMaterialIdx) + enabled: root.currentMaterial + onTriggered: materialBrowserModel.duplicateMaterial(root.currentMaterialIdx) } StudioControls.MenuItem { text: qsTr("Rename") - enabled: currentMaterial + enabled: root.currentMaterial onTriggered: { - var item = gridRepeater.itemAt(currentMaterialIdx); + var item = gridRepeater.itemAt(root.currentMaterialIdx); if (item) item.startRename(); } @@ -120,9 +174,9 @@ Item { StudioControls.MenuItem { text: qsTr("Delete") - enabled: currentMaterial + enabled: root.currentMaterial - onTriggered: materialBrowserModel.deleteMaterial(currentMaterialIdx) + onTriggered: materialBrowserModel.deleteMaterial(root.currentMaterialIdx) } StudioControls.MenuSeparator {} @@ -141,6 +195,7 @@ Item { Row { width: root.width + enabled: !materialBrowserModel.hasMaterialRoot && materialBrowserModel.hasQuick3DImport SearchBox { id: searchBox @@ -165,22 +220,22 @@ Item { color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize leftPadding: 10 - visible: materialBrowserModel.hasQuick3DImport && materialBrowserModel.isEmpty && !searchBox.isEmpty() + visible: materialBrowserModel.hasQuick3DImport && materialBrowserModel.isEmpty + && !searchBox.isEmpty() && !materialBrowserModel.hasMaterialRoot } Text { - text: qsTr("There are no materials in this project.
Select '+' to create one.") - textFormat: Text.RichText - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - horizontalAlignment: Text.AlignHCenter - topPadding: 30 - anchors.horizontalCenter: parent.horizontalCenter - visible: materialBrowserModel.hasQuick3DImport && materialBrowserModel.isEmpty && searchBox.isEmpty() - } + text: { + if (materialBrowserModel.hasMaterialRoot) + qsTr("Material Browser is disabled inside a material component.") + else if (!materialBrowserModel.hasQuick3DImport) + qsTr("To use Material Browser, first add the QtQuick3D module in the Components view.") + else if (materialBrowserModel.isEmpty && searchBox.isEmpty()) + qsTr("There are no materials in this project.
Select '+' to create one.") + else + "" + } - Text { - text: qsTr("To use Material Browser, first add the QtQuick3D module in the Components view."); textFormat: Text.RichText color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.mediumFontSize @@ -188,8 +243,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap width: root.width - anchors.horizontalCenter: parent.horizontalCenter - visible: !materialBrowserModel.hasQuick3DImport + visible: text !== "" } ScrollView { diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index a9e272b6d51..50b5ce6a7e8 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml @@ -33,6 +33,12 @@ PropertyEditorPane { id: root signal toolBarAction(int action) + signal previewEnvChanged(string env) + signal previewModelChanged(string model) + + // Called from C++, dummy methods to avoid warnings + function closeContextMenu() {} + function initPreviewData(env, model) {} Column { id: col diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index cfc037bc24f..19d25e7fdc1 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -31,6 +31,8 @@ PropertyEditorPane { id: itemPane signal toolBarAction(int action) + signal previewEnvChanged(string env) + signal previewModelChanged(string model) // invoked from C++ to refresh material preview image function refreshPreview() @@ -38,10 +40,43 @@ PropertyEditorPane { topSection.refreshPreview() } + // Called also from C++ to close context menu on focus out + function closeContextMenu() + { + topSection.closeContextMenu() + } + + // Called from C++ to initialize preview menu checkmarks + function initPreviewData(env, model) + { + topSection.previewEnv = env; + topSection.previewModel = model + } + MaterialEditorTopSection { id: topSection onToolBarAction: (action) => itemPane.toolBarAction(action) + onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv) + onPreviewModelChanged: itemPane.previewModelChanged(previewModel) + } + + Item { width: 1; height: 10 } + + Loader { + id: specificsTwo + + property string theSource: specificQmlData + + anchors.left: parent.left + anchors.right: parent.right + visible: theSource !== "" + sourceComponent: specificQmlComponent + + onTheSourceChanged: { + active = false + active = true + } } Item { width: 1; height: 10 } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml index 80a8d9abbe9..5d53bfc87d3 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml @@ -47,10 +47,10 @@ Rectangle { IconButton { icon: StudioTheme.Constants.applyMaterialToSelected - normalColor: "transparent" + normalColor: StudioTheme.Values.themeSectionHeadBackground iconSize: StudioTheme.Values.bigIconFontSize buttonSize: root.height - enabled: hasMaterial && hasModelSelection && hasQuick3DImport + enabled: hasMaterial && hasModelSelection && hasQuick3DImport && !hasMaterialRoot onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected) tooltip: qsTr("Apply material to selected model.") } @@ -58,10 +58,10 @@ Rectangle { IconButton { icon: StudioTheme.Constants.newMaterial - normalColor: "transparent" + normalColor: StudioTheme.Values.themeSectionHeadBackground iconSize: StudioTheme.Values.bigIconFontSize buttonSize: root.height - enabled: hasQuick3DImport + enabled: hasQuick3DImport && !hasMaterialRoot onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial) tooltip: qsTr("Create new material.") } @@ -69,10 +69,10 @@ Rectangle { IconButton { icon: StudioTheme.Constants.deleteMaterial - normalColor: "transparent" + normalColor: StudioTheme.Values.themeSectionHeadBackground iconSize: StudioTheme.Values.bigIconFontSize buttonSize: root.height - enabled: hasMaterial && hasQuick3DImport + enabled: hasMaterial && hasQuick3DImport && !hasMaterialRoot onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentMaterial) tooltip: qsTr("Delete current material.") } @@ -80,10 +80,10 @@ Rectangle { IconButton { icon: StudioTheme.Constants.openMaterialBrowser - normalColor: "transparent" + normalColor: StudioTheme.Values.themeSectionHeadBackground iconSize: StudioTheme.Values.bigIconFontSize buttonSize: root.height - enabled: hasMaterial && hasQuick3DImport + enabled: hasMaterial && hasQuick3DImport && !hasMaterialRoot onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser) tooltip: qsTr("Open material browser.") } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index 2e21d5814ac..80ac9b2aacb 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -29,6 +29,7 @@ import QtQuick.Layouts 1.15 import QtQuickDesignerTheme 1.0 import QtQuick.Templates 2.15 as T import HelperWidgets 2.0 +import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme Column { @@ -36,12 +37,22 @@ Column { signal toolBarAction(int action) + property string previewEnv + property string previewModel + function refreshPreview() { materialPreview.source = "" materialPreview.source = "image://materialEditor/preview" } + // Called from C++ to close context menu on focus out + function closeContextMenu() + { + modelMenu.close() + envMenu.close() + } + anchors.left: parent.left anchors.right: parent.right @@ -53,20 +64,133 @@ Column { Item { width: 1; height: 10 } // spacer - Rectangle { - width: 152 - height: 152 - color: "#000000" - anchors.horizontalCenter: parent.horizontalCenter - Image { - id: materialPreview - width: 150 - height: 150 - anchors.centerIn: parent - source: "image://materialEditor/preview" - cache: false + StudioControls.Menu { + id: modelMenu + closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside + + ListModel { + id: modelMenuModel + ListElement { + modelName: qsTr("Cone") + modelStr: "#Cone" + } + ListElement { + modelName: qsTr("Cube") + modelStr: "#Cube" + } + ListElement { + modelName: qsTr("Cylinder") + modelStr: "#Cylinder" + } + ListElement { + modelName: qsTr("Sphere") + modelStr: "#Sphere" + } } + + Repeater { + model: modelMenuModel + StudioControls.MenuItemWithIcon { + text: modelName + onClicked: { + // Force property change notifications to keep check mark when reselected + root.previewModel = "" + root.previewModel = modelStr + } + checkable: true + checked: root.previewModel === modelStr + } + } + } + + StudioControls.Menu { + id: envMenu + closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside + + ListModel { + id: envMenuModel + ListElement { + envName: qsTr("Default") + envStr: "Default" + } + ListElement { + envName: qsTr("Color") + envStr: "Color" + } + ListElement { + envName: qsTr("Studio") + envStr: "SkyBox=preview_studio" + } + ListElement { + envName: qsTr("Landscape") + envStr: "SkyBox=preview_landscape" + } + } + + Repeater { + model: envMenuModel + StudioControls.MenuItemWithIcon { + text: envName + onClicked: { + // Force property change notifications to keep check mark when reselected + root.previewEnv = "" + root.previewEnv = envStr + } + checkable: true + checked: root.previewEnv === envStr + } + } + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: previewRect.height + + Rectangle { + id: previewRect + anchors.horizontalCenter: parent.horizontalCenter + width: 152 + height: 152 + color: "#000000" + + Image { + id: materialPreview + width: 150 + height: 150 + anchors.centerIn: parent + source: "image://materialEditor/preview" + cache: false + } + } + + Item { + id: previewOptions + width: 40 + height: previewRect.height + anchors.top: previewRect.top + anchors.left: previewRect.right + + Column { + anchors.horizontalCenter: parent.horizontalCenter + IconButton { + icon: StudioTheme.Constants.materialPreviewEnvironment + iconSize: StudioTheme.Values.bigIconFontSize + buttonSize: previewOptions.width + tooltip: qsTr("Select preview environment.") + onClicked: envMenu.popup() + } + IconButton { + icon: StudioTheme.Constants.materialPreviewModel + iconSize: StudioTheme.Values.bigIconFontSize + buttonSize: previewOptions.width + tooltip: qsTr("Select preview model.") + onClicked: modelMenu.popup() + } + } + } + } Section { @@ -103,17 +227,8 @@ Column { Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } ComboBox { - currentIndex: { - if (backendValues.__classNamePrivateInternal.value === "CustomMaterial") - return 2 - - if (backendValues.__classNamePrivateInternal.value === "PrincipledMaterial") - return 1 - - return 0 - } - - model: ["DefaultMaterial", "PrincipledMaterial", "CustomMaterial"] + currentIndex: possibleTypeIndex + model: possibleTypes showExtendedFunctionButton: false implicitWidth: StudioTheme.Values.singleControlColumnWidth diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml index dcd4733134a..9593213e347 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml @@ -51,13 +51,6 @@ Rectangle { : mouseArea.containsMouse ? hoverColor : normalColor - Behavior on color { - ColorAnimation { - duration: 300 - easing.type: Easing.OutQuad - } - } - Text { id: icon diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml index 8f0079249db..c50d2e1d68e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml @@ -136,7 +136,7 @@ Item { }, State { name: "hover" - when: searchFilterText.hovered && !searchFilterText.activeFocus + when: root.enabled && searchFilterText.hovered && !searchFilterText.activeFocus PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundHover diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 5972e56e04c..46b3679198c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -122,72 +122,74 @@ QtObject { readonly property string listView: "\u0075" readonly property string lockOff: "\u0076" readonly property string lockOn: "\u0077" - readonly property string mergeCells: "\u0078" - readonly property string minus: "\u0079" - readonly property string mirror: "\u007A" - readonly property string newMaterial: "\u007B" - readonly property string openMaterialBrowser: "\u007C" - readonly property string orientation: "\u007D" - readonly property string paddingEdge: "\u007E" - readonly property string paddingFrame: "\u007F" - readonly property string pasteStyle: "\u0080" - readonly property string pause: "\u0081" - readonly property string pin: "\u0082" - readonly property string play: "\u0083" - readonly property string plus: "\u0084" - readonly property string promote: "\u0085" - readonly property string readOnly: "\u0086" - readonly property string redo: "\u0087" - readonly property string rotationFill: "\u0088" - readonly property string rotationOutline: "\u0089" - readonly property string search: "\u008A" - readonly property string sectionToggle: "\u008B" - readonly property string splitColumns: "\u008C" - readonly property string splitRows: "\u008D" - readonly property string startNode: "\u008E" - readonly property string testIcon: "\u008F" - readonly property string textAlignBottom: "\u0090" - readonly property string textAlignCenter: "\u0091" - readonly property string textAlignJustified: "\u0092" - readonly property string textAlignLeft: "\u0093" - readonly property string textAlignMiddle: "\u0094" - readonly property string textAlignRight: "\u0095" - readonly property string textAlignTop: "\u0096" - readonly property string textBulletList: "\u0097" - readonly property string textFullJustification: "\u0098" - readonly property string textNumberedList: "\u0099" - readonly property string tickIcon: "\u009A" - readonly property string translationCreateFiles: "\u009B" - readonly property string translationCreateReport: "\u009D" - readonly property string translationExport: "\u009E" - readonly property string translationImport: "\u009F" - readonly property string translationSelectLanguages: "\u00A0" - readonly property string translationTest: "\u00A1" - readonly property string transparent: "\u00A2" - readonly property string triState: "\u00A3" - readonly property string triangleArcA: "\u00A4" - readonly property string triangleArcB: "\u00A5" - readonly property string triangleCornerA: "\u00A6" - readonly property string triangleCornerB: "\u00A7" - readonly property string unLinked: "\u00A8" - readonly property string undo: "\u00A9" - readonly property string unpin: "\u00AA" - readonly property string upDownIcon: "\u00AB" - readonly property string upDownSquare2: "\u00AC" - readonly property string visibilityOff: "\u00AE" - readonly property string visibilityOn: "\u00AF" - readonly property string wildcard: "\u00B0" - readonly property string wizardsAutomotive: "\u00B1" - readonly property string wizardsDesktop: "\u00B2" - readonly property string wizardsGeneric: "\u00B3" - readonly property string wizardsMcuEmpty: "\u00B4" - readonly property string wizardsMcuGraph: "\u00B5" - readonly property string wizardsMobile: "\u00B6" - readonly property string wizardsUnknown: "\u00B7" - readonly property string zoomAll: "\u00B8" - readonly property string zoomIn: "\u00B9" - readonly property string zoomOut: "\u00BA" - readonly property string zoomSelection: "\u00BB" + readonly property string materialPreviewEnvironment: "\u0078" + readonly property string materialPreviewModel: "\u0079" + readonly property string mergeCells: "\u007A" + readonly property string minus: "\u007B" + readonly property string mirror: "\u007C" + readonly property string newMaterial: "\u007D" + readonly property string openMaterialBrowser: "\u007E" + readonly property string orientation: "\u007F" + readonly property string paddingEdge: "\u0080" + readonly property string paddingFrame: "\u0081" + readonly property string pasteStyle: "\u0082" + readonly property string pause: "\u0083" + readonly property string pin: "\u0084" + readonly property string play: "\u0085" + readonly property string plus: "\u0086" + readonly property string promote: "\u0087" + readonly property string readOnly: "\u0088" + readonly property string redo: "\u0089" + readonly property string rotationFill: "\u008A" + readonly property string rotationOutline: "\u008B" + readonly property string search: "\u008C" + readonly property string sectionToggle: "\u008D" + readonly property string splitColumns: "\u008E" + readonly property string splitRows: "\u008F" + readonly property string startNode: "\u0090" + readonly property string testIcon: "\u0091" + readonly property string textAlignBottom: "\u0092" + readonly property string textAlignCenter: "\u0093" + readonly property string textAlignJustified: "\u0094" + readonly property string textAlignLeft: "\u0095" + readonly property string textAlignMiddle: "\u0096" + readonly property string textAlignRight: "\u0097" + readonly property string textAlignTop: "\u0098" + readonly property string textBulletList: "\u0099" + readonly property string textFullJustification: "\u009A" + readonly property string textNumberedList: "\u009B" + readonly property string tickIcon: "\u009D" + readonly property string translationCreateFiles: "\u009E" + readonly property string translationCreateReport: "\u009F" + readonly property string translationExport: "\u00A0" + readonly property string translationImport: "\u00A1" + readonly property string translationSelectLanguages: "\u00A2" + readonly property string translationTest: "\u00A3" + readonly property string transparent: "\u00A4" + readonly property string triState: "\u00A5" + readonly property string triangleArcA: "\u00A6" + readonly property string triangleArcB: "\u00A7" + readonly property string triangleCornerA: "\u00A8" + readonly property string triangleCornerB: "\u00A9" + readonly property string unLinked: "\u00AA" + readonly property string undo: "\u00AB" + readonly property string unpin: "\u00AC" + readonly property string upDownIcon: "\u00AE" + readonly property string upDownSquare2: "\u00AF" + readonly property string visibilityOff: "\u00B0" + readonly property string visibilityOn: "\u00B1" + readonly property string wildcard: "\u00B2" + readonly property string wizardsAutomotive: "\u00B3" + readonly property string wizardsDesktop: "\u00B4" + readonly property string wizardsGeneric: "\u00B5" + readonly property string wizardsMcuEmpty: "\u00B6" + readonly property string wizardsMcuGraph: "\u00B7" + readonly property string wizardsMobile: "\u00B8" + readonly property string wizardsUnknown: "\u00B9" + readonly property string zoomAll: "\u00BA" + readonly property string zoomIn: "\u00BB" + readonly property string zoomOut: "\u00BC" + readonly property string zoomSelection: "\u00BD" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index cefd1d7d866..934c22110e5 100644 Binary files a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf and b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf differ diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 3e7ef11beb7..6912200c14b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -318,8 +318,7 @@ }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", - "openInEditor": true + "target": "%{ProjectDirectory}/content/Screen01.ui.qml" }, { "source": "../common/fonts.txt", diff --git a/src/libs/advanceddockingsystem/dockoverlay.cpp b/src/libs/advanceddockingsystem/dockoverlay.cpp index f98c3fbd5ff..046e02dcb81 100644 --- a/src/libs/advanceddockingsystem/dockoverlay.cpp +++ b/src/libs/advanceddockingsystem/dockoverlay.cpp @@ -421,6 +421,7 @@ namespace ADS { d->m_lastLocation = InvalidDockWidgetArea; // Move it over the target. + hide(); resize(target->size()); QPoint topLeft = target->mapToGlobal(target->rect().topLeft()); move(topLeft); diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 6640ee2c1ca..cd223e84723 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -612,7 +612,7 @@ class UnsupportedTypesByVisualDesigner : public QStringList { public: UnsupportedTypesByVisualDesigner() - : QStringList({"Timer", "Package", "Particles", "ApplicationWindow"}) + : QStringList({"Package", "Particles", "ApplicationWindow"}) {} }; diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp index 37986607392..1625c44e892 100644 --- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp @@ -175,6 +175,11 @@ bool JLinkGdbServerProvider::operator==(const IDebugServerProvider &other) const const auto p = static_cast(&other); return m_executableFile == p->m_executableFile + && m_jlinkDevice == p->m_jlinkDevice + && m_jlinkHost == p->m_jlinkHost + && m_jlinkHostAddr == p->m_jlinkHostAddr + && m_jlinkTargetIface == p->m_jlinkTargetIface + && m_jlinkTargetIfaceSpeed == p->m_jlinkTargetIfaceSpeed && m_additionalArguments == p->m_additionalArguments; } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index d8f61a37078..008b42f6a66 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -157,6 +157,20 @@ static void checkSystemForClangdSuitability() Core::ICore::infoBar()->addInfo(info); } +static void updateParserConfig(ClangdClient *client) +{ + if (!client->reachable()) + return; + if (const auto editor = TextEditor::BaseTextEditor::currentTextEditor()) { + if (!client->documentOpen(editor->textDocument())) + return; + const Utils::FilePath filePath = editor->textDocument()->filePath(); + if (const auto processor = ClangEditorDocumentProcessor::get(filePath.toString())) + client->updateParserConfig(filePath, processor->parserConfig()); + } +} + + ClangModelManagerSupport::ClangModelManagerSupport() { watchForExternalChanges(); @@ -382,6 +396,13 @@ void ClangModelManagerSupport::updateLanguageClient( if (Client * const oldClient = clientForProject(project)) LanguageClientManager::shutdownClient(oldClient); ClangdClient * const client = new ClangdClient(project, jsonDbDir); + connect(client, &Client::shadowDocumentSwitched, this, [](const Utils::FilePath &fp) { + ClangdClient::handleUiHeaderChange(fp.fileName()); + }); + connect(CppModelManager::instance(), + &CppModelManager::projectPartsUpdated, + client, + [client] { updateParserConfig(client); }); connect(client, &Client::initialized, this, [this, client, project, projectInfo, jsonDbDir] { using namespace ProjectExplorer; if (!SessionManager::hasProject(project)) @@ -393,25 +414,15 @@ void ClangModelManagerSupport::updateLanguageClient( if (!newProjectInfo || *newProjectInfo != *projectInfo) return; - const auto updateParserConfig = [client] { - if (const auto editor = TextEditor::BaseTextEditor::currentTextEditor()) { - if (!client->documentOpen(editor->textDocument())) - return; - const Utils::FilePath filePath = editor->textDocument()->filePath(); - if (const auto processor = ClangEditorDocumentProcessor::get( - filePath.toString())) { - const CppEditor::BaseEditorDocumentParser::Configuration config - = processor->parserConfig(); - client->updateParserConfig(filePath, config); - } - } - }; - // Acquaint the client with all open C++ documents for this project. bool hasDocuments = false; const ClangdSettings settings(ClangdProjectSettings(project).settings()); for (TextEditor::TextDocument * const doc : allCppDocuments()) { Client * const currentClient = LanguageClientManager::clientForDocument(doc); + if (currentClient == client) { + hasDocuments = true; + continue; + } if (!settings.sizeIsOkay(doc->filePath())) continue; const Project * const docProject = SessionManager::projectForFile(doc->filePath()); @@ -440,17 +451,8 @@ void ClangModelManagerSupport::updateLanguageClient( ++it; } } - connect(client, &Client::shadowDocumentSwitched, this, - [](const Utils::FilePath &fp) { - ClangdClient::handleUiHeaderChange(fp.fileName()); - }); - if (client->state() == Client::Initialized) - updateParserConfig(); - else - connect(client, &Client::initialized, client, updateParserConfig); - connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated, - client, updateParserConfig); + updateParserConfig(client); if (hasDocuments) return; diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 22b55170a6f..4ee5daebdba 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -66,7 +66,6 @@ void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style, if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) return; - style.DisableFormat = false; style.ColumnLimit = 0; #ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED style.KeepLineBreaksForNonEmptyLines = true; diff --git a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp index 46925e8edba..9bdf6636867 100644 --- a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp +++ b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp @@ -435,7 +435,6 @@ public: private: void onBinaryPathEditingFinished(); void updateQchFilePath(); - void reload(); CMakeToolItemModel *m_model; QLineEdit *m_displayNameLineEdit; @@ -501,7 +500,6 @@ void CMakeToolItemConfigWidget::onBinaryPathEditingFinished() { updateQchFilePath(); store(); - reload(); } void CMakeToolItemConfigWidget::updateQchFilePath() @@ -510,18 +508,6 @@ void CMakeToolItemConfigWidget::updateQchFilePath() m_qchFileChooser->setFilePath(CMakeTool::searchQchFile(m_binaryChooser->filePath())); } -void CMakeToolItemConfigWidget::reload() -{ - if (!m_id.isValid()) - return; - - const CMakeToolTreeItem *item = m_model->cmakeToolItem(m_id); - if (!item) - return; - - load(item); -} - void CMakeToolItemConfigWidget::load(const CMakeToolTreeItem *item) { m_loadingItem = true; // avoid intermediate signal handling diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp index 505cb4ee0a1..d663b227bca 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp @@ -473,16 +473,16 @@ void CompilerOptionsBuilder::addLanguageVersionAndExtensions() default: break; case LanguageVersion::CXX14: - option = "-clang:std=c++14"; + option = "-clang:-std=c++14"; break; case LanguageVersion::CXX17: - option = "-clang:std=c++17"; + option = "-clang:-std=c++17"; break; case LanguageVersion::CXX20: - option = "-clang:std=c++20"; + option = "-clang:-std=c++20"; break; case LanguageVersion::CXX2b: - option = "-clang:std=c++2b"; + option = "-clang:-std=c++2b"; break; } diff --git a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp index 65d18291849..6131ce5f023 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp @@ -192,7 +192,7 @@ void CompilerOptionsBuilderTest::testLanguageVersionIsExplicitlySetIfNotProvided UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); - QVERIFY(compilerOptionsBuilder.options().contains("-clang:std=c++17")); + QVERIFY(compilerOptionsBuilder.options().contains("-clang:-std=c++17")); } void CompilerOptionsBuilderTest::testAddWordWidth() @@ -633,7 +633,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvc() [&t](const QString &o) { return o.contains(t.toNative("wrappedQtHeaders/QtCore")); }); QCOMPARE(compilerOptionsBuilder.options(), (QStringList{"-nostdinc", "-nostdinc++", "--driver-mode=cl", "/Zs", "-m64", - "--target=x86_64-apple-darwin10", "/TP", "-clang:std=c++17", + "--target=x86_64-apple-darwin10", "/TP", "-clang:-std=c++17", "-fms-compatibility-version=19.00", "-DprojectFoo=projectBar", "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"", @@ -662,7 +662,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvcWithExceptions() [&t](const QString &o) { return o.contains(t.toNative("wrappedQtHeaders/QtCore")); }); QCOMPARE(compilerOptionsBuilder.options(), (QStringList{"-nostdinc", "-nostdinc++", "--driver-mode=cl", "/Zs", "-m64", - "--target=x86_64-apple-darwin10", "/TP", "-clang:std=c++17", "-fcxx-exceptions", + "--target=x86_64-apple-darwin10", "/TP", "-clang:-std=c++17", "-fcxx-exceptions", "-fexceptions", "-fms-compatibility-version=19.00", "-DprojectFoo=projectBar", "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index fad28d90a81..41f343c11bb 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -378,7 +378,6 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, QTextCursor cursor(&textDocument); cursor.setPosition(position); - // Are we at the second argument of a function call? const QList path = ASTPath(document)(cursor); if (path.isEmpty()) return SignalSlotType::None; @@ -388,9 +387,27 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, break; } - // Is the function called "connect" or "disconnect"? if (!callAst || !callAst->base_expression) return SignalSlotType::None; + + const int argumentPosition = argumentPositionOf(path.last(), callAst); + if (argumentPosition != 2 && argumentPosition != 4) + return SignalSlotType::None; + + const NameAST *nameAst = nullptr; + if (const IdExpressionAST * const idAst = callAst->base_expression->asIdExpression()) + nameAst = idAst->name; + else if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) + nameAst = ast->member_name; + if (!nameAst || !nameAst->name) + return SignalSlotType::None; + const Identifier * const id = nameAst->name->identifier(); + if (!id) + return SignalSlotType::None; + const QString funcName = QString::fromUtf8(id->chars(), id->size()); + if (funcName != "connect" && funcName != "disconnect") + return SignalSlotType::None; + Scope *scope = document->globalNamespace(); for (auto it = path.crbegin(); it != path.crend(); ++it) { if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) { @@ -398,12 +415,8 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, break; } } - const NameAST *nameAst = nullptr; const LookupContext context(document, snapshot); - if (const IdExpressionAST * const idAst = callAst->base_expression->asIdExpression()) { - nameAst = idAst->name; - } else if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) { - nameAst = ast->member_name; + if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) { TypeOfExpression exprType; exprType.setExpandTemplates(true); exprType.init(document, snapshot); @@ -433,14 +446,6 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, if (!scope) return SignalSlotType::None; } - if (!nameAst || !nameAst->name) - return SignalSlotType::None; - const Identifier * const id = nameAst->name->identifier(); - if (!id) - return SignalSlotType::None; - const QString funcName = QString::fromUtf8(id->chars(), id->size()); - if (funcName != "connect" && funcName != "disconnect") - return SignalSlotType::None; // Is the function a member function of QObject? const QList matches = context.lookup(nameAst->name, scope); @@ -463,10 +468,8 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, expression = expressionUnderCursor(cursor); - const int argumentPosition = argumentPositionOf(path.last(), callAst); - if ((expression.endsWith(QLatin1String("SIGNAL")) - && (argumentPosition == 2 || argumentPosition == 4)) - || (expression.endsWith(QLatin1String("SLOT")) && argumentPosition == 4)) + if (expression.endsWith(QLatin1String("SIGNAL")) + || (expression.endsWith(QLatin1String("SLOT")) && argumentPosition == 4)) return SignalSlotType::OldStyleSignal; if (argumentPosition == 2) diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 37f4bc9441b..bcb6cac7d79 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -5247,10 +5247,12 @@ ExtractFunction::ExtractFunction(FunctionNameGetter functionNameGetter) void ExtractFunction::match(const CppQuickFixInterface &interface, QuickFixOperations &result) { const CppRefactoringFilePtr file = interface.currentFile(); - if (CppModelManager::usesClangd(file->editor()->textDocument()) - && file->cppDocument()->languageFeatures().cxxEnabled) { - return; - } + + // TODO: Fix upstream and uncomment; see QTCREATORBUG-28030. +// if (CppModelManager::usesClangd(file->editor()->textDocument()) +// && file->cppDocument()->languageFeatures().cxxEnabled) { +// return; +// } QTextCursor cursor = file->cursor(); if (!cursor.hasSelection()) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index c624b13979d..7ffc811cb3e 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -283,7 +283,17 @@ public: QString m_displayName; LanguageFilter m_languagFilter; QJsonObject m_initializationOptions; - QMap m_openedDocument; + class OpenedDocument + { + public: + ~OpenedDocument() + { + QObject::disconnect(contentsChangedConnection); + } + QMetaObject::Connection contentsChangedConnection; + QString documentContents; + }; + QMap m_openedDocument; // Used for build system artifacts (e.g. UI headers) that Qt Creator "live-generates" ahead of // the build. @@ -618,11 +628,14 @@ void Client::openDocument(TextEditor::TextDocument *document) } } - d->m_openedDocument[document] = document->plainText(); - connect(document, &TextDocument::contentsChangedWithPosition, this, - [this, document](int position, int charsRemoved, int charsAdded) { - documentContentsChanged(document, position, charsRemoved, charsAdded); - }); + d->m_openedDocument[document].documentContents = document->plainText(); + d->m_openedDocument[document].contentsChangedConnection + = connect(document, + &TextDocument::contentsChangedWithPosition, + this, + [this, document](int position, int charsRemoved, int charsAdded) { + documentContentsChanged(document, position, charsRemoved, charsAdded); + }); if (!d->m_documentVersions.contains(filePath)) d->m_documentVersions[filePath] = 0; d->sendOpenNotification(filePath, document->mimeType(), document->plainText(), @@ -1083,7 +1096,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, } } if (append) { - QTextDocument oldDoc(d->m_openedDocument[document]); + QTextDocument oldDoc(d->m_openedDocument[document].documentContents); QTextCursor cursor(&oldDoc); // Workaround https://bugreports.qt.io/browse/QTBUG-80662 // The contentsChanged gives a character count that can be wrong for QTextCursor @@ -1104,7 +1117,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, d->m_documentsToUpdate[document] = { DidChangeTextDocumentParams::TextDocumentContentChangeEvent(document->plainText())}; } - d->m_openedDocument[document] = document->plainText(); + d->m_openedDocument[document].documentContents = document->plainText(); } ++d->m_documentVersions[document->filePath()]; @@ -1532,8 +1545,6 @@ bool ClientPrivate::reset() m_dynamicCapabilities.reset(); if (m_diagnosticManager) m_diagnosticManager->clearDiagnostics(); - for (auto it = m_openedDocument.cbegin(); it != m_openedDocument.cend(); ++it) - it.key()->disconnect(this); m_openedDocument.clear(); // temporary container needed since m_resetAssistProvider is changed in resetAssistProviders for (TextEditor::TextDocument *document : m_resetAssistProvider.keys()) @@ -1544,7 +1555,8 @@ bool ClientPrivate::reset() qDeleteAll(m_documentHighlightsTimer); m_documentHighlightsTimer.clear(); m_progressManager.reset(); - m_shadowDocuments.clear(); + for (auto &doc : m_shadowDocuments) + doc.second.clear(); m_documentVersions.clear(); return true; } diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index 9775fc89824..302af8a8fc5 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -117,13 +117,13 @@ public: if (python.exists()) PyLSConfigureAssistant::openDocumentWithPython(python, this); }); + connect(this, &PythonDocument::openFinishedSuccessfully, + this, &PythonDocument::checkForPyls); } - void setFilePath(const FilePath &filePath) override + void checkForPyls() { - TextEditor::TextDocument::setFilePath(filePath); - - const FilePath &python = detectPython(filePath); + const FilePath &python = detectPython(filePath()); if (!python.exists()) return; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6fd9e7e1942..517d0594f4d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -595,7 +595,7 @@ extend_qtc_plugin(QmlDesigner easingcurve.cpp easingcurve.h easingcurvedialog.cpp easingcurvedialog.h preseteditor.cpp preseteditor.h - setframevaluedialog.cpp setframevaluedialog.h setframevaluedialog.ui + setframevaluedialog.cpp setframevaluedialog.h splineeditor.cpp splineeditor.h timeline.qrc timelineabstracttool.cpp timelineabstracttool.h diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 00dc1f50f72..31c4971ed33 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -129,11 +129,13 @@ void DesignerActionManager::polishActions() const Core::Context qmlDesignerFormEditorContext(Constants::C_QMLFORMEDITOR); Core::Context qmlDesignerEditor3DContext(Constants::C_QMLEDITOR3D); Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); + Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerUIContext; qmlDesignerUIContext.add(qmlDesignerFormEditorContext); qmlDesignerUIContext.add(qmlDesignerEditor3DContext); qmlDesignerUIContext.add(qmlDesignerNavigatorContext); + qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index d8853cc7906..7707cdeb12b 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -130,6 +130,8 @@ public: listView, lockOff, lockOn, + materialPreviewEnvironment, + materialPreviewModel, mergeCells, minus, mirror, diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp index f8587df06fe..cb6811c9750 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp @@ -85,7 +85,7 @@ void ColorControl::mouseReleaseEvent(QMouseEvent *event) event->accept(); - if (color != m_color) { + if (color.isValid() && color != m_color) { m_color = color; update(); emit valueChanged(); diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp index a4c117ae041..25393de5a8b 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp @@ -27,7 +27,10 @@ #include "curveitem.h" #include "handleitem.h" +#include #include +#include +#include #include @@ -452,10 +455,22 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons lseg.moveRightTo(position); rseg.moveLeftTo(position); - if (legalLeft() && legalRight()) - m_validPos = position; + if (legalLeft() && legalRight()) { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) { + if (m_firstPos) { + auto firstToNow = QLineF(*m_firstPos, position); + if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy())) + m_validPos = QPointF(position.x(), m_firstPos->y()); + else + m_validPos = QPointF(m_firstPos->x(), position.y()); + } - return QVariant(m_transform.map(m_validPos)); + } else { + m_validPos = position; + } + } + + return QVariant(m_transform.map(*m_validPos)); } } } @@ -465,6 +480,11 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons void KeyframeItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + bool ok; + m_firstPos = m_transform.inverted(&ok).map(event->scenePos()); + if (!ok) + m_firstPos = Utils::nullopt; + SelectableItem::mousePressEvent(event); if (auto *curveItem = qgraphicsitem_cast(parentItem())) curveItem->setHandleVisibility(false); @@ -472,6 +492,7 @@ void KeyframeItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void KeyframeItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + m_firstPos = Utils::nullopt; SelectableItem::mouseReleaseEvent(event); if (auto *curveItem = qgraphicsitem_cast(parentItem())) curveItem->setHandleVisibility(true); diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h index 14c3ae74266..c7bfa02cd18 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h @@ -30,6 +30,8 @@ #include "keyframe.h" #include "selectableitem.h" +#include + #include namespace QmlDesigner { @@ -133,7 +135,8 @@ private: HandleItem *m_right; - QPointF m_validPos; + Utils::optional< QPointF > m_firstPos; + Utils::optional< QPointF > m_validPos; bool m_visibleOverride = true; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index 49f313534d1..4027a1c75ae 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -24,9 +24,13 @@ ****************************************************************************/ #include "materialbrowsermodel.h" -#include "variantproperty.h" + +#include #include #include +#include +#include "variantproperty.h" +#include "utils/qtcassert.h" namespace QmlDesigner { @@ -46,24 +50,27 @@ int MaterialBrowserModel::rowCount(const QModelIndex &) const QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= m_materialList.count()) { - qWarning() << Q_FUNC_INFO << "invalid index requested"; - return {}; - } + QTC_ASSERT(index.isValid() && index.row() < m_materialList.count(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); - if (roleNames().value(role) == "materialName") { + QByteArray roleName = roleNames().value(role); + if (roleName == "materialName") { QVariant objName = m_materialList.at(index.row()).variantProperty("objectName").value(); return objName.isValid() ? objName : ""; } - if (roleNames().value(role) == "materialInternalId") + if (roleName == "materialInternalId") return m_materialList.at(index.row()).internalId(); - if (roleNames().value(role) == "materialVisible") + if (roleName == "materialVisible") return isMaterialVisible(index.row()); - if (!roleNames().contains(role)) - qWarning() << Q_FUNC_INFO << "invalid role requested"; + if (roleName == "materialType") { + QString matType = QString::fromLatin1(m_materialList.at(index.row()).type()); + if (matType.startsWith("QtQuick3D.")) + matType.remove("QtQuick3D."); + return matType; + } return {}; } @@ -82,12 +89,57 @@ bool MaterialBrowserModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } +/** + * @brief Loads and parses propertyGroups.json from QtQuick3D module's designer folder + * + * propertyGroups.json contains lists of QtQuick3D objects' properties grouped by sections + * + * @param path path to propertyGroups.json file + */ +void MaterialBrowserModel::loadPropertyGroups(const QString &path) +{ + bool ok = true; + + if (m_propertyGroupsObj.isEmpty()) { + QFile matPropsFile(path); + + if (!matPropsFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open propertyGroups.json"); + ok = false; + } + + if (ok) { + QJsonDocument matPropsJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); + if (matPropsJsonDoc.isNull()) { + qWarning("Invalid propertyGroups.json file"); + ok = false; + } else { + m_propertyGroupsObj = matPropsJsonDoc.object(); + } + } + } + + m_defaultMaterialSections.clear(); + m_principledMaterialSections.clear(); + m_customMaterialSections.clear(); + if (ok) { + m_defaultMaterialSections.append(m_propertyGroupsObj.value("DefaultMaterial").toObject().keys()); + m_principledMaterialSections.append(m_propertyGroupsObj.value("PrincipledMaterial").toObject().keys()); + + QStringList customMatSections = m_propertyGroupsObj.value("CustomMaterial").toObject().keys(); + if (customMatSections.size() > 1) // as of now custom material has only 1 section, so we don't add it + m_customMaterialSections.append(customMatSections); + } + emit materialSectionsChanged(); +} + QHash MaterialBrowserModel::roleNames() const { static const QHash roles { {Qt::UserRole + 1, "materialName"}, {Qt::UserRole + 2, "materialInternalId"}, {Qt::UserRole + 3, "materialVisible"}, + {Qt::UserRole + 4, "materialType"} }; return roles; } @@ -120,6 +172,34 @@ void MaterialBrowserModel::setHasModelSelection(bool b) emit hasModelSelectionChanged(); } +bool MaterialBrowserModel::hasMaterialRoot() const +{ + return m_hasMaterialRoot; +} + +void MaterialBrowserModel::setHasMaterialRoot(bool b) +{ + if (m_hasMaterialRoot == b) + return; + + m_hasMaterialRoot = b; + emit hasMaterialRootChanged(); +} + +QString MaterialBrowserModel::copiedMaterialType() const +{ + return m_copiedMaterialType; +} + +void MaterialBrowserModel::setCopiedMaterialType(const QString &matType) +{ + if (matType == m_copiedMaterialType) + return; + + m_copiedMaterialType = matType; + emit copiedMaterialTypeChanged(); +} + QList MaterialBrowserModel::materials() const { return m_materialList; @@ -201,6 +281,12 @@ void MaterialBrowserModel::removeMaterial(const ModelNode &material) } } +void MaterialBrowserModel::deleteSelectedMaterial() +{ + if (isValidIndex(m_selectedIndex)) + m_materialList[m_selectedIndex].destroy(); +} + void MaterialBrowserModel::updateSelectedMaterial() { selectMaterial(m_selectedIndex, true); @@ -256,6 +342,42 @@ void MaterialBrowserModel::duplicateMaterial(int idx) emit duplicateMaterialTriggered(m_materialList.at(idx)); } +void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §ion) +{ + ModelNode mat = m_materialList.at(idx); + QString matType = QString::fromLatin1(mat.type()); + + if (matType.startsWith("QtQuick3D.")) + matType.remove("QtQuick3D."); + + setCopiedMaterialType(matType); + m_allPropsCopied = section == "All"; + + if (m_allPropsCopied || m_propertyGroupsObj.empty()) { + m_copiedMaterialProps = mat.properties(); + } else { + QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); + if (propsSpecObj.contains(section)) { // should always be true + m_copiedMaterialProps.clear(); + const QJsonArray propNames = propsSpecObj.value(section).toArray(); + for (const QJsonValueRef &propName : propNames) + m_copiedMaterialProps.append(mat.property(propName.toString().toLatin1())); + + if (section == "Base") { // add QtQuick3D.Material base props as well + QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject(); + const QJsonArray propNames = propsMatObj.value("Base").toArray(); + for (const QJsonValueRef &propName : propNames) + m_copiedMaterialProps.append(mat.property(propName.toString().toLatin1())); + } + } + } +} + +void MaterialBrowserModel::pasteMaterialProperties(int idx) +{ + emit pasteMaterialPropertiesTriggered(m_materialList.at(idx), m_copiedMaterialProps, m_allPropsCopied); +} + void MaterialBrowserModel::deleteMaterial(int idx) { m_materialList[idx].destroy(); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 73e6b2f99c6..5f38e7488eb 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -25,7 +25,9 @@ #pragma once +#include "qjsonobject.h" #include +#include #include #include @@ -41,6 +43,11 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) + Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged) + Q_PROPERTY(QString copiedMaterialType READ copiedMaterialType WRITE setCopiedMaterialType NOTIFY copiedMaterialTypeChanged) + Q_PROPERTY(QStringList defaultMaterialSections MEMBER m_defaultMaterialSections NOTIFY materialSectionsChanged) + Q_PROPERTY(QStringList principledMaterialSections MEMBER m_principledMaterialSections NOTIFY materialSectionsChanged) + Q_PROPERTY(QStringList customMaterialSections MEMBER m_customMaterialSections NOTIFY materialSectionsChanged) public: MaterialBrowserModel(QObject *parent = nullptr); @@ -58,18 +65,28 @@ public: bool hasModelSelection() const; void setHasModelSelection(bool b); + bool hasMaterialRoot() const; + void setHasMaterialRoot(bool b); + + QString copiedMaterialType() const; + void setCopiedMaterialType(const QString &matType); + QList materials() const; void setMaterials(const QList &materials, bool hasQuick3DImport); void removeMaterial(const ModelNode &material); + void deleteSelectedMaterial(); void updateMaterialName(const ModelNode &material); void updateSelectedMaterial(); int materialIndex(const ModelNode &material) const; ModelNode materialAt(int idx) const; + void loadPropertyGroups(const QString &path); void resetModel(); Q_INVOKABLE void selectMaterial(int idx, bool force = false); Q_INVOKABLE void duplicateMaterial(int idx); + Q_INVOKABLE void copyMaterialProperties(int idx, const QString §ion); + Q_INVOKABLE void pasteMaterialProperties(int idx); Q_INVOKABLE void deleteMaterial(int idx); Q_INVOKABLE void renameMaterial(int idx, const QString &newName); Q_INVOKABLE void addNewMaterial(); @@ -80,11 +97,17 @@ signals: void isEmptyChanged(); void hasQuick3DImportChanged(); void hasModelSelectionChanged(); + void hasMaterialRootChanged(); + void copiedMaterialTypeChanged(); + void materialSectionsChanged(); void selectedIndexChanged(int idx); void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName); void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false); void addNewMaterialTriggered(); void duplicateMaterialTriggered(const QmlDesigner::ModelNode &material); + void pasteMaterialPropertiesTriggered(const QmlDesigner::ModelNode &material, + const QList &props, + bool all); private: bool isMaterialVisible(int idx) const; @@ -92,12 +115,20 @@ private: QString m_searchText; QList m_materialList; + QStringList m_defaultMaterialSections; + QStringList m_principledMaterialSections; + QStringList m_customMaterialSections; + QList m_copiedMaterialProps; QHash m_materialIndexHash; // internalId -> index + QJsonObject m_propertyGroupsObj; int m_selectedIndex = 0; bool m_isEmpty = true; bool m_hasQuick3DImport = false; bool m_hasModelSelection = false; + bool m_hasMaterialRoot = false; + bool m_allPropsCopied = true; + QString m_copiedMaterialType; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 08aa1f420ce..00e7526cf6f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -24,12 +24,17 @@ ****************************************************************************/ #include "materialbrowserview.h" + +#include "bindingproperty.h" #include "materialbrowserwidget.h" #include "materialbrowsermodel.h" #include "nodeabstractproperty.h" +#include "nodemetainfo.h" #include "qmlobjectnode.h" #include "variantproperty.h" + #include +#include #include #include @@ -39,7 +44,6 @@ namespace QmlDesigner { MaterialBrowserView::MaterialBrowserView(QObject *parent) : AbstractView(parent) - {} MaterialBrowserView::~MaterialBrowserView() @@ -53,7 +57,11 @@ bool MaterialBrowserView::hasWidget() const WidgetInfo MaterialBrowserView::widgetInfo() { if (m_widget.isNull()) { - m_widget = new MaterialBrowserWidget; + m_widget = new MaterialBrowserWidget(this); + + auto matEditorContext = new Internal::MaterialBrowserContext(m_widget.data()); + Core::ICore::addContextObject(matEditorContext); + MaterialBrowserModel *matBrowserModel = m_widget->materialBrowserModel().data(); // custom notifications below are sent to the MaterialEditor @@ -81,6 +89,32 @@ WidgetInfo MaterialBrowserView::widgetInfo() [&] (const ModelNode &material) { emitCustomNotification("duplicate_material", {material}); }); + + connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this, + [&] (const ModelNode &material, const QList &props, bool all) { + QmlObjectNode mat(material); + executeInTransaction(__FUNCTION__, [&] { + if (all) { // all material properties copied + // remove current properties + const PropertyNameList propNames = material.propertyNames(); + for (const PropertyName &propName : propNames) { + if (propName != "objectName") + mat.removeProperty(propName); + } + } + + // apply pasted properties + for (const AbstractProperty &prop : props) { + if (prop.name() == "objectName") + continue; + + if (prop.isVariantProperty()) + mat.setVariantProperty(prop.name(), prop.toVariantProperty().value()); + else if (prop.isBindingProperty()) + mat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); + } + }); + }); } return createWidgetInfo(m_widget.data(), @@ -94,7 +128,12 @@ void MaterialBrowserView::modelAttached(Model *model) { AbstractView::modelAttached(model); + QString matPropsPath = model->metaInfo("QtQuick3D.Material").importDirectoryPath() + + "/designer/propertyGroups.json"; + m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); + m_widget->clearSearchFilter(); + m_widget->materialBrowserModel()->setHasMaterialRoot(rootModelNode().isSubclassOf("QtQuick3D.Material")); m_hasQuick3DImport = model->hasImport("QtQuick3D"); // Project load is already very busy and may even trigger puppet reset, so let's wait a moment @@ -130,7 +169,7 @@ void MaterialBrowserView::refreshModel(bool updateImages) bool MaterialBrowserView::isMaterial(const ModelNode &node) const { - if (!node.isValid() || node.isComponent()) + if (!node.isValid()) return false; return node.isSubclassOf("QtQuick3D.Material"); @@ -273,6 +312,12 @@ void MaterialBrowserView::customNotification(const AbstractView *view, int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first()); if (idx != -1) m_widget->materialBrowserModel()->selectMaterial(idx); + } else if (identifier == "refresh_material_browser") { + QTimer::singleShot(0, this, [this]() { + refreshModel(true); + }); + } else if (identifier == "delete_selected_material") { + m_widget->materialBrowserModel()->deleteSelectedMaterial(); } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 2ca37bd99f3..59a06e1a921 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -25,6 +25,7 @@ #include "materialbrowserwidget.h" #include "materialbrowsermodel.h" +#include "materialbrowserview.h" #include @@ -130,14 +131,20 @@ bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event) return QObject::eventFilter(obj, event); } -MaterialBrowserWidget::MaterialBrowserWidget() - : m_materialBrowserModel(new MaterialBrowserModel(this)) +MaterialBrowserWidget::MaterialBrowserWidget(MaterialBrowserView *view) + : m_materialBrowserView(view) + , m_materialBrowserModel(new MaterialBrowserModel(this)) , m_quickWidget(new QQuickWidget(this)) , m_previewImageProvider(new PreviewImageProvider()) { setWindowTitle(tr("Material Browser", "Title of material browser widget")); setMinimumWidth(120); + Core::Context context(Constants::C_QMLMATERIALBROWSER); + m_context = new Core::IContext(this); + m_context->setContext(context); + m_context->setWidget(this); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); @@ -182,6 +189,14 @@ QList MaterialBrowserWidget::createToolBarWidgets() return {}; } +void MaterialBrowserWidget::contextHelp(const Core::IContext::HelpCallback &callback) const +{ + if (m_materialBrowserView) + m_materialBrowserView->contextHelp(callback); + else + callback({}); +} + void MaterialBrowserWidget::handleSearchfilterChanged(const QString &filterText) { if (filterText != m_filterText) { diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index 30f9d05b509..9c08f21f9af 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -25,20 +25,19 @@ #pragma once -#include "itemlibraryinfo.h" -#include "import.h" #include "modelnode.h" -#include +#include #include +#include -#include -#include #include -#include -#include -#include +#include #include +#include +#include +#include +#include #include @@ -49,6 +48,7 @@ QT_END_NAMESPACE namespace QmlDesigner { +class MaterialBrowserView; class MaterialBrowserModel; class PreviewImageProvider; @@ -57,10 +57,11 @@ class MaterialBrowserWidget : public QFrame Q_OBJECT public: - MaterialBrowserWidget(); + MaterialBrowserWidget(MaterialBrowserView *view); ~MaterialBrowserWidget() = default; QList createToolBarWidgets(); + void contextHelp(const Core::IContext::HelpCallback &callback) const; static QString qmlSourcesPath(); void clearSearchFilter(); @@ -80,11 +81,13 @@ private: void reloadQmlSource(); void updateSearch(); + QPointer m_materialBrowserView; QPointer m_materialBrowserModel; QScopedPointer m_quickWidget; QShortcut *m_qmlSourceUpdateShortcut = nullptr; PreviewImageProvider *m_previewImageProvider = nullptr; + Core::IContext *m_context = nullptr; QString m_filterText; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index cf789bcd66e..95b29426279 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -47,12 +48,24 @@ namespace QmlDesigner { -MaterialEditorContextObject::MaterialEditorContextObject(QObject *parent) +MaterialEditorContextObject::MaterialEditorContextObject(QQmlContext *context, QObject *parent) : QObject(parent) + , m_qmlContext(context) { qmlRegisterUncreatableType("ToolBarAction", 1, 0, "ToolBarAction", "Enum type"); } +QQmlComponent *MaterialEditorContextObject::specificQmlComponent() +{ + if (m_specificQmlComponent) + return m_specificQmlComponent; + + m_specificQmlComponent = new QQmlComponent(m_qmlContext->engine(), this); + m_specificQmlComponent->setData(m_specificQmlData.toUtf8(), QUrl::fromLocalFile("specifics.qml")); + + return m_specificQmlComponent; +} + QString MaterialEditorContextObject::convertColorToString(const QVariant &color) { QString colorString; @@ -159,8 +172,10 @@ void MaterialEditorContextObject::changeTypeName(const QString &typeName) msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); - if (msgBox.exec() == QMessageBox::Cancel) + if (msgBox.exec() == QMessageBox::Cancel) { + updatePossibleTypeIndex(); return; + } for (const auto &p : std::as_const(incompatibleProperties)) m_selectedMaterial.removeProperty(p); @@ -234,6 +249,20 @@ void MaterialEditorContextObject::setHasQuick3DImport(bool b) emit hasQuick3DImportChanged(); } +bool MaterialEditorContextObject::hasMaterialRoot() const +{ + return m_hasMaterialRoot; +} + +void MaterialEditorContextObject::setHasMaterialRoot(bool b) +{ + if (b == m_hasMaterialRoot) + return; + + m_hasMaterialRoot = b; + emit hasMaterialRootChanged(); +} + bool MaterialEditorContextObject::hasModelSelection() const { return m_hasModelSelection; @@ -262,6 +291,20 @@ void MaterialEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl) emit specificsUrlChanged(); } +void MaterialEditorContextObject::setSpecificQmlData(const QString &newSpecificQmlData) +{ + if (newSpecificQmlData == m_specificQmlData) + return; + + m_specificQmlData = newSpecificQmlData; + + delete m_specificQmlComponent; + m_specificQmlComponent = nullptr; + + emit specificQmlComponentChanged(); + emit specificQmlDataChanged(); +} + void MaterialEditorContextObject::setStateName(const QString &newStateName) { if (newStateName == m_stateName) @@ -280,6 +323,23 @@ void MaterialEditorContextObject::setAllStateNames(const QStringList &allStates) emit allStateNamesChanged(); } +void MaterialEditorContextObject::setPossibleTypes(const QStringList &types) +{ + if (types == m_possibleTypes) + return; + + m_possibleTypes = types; + emit possibleTypesChanged(); + + updatePossibleTypeIndex(); +} + +void MaterialEditorContextObject::setCurrentType(const QString &type) +{ + m_currentType = type.split('.').last(); + updatePossibleTypeIndex(); +} + void MaterialEditorContextObject::setIsBaseState(bool newIsBaseState) { if (newIsBaseState == m_isBaseState) @@ -326,6 +386,20 @@ void MaterialEditorContextObject::setHasAliasExport(bool hasAliasExport) emit hasAliasExportChanged(); } +void MaterialEditorContextObject::updatePossibleTypeIndex() +{ + int newIndex = -1; + if (!m_currentType.isEmpty()) + newIndex = m_possibleTypes.indexOf(m_currentType); + + // Emit valid possible type index change even if the index doesn't change, as currentIndex on + // QML side will change to default internally if model is updated + if (m_possibleTypeIndex != -1 || m_possibleTypeIndex != newIndex) { + m_possibleTypeIndex = newIndex; + emit possibleTypeIndexChanged(); + } +} + void MaterialEditorContextObject::hideCursor() { if (QApplication::overrideCursor()) @@ -390,4 +464,10 @@ bool MaterialEditorContextObject::isBlocked(const QString &propName) const return false; } +void MaterialEditorContextObject::goIntoComponent() +{ + QTC_ASSERT(m_model, return); + DocumentManager::goIntoComponent(m_selectedMaterial); +} + } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h index 54d72171a56..07ce582d443 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h @@ -43,9 +43,13 @@ class MaterialEditorContextObject : public QObject Q_OBJECT Q_PROPERTY(QUrl specificsUrl READ specificsUrl WRITE setSpecificsUrl NOTIFY specificsUrlChanged) + Q_PROPERTY(QString specificQmlData READ specificQmlData WRITE setSpecificQmlData NOTIFY specificQmlDataChanged) + Q_PROPERTY(QQmlComponent *specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged) Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged) Q_PROPERTY(QStringList allStateNames READ allStateNames WRITE setAllStateNames NOTIFY allStateNamesChanged) + Q_PROPERTY(QStringList possibleTypes READ possibleTypes WRITE setPossibleTypes NOTIFY possibleTypesChanged) + Q_PROPERTY(int possibleTypeIndex READ possibleTypeIndex NOTIFY possibleTypeIndexChanged) Q_PROPERTY(bool isBaseState READ isBaseState WRITE setIsBaseState NOTIFY isBaseStateChanged) Q_PROPERTY(bool selectionChanged READ selectionChanged WRITE setSelectionChanged NOTIFY selectionChangedChanged) @@ -56,15 +60,20 @@ class MaterialEditorContextObject : public QObject Q_PROPERTY(bool hasActiveTimeline READ hasActiveTimeline NOTIFY hasActiveTimelineChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) + Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged) Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) public: - MaterialEditorContextObject(QObject *parent = nullptr); + MaterialEditorContextObject(QQmlContext *context, QObject *parent = nullptr); QUrl specificsUrl() const { return m_specificsUrl; } + QString specificQmlData() const {return m_specificQmlData; } + QQmlComponent *specificQmlComponent(); QString stateName() const { return m_stateName; } QStringList allStateNames() const { return m_allStateNames; } + QStringList possibleTypes() const { return m_possibleTypes; } + int possibleTypeIndex() const { return m_possibleTypeIndex; } bool isBaseState() const { return m_isBaseState; } bool selectionChanged() const { return m_selectionChanged; } @@ -86,6 +95,7 @@ public: Q_INVOKABLE QStringList allStatesForId(const QString &id); Q_INVOKABLE bool isBlocked(const QString &propName) const; + Q_INVOKABLE void goIntoComponent(); enum ToolBarAction { ApplyToSelected = 0, @@ -105,6 +115,9 @@ public: bool hasQuick3DImport() const; void setHasQuick3DImport(bool b); + bool hasMaterialRoot() const; + void setHasMaterialRoot(bool b); + bool hasModelSelection() const; void setHasModelSelection(bool b); @@ -113,8 +126,11 @@ public: void setSelectedMaterial(const ModelNode &matNode); void setSpecificsUrl(const QUrl &newSpecificsUrl); + void setSpecificQmlData(const QString &newSpecificQmlData); void setStateName(const QString &newStateName); void setAllStateNames(const QStringList &allStates); + void setPossibleTypes(const QStringList &types); + void setCurrentType(const QString &type); void setIsBaseState(bool newIsBaseState); void setSelectionChanged(bool newSelectionChanged); void setBackendValues(QQmlPropertyMap *newBackendValues); @@ -125,8 +141,12 @@ public: signals: void specificsUrlChanged(); + void specificQmlDataChanged(); + void specificQmlComponentChanged(); void stateNameChanged(); void allStateNamesChanged(); + void possibleTypesChanged(); + void possibleTypeIndexChanged(); void isBaseStateChanged(); void selectionChangedChanged(); void backendValuesChanged(); @@ -134,18 +154,26 @@ signals: void hasAliasExportChanged(); void hasActiveTimelineChanged(); void hasQuick3DImportChanged(); + void hasMaterialRootChanged(); void hasModelSelectionChanged(); private: + void updatePossibleTypeIndex(); + QUrl m_specificsUrl; + QString m_specificQmlData; + QQmlComponent *m_specificQmlComponent = nullptr; + QQmlContext *m_qmlContext = nullptr; QString m_stateName; QStringList m_allStateNames; + QStringList m_possibleTypes; + int m_possibleTypeIndex = -1; + QString m_currentType; int m_majorVersion = 1; QQmlPropertyMap *m_backendValues = nullptr; - QQmlComponent *m_qmlComponent = nullptr; Model *m_model = nullptr; QPoint m_lastPos; @@ -155,6 +183,7 @@ private: bool m_aliasExport = false; bool m_hasActiveTimeline = false; bool m_hasQuick3DImport = false; + bool m_hasMaterialRoot = false; bool m_hasModelSelection = false; ModelNode m_selectedMaterial; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index 09c55178c4b..542a3b46c66 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -102,7 +102,7 @@ public: MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) : m_view(new QQuickWidget) , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor)) - , m_contextObject(new MaterialEditorContextObject()) + , m_contextObject(new MaterialEditorContextObject(m_view->rootContext())) , m_materialEditorImageProvider(new MaterialEditorImageProvider()) { m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 52018307341..55129a17b66 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,7 @@ #include #include #include +#include namespace QmlDesigner { @@ -82,6 +84,10 @@ MaterialEditorView::MaterialEditorView(QWidget *parent) } }); + m_typeUpdateTimer.setSingleShot(true); + m_typeUpdateTimer.setInterval(500); + connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); + m_stackedWidget->setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_stackedWidget->setMinimumWidth(250); @@ -420,12 +426,15 @@ void MaterialEditorView::handleToolBarAction(int action) if (!model()) break; executeInTransaction("MaterialEditorView:handleToolBarAction", [&] { + ModelNode matLib = materialLibraryNode(); + if (!matLib.isValid()) + return; + NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial"); ModelNode newMatNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(), metaInfo.minorVersion()); renameMaterial(newMatNode, "New Material"); - - materialLibraryNode().defaultNodeListProperty().reparentHere(newMatNode); + matLib.defaultNodeListProperty().reparentHere(newMatNode); }); break; } @@ -443,18 +452,110 @@ void MaterialEditorView::handleToolBarAction(int action) } } +void MaterialEditorView::handlePreviewEnvChanged(const QString &envAndValue) +{ + // if (envAndValue.isEmpty() || m_initializingPreviewData) + // return; + + // QTC_ASSERT(m_hasQuick3DImport, return); + // QTC_ASSERT(model(), return); + // QTC_ASSERT(model()->nodeInstanceView(), return); + + // QStringList parts = envAndValue.split('='); + // QString env = parts[0]; + // QString value; + // if (parts.size() > 1) + // value = parts[1]; + + // PropertyName matPrevEnvAuxProp("matPrevEnv"); + // PropertyName matPrevEnvValueAuxProp("matPrevEnvValue"); + + // auto renderPreviews = [=](const QString &auxEnv, const QString &auxValue) { + // rootModelNode().setAuxiliaryData(matPrevEnvAuxProp, auxEnv); + // rootModelNode().setAuxiliaryData(matPrevEnvValueAuxProp, auxValue); + // QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); + // emitCustomNotification("refresh_material_browser", {}); + // }; + + // if (env == "Color") { + // m_colorDialog.clear(); + + // // Store color to separate property to persist selection over non-color env changes + // PropertyName colorAuxProp("matPrevColor"); + // QString oldColor = rootModelNode().auxiliaryData(colorAuxProp).toString(); + // QString oldEnv = rootModelNode().auxiliaryData(matPrevEnvAuxProp).toString(); + // QString oldValue = rootModelNode().auxiliaryData(matPrevEnvValueAuxProp).toString(); + + // m_colorDialog = new QColorDialog(Core::ICore::dialogParent()); + // m_colorDialog->setModal(true); + // m_colorDialog->setAttribute(Qt::WA_DeleteOnClose); + // m_colorDialog->setCurrentColor(QColor(oldColor)); + // m_colorDialog->show(); + + // QObject::connect(m_colorDialog, &QColorDialog::currentColorChanged, + // m_colorDialog, [=](const QColor &color) { + // renderPreviews(env, color.name()); + // }); + + // QObject::connect(m_colorDialog, &QColorDialog::colorSelected, + // m_colorDialog, [=](const QColor &color) { + // renderPreviews(env, color.name()); + // rootModelNode().setAuxiliaryData(colorAuxProp, color.name()); + // }); + + // QObject::connect(m_colorDialog, &QColorDialog::rejected, + // m_colorDialog, [=]() { + // renderPreviews(oldEnv, oldValue); + // initPreviewData(); + // }); + // return; + // } + // renderPreviews(env, value); +} + +void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr) +{ + // if (modelStr.isEmpty() || m_initializingPreviewData) + // return; + + // QTC_ASSERT(m_hasQuick3DImport, return); + // QTC_ASSERT(model(), return); + // QTC_ASSERT(model()->nodeInstanceView(), return); + + // rootModelNode().setAuxiliaryData("matPrevModel", modelStr); + + // QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); + // emitCustomNotification("refresh_material_browser", {}); +} + void MaterialEditorView::setupQmlBackend() { QUrl qmlPaneUrl; QUrl qmlSpecificsUrl; + QString specificQmlData; + QString currentTypeName; if (m_selectedMaterial.isValid() && m_hasQuick3DImport) { qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/MaterialEditorPane.qml"); + TypeName diffClassName; NodeMetaInfo metaInfo = m_selectedMaterial.metaInfo(); - QDir importDir(metaInfo.importDirectoryPath() + Constants::QML_DESIGNER_SUBFOLDER); - QString typeName = QString::fromUtf8(metaInfo.typeName().split('.').constLast()); - qmlSpecificsUrl = QUrl::fromLocalFile(importDir.absoluteFilePath(typeName + "Specifics.qml")); + if (metaInfo.isValid()) { + diffClassName = metaInfo.typeName(); + const QList hierarchy = metaInfo.classHierarchy(); + for (const NodeMetaInfo &metaInfo : hierarchy) { + if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsUrl)) + break; + qmlSpecificsUrl = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName() + + "Specifics", metaInfo); + diffClassName = metaInfo.typeName(); + } + } + if (metaInfo.isValid() && diffClassName != m_selectedMaterial.type()) { + specificQmlData = PropertyEditorQmlBackend::templateGeneration( + metaInfo, model()->metaInfo(diffClassName), m_selectedMaterial); + } + currentTypeName = QString::fromLatin1(m_selectedMaterial.type()); } else { qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/EmptyMaterialEditorPane.qml"); } @@ -473,17 +574,29 @@ void MaterialEditorView::setupQmlBackend() currentQmlBackend->setSource(qmlPaneUrl); - QObject::connect(currentQmlBackend->widget()->rootObject(), SIGNAL(toolBarAction(int)), + QObject *rootObj = currentQmlBackend->widget()->rootObject(); + QObject::connect(rootObj, SIGNAL(toolBarAction(int)), this, SLOT(handleToolBarAction(int))); + QObject::connect(rootObj, SIGNAL(previewEnvChanged(QString)), + this, SLOT(handlePreviewEnvChanged(QString))); + QObject::connect(rootObj, SIGNAL(previewModelChanged(QString)), + this, SLOT(handlePreviewModelChanged(QString))); } else { currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this); } + currentQmlBackend->widget()->installEventFilter(this); currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); - - m_stackedWidget->setCurrentWidget(currentQmlBackend->widget()); + currentQmlBackend->contextObject()->setHasMaterialRoot(m_hasMaterialRoot); + currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); + currentQmlBackend->contextObject()->setCurrentType(currentTypeName); m_qmlBackEnd = currentQmlBackend; + + delayedTypeUpdate(); + initPreviewData(); + + m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -529,6 +642,75 @@ bool MaterialEditorView::noValidSelection() const return !QmlObjectNode::isValidQmlObjectNode(m_selectedMaterial); } +void MaterialEditorView::initPreviewData() +{ + // if (model() && m_qmlBackEnd) { + // QString env = rootModelNode().auxiliaryData("matPrevEnv").toString(); + // QString envValue = rootModelNode().auxiliaryData("matPrevEnvValue").toString(); + // QString modelStr = rootModelNode().auxiliaryData("matPrevModel").toString(); + // if (!envValue.isEmpty() && env != "Color" && env != "Default") { + // env += '='; + // env += envValue; + // } + // if (env.isEmpty()) + // env = "Default"; + // if (modelStr.isEmpty()) + // modelStr = "#Sphere"; + // m_initializingPreviewData = true; + // QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), + // "initPreviewData", + // Q_ARG(QVariant, env), Q_ARG(QVariant, modelStr)); + // m_initializingPreviewData = false; + // } +} + +void MaterialEditorView::delayedTypeUpdate() +{ + m_typeUpdateTimer.start(); +} + +static Import entryToImport(const ItemLibraryEntry &entry) +{ + if (entry.majorVersion() == -1 && entry.minorVersion() == -1) + return Import::createFileImport(entry.requiredImport()); + return Import::createLibraryImport(entry.requiredImport(), + QString::number(entry.majorVersion()) + QLatin1Char('.') + + QString::number(entry.minorVersion())); +} + +void MaterialEditorView::updatePossibleTypes() +{ + QTC_ASSERT(model(), return); + + if (!m_qmlBackEnd) + return; + + // Ensure basic types are always first + static const QStringList basicTypes {"DefaultMaterial", "PrincipledMaterial", "CustomMaterial"}; + QStringList allTypes = basicTypes; + + const QList itemLibEntries = m_itemLibraryInfo->entries(); + for (const ItemLibraryEntry &entry : itemLibEntries) { + NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName()); + bool valid = metaInfo.isValid() + && (metaInfo.majorVersion() >= entry.majorVersion() + || metaInfo.majorVersion() < 0); + if (valid && metaInfo.isSubclassOf("QtQuick3D.Material")) { + bool addImport = entry.requiredImport().isEmpty(); + if (!addImport) { + Import import = entryToImport(entry); + addImport = model()->hasImport(import, true, true); + } + if (addImport) { + QString typeName = QString::fromLatin1(entry.typeName().split('.').last()); + if (!allTypes.contains(typeName)) + allTypes.append(typeName); + } + } + } + m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); +} + void MaterialEditorView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -536,11 +718,27 @@ void MaterialEditorView::modelAttached(Model *model) m_locked = true; m_hasQuick3DImport = model->hasImport("QtQuick3D"); + m_hasMaterialRoot = rootModelNode().isSubclassOf("QtQuick3D.Material"); - // Creating the material library node on model attach causes errors as long as the type information - // not complete yet, so we keep checking until type info is complete. - if (m_hasQuick3DImport) + if (m_hasMaterialRoot) { + m_selectedMaterial = rootModelNode(); + } else if (m_hasQuick3DImport) { + // Creating the material library node on model attach causes errors as long as the type + // information is not complete yet, so we keep checking until type info is complete. m_ensureMatLibTimer.start(500); + } + + if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) { + if (m_itemLibraryInfo) { + disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, + this, &MaterialEditorView::delayedTypeUpdate); + } + m_itemLibraryInfo = model->metaInfo().itemLibraryInfo(); + if (m_itemLibraryInfo) { + connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, + this, &MaterialEditorView::delayedTypeUpdate); + } + } if (!m_setupCompleted) { reloadQml(); @@ -705,10 +903,20 @@ void MaterialEditorView::instancePropertyChanged(const QListcontextObject()->setCurrentType(QString::fromLatin1(typeName)); delayedResetView(); + } +} + +void MaterialEditorView::rootNodeTypeChanged(const QString &type, int, int) +{ + if (rootModelNode() == m_selectedMaterial) { + m_qmlBackEnd->contextObject()->setCurrentType(type); + delayedResetView(); + } } void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) @@ -752,6 +960,10 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) QmlObjectNode sourceMat(material); executeInTransaction(__FUNCTION__, [&] { + ModelNode matLib = materialLibraryNode(); + if (!matLib.isValid()) + return; + // create the duplicate material NodeMetaInfo metaInfo = model()->metaInfo(matType); QmlObjectNode duplicateMat = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); @@ -773,7 +985,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) duplicateMat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); } - materialLibraryNode().defaultNodeListProperty().reparentHere(duplicateMat); + matLib.defaultNodeListProperty().reparentHere(duplicateMat); }); } @@ -783,8 +995,10 @@ void MaterialEditorView::customNotification([[maybe_unused]] const AbstractView const QList &data) { if (identifier == "selected_material_changed") { - m_selectedMaterial = nodeList.first(); - QTimer::singleShot(0, this, &MaterialEditorView::resetView); + if (!m_hasMaterialRoot) { + m_selectedMaterial = nodeList.first(); + QTimer::singleShot(0, this, &MaterialEditorView::resetView); + } } else if (identifier == "apply_to_selected_triggered") { applyMaterialToSelectedModels(nodeList.first(), data.first().toBool()); } else if (identifier == "rename_material") { @@ -843,6 +1057,15 @@ void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const Prop m_locked = false; } +bool MaterialEditorView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::FocusOut) { + if (m_qmlBackEnd && m_qmlBackEnd->widget() == obj) + QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), "closeContextMenu"); + } + return QObject::eventFilter(obj, event); +} + void MaterialEditorView::reloadQml() { m_qmlBackendHash.clear(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index ba16ea38b77..25f6bac0647 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -26,13 +26,17 @@ #pragma once #include +#include + #include +#include #include QT_BEGIN_NAMESPACE class QShortcut; class QStackedWidget; class QTimer; +class QColorDialog; QT_END_NAMESPACE namespace QmlDesigner { @@ -70,6 +74,7 @@ public: void instancePropertyChanged(const QList > &propertyList) override; void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; + void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override; void importsChanged(const QList &addedImports, const QList &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, @@ -89,10 +94,13 @@ public: public slots: void handleToolBarAction(int action); + void handlePreviewEnvChanged(const QString &envAndValue); + void handlePreviewModelChanged(const QString &modelStr); protected: void timerEvent(QTimerEvent *event) override; void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value); + bool eventFilter(QObject *obj, QEvent *event) override; private: static QString materialEditorResourcesPath(); @@ -115,8 +123,13 @@ private: bool noValidSelection() const; + void initPreviewData(); + void delayedTypeUpdate(); + void updatePossibleTypes(); + ModelNode m_selectedMaterial; QTimer m_ensureMatLibTimer; + QTimer m_typeUpdateTimer; QShortcut *m_updateShortcut = nullptr; int m_timerId = 0; QStackedWidget *m_stackedWidget = nullptr; @@ -126,6 +139,11 @@ private: bool m_locked = false; bool m_setupCompleted = false; bool m_hasQuick3DImport = false; + bool m_hasMaterialRoot = false; + bool m_initializingPreviewData = false; + + QPointer m_colorDialog; + QPointer m_itemLibraryInfo; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp index 2d96d01a9dc..0d8d39936bd 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -65,7 +65,7 @@ QModelIndex StatesEditorModel::index(int row, int column, const QModelIndex &par int internalNodeId = 0; if (row > 0 && row < rowCount() - 1) // first and last rows are base state, add state - internalNodeId = m_statesEditorView->rootModelNode().nodeListProperty("states").at(row - 1).internalId(); + internalNodeId = m_statesEditorView->acitveStatesGroupNode().nodeListProperty("states").at(row - 1).internalId(); return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex(); } @@ -75,10 +75,10 @@ int StatesEditorModel::rowCount(const QModelIndex &parent) const if (parent.isValid() || m_statesEditorView.isNull() || !m_statesEditorView->model()) return 0; - if (!m_statesEditorView->rootModelNode().hasNodeListProperty("states")) + if (!m_statesEditorView->acitveStatesGroupNode().hasNodeListProperty("states")) return 2; // base state + add new state - return m_statesEditorView->rootModelNode().nodeListProperty("states").count() + 2; // 2 = base state + add new state + return m_statesEditorView->acitveStatesGroupNode().nodeListProperty("states").count() + 2; // 2 = base state + add new state } void StatesEditorModel::reset() diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index d3e496058d9..1b14127e52e 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -85,6 +85,20 @@ void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorV checkForStatesAvailability(); } +ModelNode StatesEditorView::acitveStatesGroupNode() const +{ + return m_activeStatesGroupNode; +} + +void StatesEditorView::setAcitveStatesGroupNode(const ModelNode &modelNode) +{ + if (m_activeStatesGroupNode == modelNode) + return; + + m_activeStatesGroupNode = modelNode; + resetModel(); +} + void StatesEditorView::removeState(int nodeId) { try { @@ -193,12 +207,12 @@ void StatesEditorView::createNewState() void StatesEditorView::addState() { // can happen when root node is e.g. a ListModel - if (!QmlVisualNode::isValidQmlVisualNode(rootModelNode())) + if (!QmlVisualNode::isValidQmlVisualNode(acitveStatesGroupNode())) return; QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATE_ADDED); - QStringList modelStateNames = rootStateGroup().names(); + QStringList modelStateNames = activeStateGroup().names(); QString newStateName; int index = 1; @@ -209,9 +223,9 @@ void StatesEditorView::addState() } executeInTransaction("addState", [this, newStateName]() { - rootModelNode().validId(); + acitveStatesGroupNode().validId(); - ModelNode newState = rootStateGroup().addState(newStateName); + ModelNode newState = activeStateGroup().addState(newStateName); setCurrentState(newState); }); } @@ -244,7 +258,7 @@ void StatesEditorView::duplicateCurrentState() newName = newName.left(match.capturedStart()); int i = 1; - QStringList stateNames = rootStateGroup().names(); + QStringList stateNames = activeStateGroup().names(); while (stateNames.contains(newName + QString::number(i))) i++; const QString newStateName = newName + QString::number(i); @@ -258,7 +272,7 @@ void StatesEditorView::duplicateCurrentState() void StatesEditorView::checkForStatesAvailability() { if (m_statesEditorWidget) { - const bool isVisual = QmlVisualNode::isValidQmlVisualNode(rootModelNode()); + const bool isVisual = QmlVisualNode::isValidQmlVisualNode(acitveStatesGroupNode()); m_statesEditorWidget->showAddNewStatesButton(isVisual); } } @@ -277,16 +291,16 @@ QmlModelState StatesEditorView::baseState() const return QmlModelState::createBaseState(this); } -QmlModelStateGroup StatesEditorView::rootStateGroup() const +QmlModelStateGroup StatesEditorView::activeStateGroup() const { - return QmlModelStateGroup(rootModelNode()); + return QmlModelStateGroup(acitveStatesGroupNode()); } bool StatesEditorView::validStateName(const QString &name) const { if (name == tr("base state")) return false; - const QList modelStates = rootStateGroup().allStates(); + const QList modelStates = activeStateGroup().allStates(); for (const QmlModelState &state : modelStates) { if (state.name() == name) return false; @@ -392,8 +406,8 @@ void StatesEditorView::resetDefaultState() auto guard = qScopeGuard([&]() { m_block = false; }); try { - if (rootModelNode().hasProperty("state")) - rootModelNode().removeProperty("state"); + if (acitveStatesGroupNode().hasProperty("state")) + acitveStatesGroupNode().removeProperty("state"); } catch (const RewritingException &e) { e.showException(); @@ -402,7 +416,7 @@ void StatesEditorView::resetDefaultState() bool StatesEditorView::hasDefaultState() const { - return rootModelNode().hasProperty("state"); + return acitveStatesGroupNode().hasProperty("state"); } void StatesEditorView::setAnnotation(int internalNodeId) @@ -475,6 +489,8 @@ void StatesEditorView::modelAttached(Model *model) Q_ASSERT(model); AbstractView::modelAttached(model); + m_activeStatesGroupNode = rootModelNode(); + if (m_statesEditorWidget) m_statesEditorWidget->setNodeInstanceView(nodeInstanceView()); @@ -593,7 +609,7 @@ void StatesEditorView::instancesPreviewImageChanged(const QVector &no minimumIndex = qMin(minimumIndex, 0); maximumIndex = qMax(maximumIndex, 0); } else { - int index = rootStateGroup().allStates().indexOf(QmlModelState(node)) + 1; + int index = activeStateGroup().allStates().indexOf(QmlModelState(node)) + 1; if (index > 0) { minimumIndex = qMin(minimumIndex, index); maximumIndex = qMax(maximumIndex, index); diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h index 71a82711eaa..1ee8144a2bb 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h @@ -55,7 +55,7 @@ public: QString currentStateName() const; void setCurrentState(const QmlModelState &state); QmlModelState baseState() const; - QmlModelStateGroup rootStateGroup() const; + QmlModelStateGroup activeStateGroup() const; // AbstractView void modelAttached(Model *model) override; @@ -87,6 +87,10 @@ public: void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; + ModelNode acitveStatesGroupNode() const; + void setAcitveStatesGroupNode(const ModelNode &modelNode); + + public slots: void synchonizeCurrentStateFromWidget(); void createNewState(); @@ -105,6 +109,7 @@ private: int m_lastIndex; bool m_block = false; QPointer m_editor; + ModelNode m_activeStatesGroupNode; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp index ad4d02df891..60553dc6202 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp @@ -24,47 +24,147 @@ ****************************************************************************/ #include "setframevaluedialog.h" -#include "ui_setframevaluedialog.h" +#include "timelinecontrols.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include namespace QmlDesigner { SetFrameValueDialog::SetFrameValueDialog(qreal frame, const QVariant &value, const QString &propertyName, QWidget *parent) - : QDialog(parent) - , ui(new Ui::SetFrameValueDialog) + : QDialog(parent, Qt::Tool) + , m_valueGetter() + , m_valueType(value.metaType()) + , m_frameControl(new QSpinBox) { - ui->setupUi(this); setWindowTitle(tr("Edit Keyframe")); - setFixedSize(size()); - ui->lineEditFrame->setValidator(new QIntValidator(0, 99999, this)); - auto dv = new QDoubleValidator(this); - dv->setDecimals(2); - ui->lineEditValue->setValidator(dv); + auto frameLabelString = QString(tr("Frame")); + auto labelWidth = fontMetrics().boundingRect(frameLabelString).width(); + if (auto tmp = fontMetrics().boundingRect(propertyName).width(); tmp > labelWidth) + labelWidth = tmp; - QLocale l; - ui->lineEditFrame->setText(l.toString(qRound(frame))); - ui->lineEditValue->setText(l.toString(value.toDouble(), 'f', 2)); - ui->labelValue->setText(propertyName); + auto *frameLabel = new QLabel(frameLabelString); + frameLabel->setAlignment(Qt::AlignRight); + frameLabel->setFixedWidth(labelWidth); + + auto *valueLabel = new QLabel(propertyName); + valueLabel->setAlignment(Qt::AlignRight); + valueLabel->setFixedWidth(labelWidth); + + m_frameControl->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + m_frameControl->setValue(static_cast(frame)); + m_frameControl->setAlignment(Qt::AlignRight); + + auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + + auto* frameRow = new QHBoxLayout; + frameRow->addWidget(frameLabel); + frameRow->addWidget(m_frameControl); + + auto* valueRow = new QHBoxLayout; + valueRow->addWidget(valueLabel); + valueRow->addWidget(createValueControl(value)); + + auto* hbox = new QVBoxLayout; + hbox->addLayout(frameRow); + hbox->addLayout(valueRow); + hbox->addStretch(); + hbox->addWidget(buttons); + + setLayout(hbox); } SetFrameValueDialog::~SetFrameValueDialog() -{ - delete ui; -} +{ } qreal SetFrameValueDialog::frame() const { - QLocale l; - return l.toDouble(ui->lineEditFrame->text()); + return static_cast(m_frameControl->value()); } QVariant SetFrameValueDialog::value() const { - QLocale l; - return QVariant(l.toDouble(ui->lineEditValue->text())); + if (m_valueGetter) + return m_valueGetter(); + return QVariant(m_valueType); +} + +QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) +{ + m_valueType = value.metaType(); + + switch (value.metaType().id()) + { + + case QMetaType::QColor: { + auto* widget = new ColorControl(value.value()); + m_valueGetter = [widget]() { return widget->value(); }; + return widget; + } + + case QMetaType::Bool: { + auto* widget = new QCheckBox; + widget->setChecked(value.toBool()); + m_valueGetter = [widget]() { return widget->isChecked(); }; + return widget; + } + + case QMetaType::Int: { + auto* widget = new QSpinBox; + widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setAlignment(Qt::AlignRight); + widget->setValue(value.toInt()); + m_valueGetter = [widget]() { return widget->value(); }; + return widget; + } + + case QMetaType::UInt: { + auto* widget = new QSpinBox; + widget->setRange(0, std::numeric_limits::max()); + widget->setAlignment(Qt::AlignRight); + widget->setValue(value.toUInt()); + m_valueGetter = [widget]() { return static_cast(widget->value()); }; + return widget; + } + + case QMetaType::Float: { + auto* widget = new QDoubleSpinBox; + widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setAlignment(Qt::AlignRight); + widget->setValue(value.toFloat()); + m_valueGetter = [widget]() { return static_cast(widget->value()); }; + return widget; + } + + case QMetaType::Double: + [[fallthrough]]; + + default: { + auto* widget = new QDoubleSpinBox; + widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setAlignment(Qt::AlignRight); + widget->setValue(value.toDouble()); + m_valueGetter = [widget]() { return widget->value(); }; + return widget; + } + + } + + m_valueGetter = nullptr; + return nullptr; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.h b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.h index 799e3fadc52..e2ca6e39010 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.h +++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.h @@ -27,14 +27,10 @@ #include -QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QSpinBox) namespace QmlDesigner { -namespace Ui { -class SetFrameValueDialog; -} - class SetFrameValueDialog : public QDialog { Q_OBJECT @@ -48,7 +44,12 @@ public: QVariant value() const; private: - Ui::SetFrameValueDialog *ui; + QWidget* createValueControl(const QVariant& value); + + std::function m_valueGetter; + + QMetaType m_valueType; + QSpinBox *m_frameControl; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.ui b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.ui deleted file mode 100644 index 8a3ba3c99cb..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.ui +++ /dev/null @@ -1,84 +0,0 @@ - - - QmlDesigner::SetFrameValueDialog - - - - 0 - 0 - 212 - 148 - - - - Dialog - - - - - - Frame - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - - - Value - - - - - - - - - buttonBox - accepted() - QmlDesigner::SetFrameValueDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - QmlDesigner::SetFrameValueDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp index 185b12ea422..f417d5dad06 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp @@ -191,7 +191,7 @@ void ColorControl::mouseReleaseEvent(QMouseEvent *event) event->accept(); - if (color != m_color) { + if (color.isValid() && color != m_color) { m_color = color; update(); emit valueChanged(); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 87cd27062d6..90ab101fada 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1044,6 +1044,18 @@ bool shouldSendAuxiliary(const AuxiliaryDataKey &key) } } // namespace +bool parentIsBehavior(ModelNode node) +{ + while (node.isValid() && !node.isRootNode()) { + if (!node.behaviorPropertyName().isEmpty()) + return true; + + node = node.parentProperty().parentModelNode(); + } + + return false; +} + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList nodeList = allModelNodes(); @@ -1103,7 +1115,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() nodeMetaType, nodeFlags); - if (instance.modelNode().behaviorPropertyName().isEmpty()) + if (!parentIsBehavior(instance.modelNode())) instanceContainerList.append(container); } diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index ae2dd2f8511..f55f2580860 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -836,7 +836,7 @@ void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, in void AbstractView::ensureMaterialLibraryNode() { ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); - if (matLib.isValid()) + if (matLib.isValid() || rootModelNode().isSubclassOf("QtQuick3D.Material")) return; // Create material library node @@ -865,13 +865,11 @@ void AbstractView::ensureMaterialLibraryNode() } // Returns ModelNode for project's material library. +// Since this calls ensureMaterialLibraryNode(), it should only be called within a transaction. ModelNode AbstractView::materialLibraryNode() { ensureMaterialLibraryNode(); - ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); - QTC_ASSERT(matLib.isValid(), return {}); - return matLib; } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index dc12c2a0b33..d01ba0d82b0 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -408,8 +408,12 @@ void ModelPrivate::notifyNodeInstanceViewLast(Callable call) } for (const QPointer &view : enabledViews()) { - if (!view->isBlockingNotifications()) - call(view.data()); + try { + if (!view->isBlockingNotifications()) + call(view.data()); + } catch (const Exception &e) { + e.showException(tr("Exception thrown by view %1.").arg(view->widgetInfo().tabName)); + } } if (nodeInstanceView() && !nodeInstanceView()->isBlockingNotifications()) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index ff80243e83e..f1de0045b36 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -88,6 +88,7 @@ RewriterView::RewriterView(DifferenceHandling differenceHandling, QObject *paren m_textToModelMerger(new Internal::TextToModelMerger(this)) { m_amendTimer.setSingleShot(true); + m_amendTimer.setInterval(800); connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText); @@ -524,6 +525,10 @@ void RewriterView::applyChanges() void RewriterView::amendQmlText() { + + if (!model()->rewriterView()) + return; + emitCustomNotification(StartRewriterAmend); const QString newQmlText = m_textModifier->text(); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index cc922fd8273..012697f52ac 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -525,13 +525,15 @@ public: && metaInfo.majorVersion() == majorVersion && metaInfo.minorVersion() == minorVersion; + if (!ok) { qDebug() << Q_FUNC_INFO; qDebug() << astTypeNode->name.toString() << typeName; qDebug() << metaInfo.isValid() << metaInfo.typeName(); qDebug() << metaInfo.directSuperClass().typeName(); - if (!typeName.startsWith("...")) + if (!typeName.startsWith("...") && m_model == m_model->metaInfoProxyModel() + && metaInfo.isValid()) throw RewritingException(__LINE__, __FUNCTION__, __FILE__, "test", "test"); } @@ -1715,7 +1717,7 @@ void TextToModelMerger::syncSignalDeclarationProperty(AbstractProperty &modelPro const QString &signature, DifferenceHandler &differenceHandler) { - if (modelProperty.isSignalHandlerProperty()) { + if (modelProperty.isSignalDeclarationProperty()) { SignalDeclarationProperty signalHandlerProperty = modelProperty.toSignalDeclarationProperty(); if (signalHandlerProperty.signature() != signature) differenceHandler.signalDeclarationSignatureDiffer(signalHandlerProperty, signature); diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index a0cb0cf4072..442b8b90536 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -28,6 +28,7 @@ #include "designmodewidget.h" #include "formeditorwidget.h" #include "edit3dwidget.h" +#include "materialbrowserwidget.h" #include "navigatorwidget.h" #include "texteditorwidget.h" @@ -70,6 +71,18 @@ void Editor3DContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } +MaterialBrowserContext::MaterialBrowserContext(QWidget *widget) + : IContext(widget) +{ + setWidget(widget); + setContext(Core::Context(Constants::C_QMLMATERIALBROWSER, Constants::C_QT_QUICK_TOOLS_MENU)); +} + +void MaterialBrowserContext::contextHelp(const HelpCallback &callback) const +{ + qobject_cast(m_widget)->contextHelp(callback); +} + NavigatorContext::NavigatorContext(QWidget *widget) : IContext(widget) { diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index b30430b5eeb..fdf5beeecef 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -60,6 +60,15 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; +class MaterialBrowserContext : public Core::IContext +{ + Q_OBJECT + +public: + MaterialBrowserContext(QWidget *widget); + void contextHelp(const Core::IContext::HelpCallback &callback) const override; +}; + class NavigatorContext : public Core::IContext { Q_OBJECT diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 0a7c1027fb2..7fd6e713ae9 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -390,19 +390,26 @@ void DesignModeWidget::setup() m_dockManager->initialize(); + // Hide all floating widgets if the initial mode isn't design mode + if (Core::ModeManager::instance()->currentModeId() != Core::Constants::MODE_DESIGN) { + for (auto &floatingWidget : m_dockManager->floatingWidgets()) + floatingWidget->hide(); + } + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, - [this](Utils::Id mode, Utils::Id oldMode) { + [this](Utils::Id mode, Utils::Id previousMode) { if (mode == Core::Constants::MODE_DESIGN) { m_dockManager->reloadActiveWorkspace(); m_dockManager->setModeChangeState(false); } - if (oldMode == Core::Constants::MODE_DESIGN && mode != Core::Constants::MODE_DESIGN) { + if (previousMode == Core::Constants::MODE_DESIGN + && mode != Core::Constants::MODE_DESIGN) { m_dockManager->save(); m_dockManager->setModeChangeState(true); - for (auto floatingWidget : m_dockManager->floatingWidgets()) + for (auto &floatingWidget : m_dockManager->floatingWidgets()) floatingWidget->hide(); } }); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 3cc0c3b8ae6..ee5fa690dca 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -32,11 +32,12 @@ const char C_BACKSPACE[] = "QmlDesigner.Backspace"; const char C_DELETE[] = "QmlDesigner.Delete"; // Context -const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; -const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; -const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; -const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; -const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; +const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; +const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; +const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; +const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; +const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; +const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; // Special context for preview menu, shared b/w designer and text editor const char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index c13f197144c..315ecf2401d 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -399,15 +399,18 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) Core::Context qmlDesignerFormEditorContext(Constants::C_QMLFORMEDITOR); Core::Context qmlDesignerEditor3dContext(Constants::C_QMLEDITOR3D); Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); + Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); context->context().add(qmlDesignerMainContext); context->context().add(qmlDesignerFormEditorContext); context->context().add(qmlDesignerEditor3dContext); context->context().add(qmlDesignerNavigatorContext); + context->context().add(qmlDesignerMaterialBrowserContext); context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, - qmlDesignerEditor3dContext, qmlDesignerNavigatorContext); + qmlDesignerEditor3dContext, qmlDesignerNavigatorContext, + qmlDesignerMaterialBrowserContext); const QStringList mimeTypes = { QmlJSTools::Constants::QML_MIMETYPE, QmlJSTools::Constants::QMLUI_MIMETYPE }; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 6c3205ced8c..be392867bf4 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -1011,7 +1011,6 @@ Project { "timelineeditor/preseteditor.h", "timelineeditor/setframevaluedialog.cpp", "timelineeditor/setframevaluedialog.h", - "timelineeditor/setframevaluedialog.ui", "timelineeditor/splineeditor.cpp", "timelineeditor/splineeditor.h", "timelineeditor/timeline.qrc", diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index e82e7a04c01..4cda11748c6 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -77,8 +77,11 @@ ShortCutManager::ShortCutManager() void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContext, const Core::Context &qmlDesignerFormEditorContext, const Core::Context &qmlDesignerEditor3DContext, - const Core::Context &qmlDesignerNavigatorContext) + const Core::Context &qmlDesignerNavigatorContext, + const Core::Context &qmlDesignerMaterialBrowserContext) { + Q_UNUSED(qmlDesignerMaterialBrowserContext) + Core::ActionContainer *editMenu = Core::ActionManager::actionContainer(Core::Constants::M_EDIT); Core::ActionContainer *fileMenu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); @@ -195,9 +198,12 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex m_pasteAction.setEnabled(true); }); - connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&designerActionManager, this](const Core::Context &context){ - if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) && !context.contains(Constants::C_QMLNAVIGATOR)) { - m_deleteAction.setEnabled(false); + connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) { + isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER); + + if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) + && !context.contains(Constants::C_QMLNAVIGATOR)) { + m_deleteAction.setEnabled(isMatBrowserActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -249,8 +255,12 @@ void ShortCutManager::redo() void ShortCutManager::deleteSelected() { - if (currentDesignDocument()) + if (isMatBrowserActive) { + DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); + designerActionManager.view()->emitCustomNotification("delete_selected_material"); + } else if (currentDesignDocument()) { currentDesignDocument()->deleteSelected(); + } } void ShortCutManager::cutSelected() diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 4bc4ae5f848..149bf566400 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -47,7 +47,8 @@ public: void registerActions(const Core::Context &qmlDesignerMainContext, const Core::Context &qmlDesignerFormEditorContext, const Core::Context &qmlDesignerEditor3DContext, - const Core::Context &qmlDesignerNavigatorContext); + const Core::Context &qmlDesignerNavigatorContext, + const Core::Context &qmlDesignerMaterialBrowserContext); void connectUndoActions(DesignDocument *designDocument); void disconnectUndoActions(DesignDocument *designDocument); @@ -84,6 +85,8 @@ private: QAction m_pasteAction; QAction m_selectAllAction; QAction m_escapeAction; + + bool isMatBrowserActive = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.cpp b/src/plugins/qmldesigner/studioplugin/studioplugin.cpp index 6d3f7b8e8b4..313d97c1238 100644 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.cpp +++ b/src/plugins/qmldesigner/studioplugin/studioplugin.cpp @@ -1,18 +1,25 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd -** All rights reserved. -** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** -** This file is part of the Qt Enterprise QML Live Preview Add-on. +** This file is part of Qt Creator. ** -** Licensees holding valid Qt Enterprise licenses may use this file in -** accordance with the Qt Enterprise License Agreement provided with the +** 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. +** 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. ** -** If you have questions regarding the use of this file, please use -** contact form at http://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. ** ****************************************************************************/ diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.h b/src/plugins/qmldesigner/studioplugin/studioplugin.h index 28f04a7d110..5543b03946b 100644 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.h +++ b/src/plugins/qmldesigner/studioplugin/studioplugin.h @@ -1,18 +1,25 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd -** All rights reserved. -** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** -** This file is part of the Qt Enterprise QML Live Preview Add-on. +** This file is part of Qt Creator. ** -** Licensees holding valid Qt Enterprise licenses may use this file in -** accordance with the Qt Enterprise License Agreement provided with the +** 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. +** 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. ** -** If you have questions regarding the use of this file, please use -** contact form at http://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. ** ****************************************************************************/ diff --git a/src/plugins/scxmleditor/common/colorsettings.cpp b/src/plugins/scxmleditor/common/colorsettings.cpp index 6433b3c99f5..5bbf60e6f69 100644 --- a/src/plugins/scxmleditor/common/colorsettings.cpp +++ b/src/plugins/scxmleditor/common/colorsettings.cpp @@ -39,12 +39,6 @@ ColorSettings::ColorSettings(QWidget *parent) m_ui.setupUi(this); m_ui.m_colorThemeView->setEnabled(false); - connect(m_ui.m_comboColorThemes, &QComboBox::currentIndexChanged, - this, &ColorSettings::selectTheme); - connect(m_ui.m_colorThemeView, &ColorThemeView::colorChanged, - this, &ColorSettings::updateCurrentColors); - connect(m_ui.m_addColorTheme, &QToolButton::clicked, this, &ColorSettings::createTheme); - connect(m_ui.m_removeColorTheme, &QToolButton::clicked, this, &ColorSettings::removeTheme); const QSettings *s = Core::ICore::settings(); @@ -54,6 +48,14 @@ ColorSettings::ColorSettings(QWidget *parent) for (auto it = m_colorThemes.cbegin(); it != m_colorThemes.cend(); ++it) m_ui.m_comboColorThemes->addItem(it.key()); m_ui.m_comboColorThemes->setCurrentText(s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME).toString()); + + connect(m_ui.m_comboColorThemes, + &QComboBox::currentIndexChanged, + this, + &ColorSettings::selectTheme); + connect(m_ui.m_colorThemeView, &ColorThemeView::colorChanged, this, &ColorSettings::updateCurrentColors); + connect(m_ui.m_addColorTheme, &QToolButton::clicked, this, &ColorSettings::createTheme); + connect(m_ui.m_removeColorTheme, &QToolButton::clicked, this, &ColorSettings::removeTheme); } void ColorSettings::save() diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index cdb0e878c39..9131009f563 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -456,21 +456,28 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) &FileDownloader::progressChanged, this, &DataModelDownloader::progressChanged); + + connect(&m_fileDownloader, + &FileDownloader::downloadFailed, + this, + &DataModelDownloader::downloadFailed); } -void DataModelDownloader::start() +bool DataModelDownloader::start() { if (!enableDownload()) { m_available = false; emit availableChanged(); - return; + return false; } m_fileDownloader.setUrl(QUrl::fromUserInput( "https://download.qt.io/learning/examples/qtdesignstudio/dataImports.zip")); - connect(&m_fileDownloader, &FileDownloader::availableChanged, this, [this]() { + bool started = false; + + connect(&m_fileDownloader, &FileDownloader::availableChanged, this, [this, &started]() { m_available = m_fileDownloader.available(); @@ -484,6 +491,8 @@ void DataModelDownloader::start() if (!m_forceDownload && (m_fileDownloader.lastModified() <= m_birthTime)) return; + started = true; + m_fileDownloader.start(); connect(&m_fileDownloader, &FileDownloader::finishedChanged, this, [this]() { if (m_fileDownloader.finished()) { @@ -501,6 +510,7 @@ void DataModelDownloader::start() } }); }); + return started; } bool DataModelDownloader::exists() const diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h index fae0fea3368..42b8b9a41b9 100644 --- a/src/plugins/studiowelcome/examplecheckout.h +++ b/src/plugins/studiowelcome/examplecheckout.h @@ -163,7 +163,7 @@ class DataModelDownloader : public QObject public: explicit DataModelDownloader(QObject *parent = nullptr); - void start(); + bool start(); bool exists() const; bool available() const; Utils::FilePath targetFolder() const; @@ -174,6 +174,7 @@ signals: void finished(); void availableChanged(); void progressChanged(); + void downloadFailed(); private: FileDownloader m_fileDownloader; diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 7e2dec67cb7..3223674dba3 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -453,7 +453,11 @@ public: ~WelcomeMode() override; private: - QQuickWidget *m_modeWidget = nullptr; + void setupQuickWidget(const QString &welcomePagePath); + void createQuickWidget(); + + QQuickWidget *m_quickWidget = nullptr; + QWidget *m_modeWidget = nullptr; DataModelDownloader *m_dataModelDownloader = nullptr; }; @@ -637,31 +641,31 @@ WelcomeMode::WelcomeMode() QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf"); ExampleCheckout::registerTypes(); - m_modeWidget = new QQuickWidget; - m_modeWidget->setMinimumSize(640, 480); - m_modeWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - QmlDesigner::Theme::setupTheme(m_modeWidget->engine()); - m_modeWidget->engine()->addImportPath("qrc:/studiofonts"); - - QmlDesigner::QmlDesignerPlugin::registerPreviewImageProvider(m_modeWidget->engine()); - - m_modeWidget->engine()->setOutputWarningsToStandardError(false); + createQuickWidget(); if (forceDownLoad() || !readme.exists()) // Only downloads contain the readme m_dataModelDownloader->setForceDownload(true); connect(m_dataModelDownloader, &DataModelDownloader::progressChanged, this, [this](){ - m_modeWidget->rootObject()->setProperty("loadingProgress", m_dataModelDownloader->progress()); + m_quickWidget->rootObject()->setProperty("loadingProgress", m_dataModelDownloader->progress()); }); - connect(m_dataModelDownloader, &DataModelDownloader::finished, this, [this](){ - auto source = m_modeWidget->source(); - m_modeWidget->engine()->clearComponentCache(); - m_modeWidget->setSource(source); - m_modeWidget->rootObject()->setProperty("loadingProgress", 100); + m_quickWidget->setEnabled(false); + + connect(m_dataModelDownloader, &DataModelDownloader::finished, this, [this, welcomePagePath]() { + delete m_quickWidget; + createQuickWidget(); + setupQuickWidget(welcomePagePath); + m_modeWidget->layout()->addWidget(m_quickWidget); }); - m_dataModelDownloader->start(); + connect(m_dataModelDownloader, &DataModelDownloader::downloadFailed, this, [this]() { + m_quickWidget->setEnabled(true); + }); + + + if (m_dataModelDownloader->start()) + m_quickWidget->setEnabled(false); /* connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this](Utils::Id mode){ @@ -669,36 +673,14 @@ WelcomeMode::WelcomeMode() m_modeWidget->rootObject()->setProperty("active", active); }); */ + setupQuickWidget(welcomePagePath); - if (!useNewWelcomePage()) { - -#ifdef QT_DEBUG - m_modeWidget->engine()->addImportPath(QLatin1String(STUDIO_QML_PATH) - + "welcomepage/imports"); - m_modeWidget->setSource(QUrl::fromLocalFile(QLatin1String(STUDIO_QML_PATH) - + "welcomepage/main.qml")); -#else - m_modeWidget->engine()->addImportPath("qrc:/qml/welcomepage/imports"); - m_modeWidget->setSource(QUrl("qrc:/qml/welcomepage/main.qml")); -#endif - } else { - - m_modeWidget->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString()); - - m_modeWidget->engine()->addImportPath(welcomePagePath + "/imports"); - m_modeWidget->engine()->addImportPath(m_dataModelDownloader->targetFolder().toString()); - m_modeWidget->setSource(QUrl::fromLocalFile(welcomePagePath + "/main.qml")); - - QShortcut *updateShortcut = nullptr; - if (Utils::HostOsInfo::isMacHost()) - updateShortcut = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_F5), m_modeWidget); - else - updateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F5), m_modeWidget); - connect(updateShortcut, &QShortcut::activated, this, [this, welcomePagePath](){ - m_modeWidget->setSource(QUrl::fromLocalFile(welcomePagePath + "/main.qml")); - }); - } + QVBoxLayout *boxLayout = new QVBoxLayout(); + boxLayout->setContentsMargins(0, 0, 0, 0); + m_modeWidget = new QWidget; + m_modeWidget->setLayout(boxLayout); + boxLayout->addWidget(m_quickWidget); setWidget(m_modeWidget); QStringList designStudioQchPathes @@ -749,6 +731,51 @@ WelcomeMode::~WelcomeMode() delete m_modeWidget; } +void WelcomeMode::setupQuickWidget(const QString &welcomePagePath) +{ + if (!useNewWelcomePage()) { + +#ifdef QT_DEBUG + m_quickWidget->engine()->addImportPath(QLatin1String(STUDIO_QML_PATH) + + "welcomepage/imports"); + m_quickWidget->setSource( + QUrl::fromLocalFile(QLatin1String(STUDIO_QML_PATH) + "welcomepage/main.qml")); +#else + m_quickWidget->engine()->addImportPath("qrc:/qml/welcomepage/imports"); + m_quickWidget->setSource(QUrl("qrc:/qml/welcomepage/main.qml")); +#endif + } else { + + m_quickWidget->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString()); + + m_quickWidget->engine()->addImportPath(welcomePagePath + "/imports"); + m_quickWidget->engine()->addImportPath(m_dataModelDownloader->targetFolder().toString()); + m_quickWidget->setSource(QUrl::fromLocalFile(welcomePagePath + "/main.qml")); + + QShortcut *updateShortcut = nullptr; + if (Utils::HostOsInfo::isMacHost()) + updateShortcut = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_F5), m_quickWidget); + else + updateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F5), m_quickWidget); + connect(updateShortcut, &QShortcut::activated, this, [this, welcomePagePath](){ + m_quickWidget->setSource(QUrl::fromLocalFile(welcomePagePath + "/main.qml")); + }); + } +} + +void WelcomeMode::createQuickWidget() +{ + m_quickWidget = new QQuickWidget; + m_quickWidget->setMinimumSize(640, 480); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + QmlDesigner::Theme::setupTheme(m_quickWidget->engine()); + m_quickWidget->engine()->addImportPath("qrc:/studiofonts"); + + QmlDesigner::QmlDesignerPlugin::registerPreviewImageProvider(m_quickWidget->engine()); + + m_quickWidget->engine()->setOutputWarningsToStandardError(false); +} + StudioSettingsPage::StudioSettingsPage() : m_buildCheckBox(new QCheckBox(tr("Build"))) , m_debugCheckBox(new QCheckBox(tr("Debug"))) diff --git a/src/shared/qbs b/src/shared/qbs index faac35b5b31..3e0553e4651 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit faac35b5b3191348a72256437ccbb71a68dfb3a9 +Subproject commit 3e0553e4651197e61c5086d96048b75462bde897