diff --git a/doc/qtcreator/src/analyze/creator-analyze.qdoc b/doc/qtcreator/src/analyze/creator-analyze.qdoc index 5f572273b6a..4a4e05f413c 100644 --- a/doc/qtcreator/src/analyze/creator-analyze.qdoc +++ b/doc/qtcreator/src/analyze/creator-analyze.qdoc @@ -38,7 +38,7 @@ identifying potential bottlenecks, especially in the evaluation of bindings. - \li \l{Checking Code Coverage}{Squish Coco} + \li \l{Checking Code Coverage}{Coco} Analyze the way an application runs as part of a test suite, for example, and use the results to make the tests more efficient and diff --git a/doc/qtcreator/src/analyze/creator-coco.qdoc b/doc/qtcreator/src/analyze/creator-coco.qdoc index 4a55382875d..64a239aec00 100644 --- a/doc/qtcreator/src/analyze/creator-coco.qdoc +++ b/doc/qtcreator/src/analyze/creator-coco.qdoc @@ -8,11 +8,10 @@ \title Checking Code Coverage - \l{https://doc.froglogic.com/squish-coco/latest/}{Squish Coco} is a complete - code coverage tool chain for Tcl, QML, C# and C/C++ programs that runs on - \macOS, Linux, and Windows. + \l{https://doc.qt.io/coco/}{Coco} is a complete code coverage tool chain for + Tcl, QML, C# and C/C++ programs that runs on \macOS, Linux, and Windows. - Squish Coco analyzes the way an application runs, as part of a test suite, + Coco analyzes the way an application runs, as part of a test suite, for example. The results enable you to make the tests more efficient and complete. @@ -20,7 +19,7 @@ \list \li Find untested code sections. - \li Find redundant tests which can then be eliminated. Squish Coco can + \li Find redundant tests which can then be eliminated. Coco can identify portions of the source code that are covered by a test. It can detect whether a new test covers lines in the source code that the existing tests do not cover. @@ -38,10 +37,10 @@ The experimental Coco plugin integrates Coco CoverageBrowser into \QC. It enables you to analyze the test coverage by loading an instrumentation database (a .csmes file) that was generated by Coco CoverageScanner. - It is currently supported only on Windows, with Squish Coco version 6.0, + It is currently supported only on Windows, with Coco version 6.0, or later. - To use the plugin, you must download and install Squish Coco. + To use the plugin, you must download and install Coco. \section1 Enabling the Coco Plugin diff --git a/doc/qtdesignstudio/images/material-copy-properties.png b/doc/qtdesignstudio/images/material-copy-properties.png new file mode 100644 index 00000000000..aae30209295 Binary files /dev/null and b/doc/qtdesignstudio/images/material-copy-properties.png differ diff --git a/doc/qtdesignstudio/images/material-editor-browser.webp b/doc/qtdesignstudio/images/material-editor-browser.webp index d3963b522e4..8933b9d8e63 100644 Binary files a/doc/qtdesignstudio/images/material-editor-browser.webp and b/doc/qtdesignstudio/images/material-editor-browser.webp differ diff --git a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc index a7855aaceb3..4120bb633e4 100644 --- a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc @@ -39,8 +39,12 @@ \section1 Assigning a Material to an Object - To assign a material to a 3D object in your project, first select the object - in \uicontrol Navigator or the \uicontrol{3D} view. Then, do one of the + To assign a material to a 3D object in your project, drag the material from + \uicontrol {Material Browser} to the object in the \uicontrol Navigator or + \uicontrol 3D view. + + Additionally, you can also first select the object + in the \uicontrol Navigator or \uicontrol{3D} view, and then do one of the following: \list @@ -64,6 +68,26 @@ \image materials-remove-material.png \endlist + \section1 Copying and Pasting Material Properties + + You can copy properties from one material to another. You can choose if you + want to copy all properties or certain property groups. + + To copy material properties from one material to another: + + \list + \li In \uicontrol {Material Browser}, right-click the material that you + want to copy properties from. + \li Select \uicontrol {Copy properties} and then + \uicontrol All or a property group. + \image material-copy-properties.png + \li Right-click the material that you want to copy the properties to. + \li Select \uicontrol {Paste properties}. + \endlist + + \note You can't copy material properties between materials of different + material types. + \section1 Using Texture Maps In \QDS you can add many different texture maps to your material. diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h index b35cd3f0f98..a6bea46e597 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h @@ -59,23 +59,10 @@ public: QQuick3DNode *pickNode() const; MouseArea3D *dragHelper() const; + QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const; + static qreal mouseDragMultiplier() { return .02; } -public slots: - void setView3D(QQuick3DViewport *view3D); - void setGrabsMouse(bool grabsMouse); - void setActive(bool active); - void setCirclePickArea(const QPointF &pickArea); - void setMinAngle(qreal angle); - void setPickNode(QQuick3DNode *node); - void setDragHelper(MouseArea3D *dragHelper); - - void setX(qreal x); - void setY(qreal y); - void setWidth(qreal width); - void setHeight(qreal height); - void setPriority(int level); - Q_INVOKABLE QVector3D rayIntersectsPlane(const QVector3D &rayPos0, const QVector3D &rayPos1, const QVector3D &planePos, @@ -98,6 +85,21 @@ public slots: Q_INVOKABLE void forceMoveEvent(double x, double y); Q_INVOKABLE void forceReleaseEvent(double x, double y); +public slots: + void setView3D(QQuick3DViewport *view3D); + void setGrabsMouse(bool grabsMouse); + void setActive(bool active); + void setCirclePickArea(const QPointF &pickArea); + void setMinAngle(qreal angle); + void setPickNode(QQuick3DNode *node); + void setDragHelper(MouseArea3D *dragHelper); + + void setX(qreal x); + void setY(qreal y); + void setWidth(qreal width); + void setHeight(qreal height); + void setPriority(int level); + signals: void view3DChanged(); @@ -131,7 +133,6 @@ private: void setHovering(bool enable); QVector3D getNormal() const; QVector3D getCameraToNodeDir(QQuick3DNode *node) const; - QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const; Q_DISABLE_COPY(MouseArea3D) QQuick3DViewport *m_view3D = nullptr; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp index 27732b4b134..f2f869de192 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp @@ -73,9 +73,7 @@ void QmlStateNodeInstance::setPropertyVariant(const PropertyName &name, const QV void QmlStateNodeInstance::setPropertyBinding(const PropertyName &name, const QString &expression) { - bool isStateOfTheRootModelNode = parentInstance() && parentInstance()->isRootNodeInstance(); - - if (name == "when" && (isStateOfTheRootModelNode)) + if (name == "when") return; ObjectNodeInstance::setPropertyBinding(name, expression); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 8528a455b05..608354aa4a6 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -405,18 +405,33 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos(const QPointF &pos) Q_ARG(QVariant, pos.x()), Q_ARG(QVariant, pos.y())); QObject *gizmoObj = qvariant_cast(gizmoVar); - QVariant instance = -1; + qint32 instanceId = -1; if (gizmoObj && hasInstanceForObject(gizmoObj)) { - instance = instanceForObject(gizmoObj).instanceId(); + instanceId = instanceForObject(gizmoObj).instanceId(); } else { QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit(); QObject *resolvedPick = helper->resolvePick(hitModel); if (hasInstanceForObject(resolvedPick)) - instance = instanceForObject(resolvedPick).instanceId(); + instanceId = instanceForObject(resolvedPick).instanceId(); } - nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, instance}); + // Also get the intersection with an axis plane of the scene. + QVector3D pos3d; + if (editView) { + Internal::MouseArea3D ma; + ma.setView3D(editView); + ma.setEulerRotation({90, 0, 0}); // Default grid plane (XZ plane) + QVector3D planePos = ma.getMousePosInPlane(nullptr, pos); + const float limit = 10000000; // Remove extremes on nearly parallel plane + if (!qFuzzyCompare(planePos.z(), -1.f) && qAbs(planePos.x()) < limit && qAbs(planePos.y()) < limit) + pos3d = {planePos.x(), 0, planePos.y()}; + } + QVariantList data; + data.append(instanceId); + data.append(pos3d); + nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, + QVariant::fromValue(data)}); #else Q_UNUSED(pos) #endif diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index f97db29715b..6db3bb22f72 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -1,5 +1,3 @@ - - // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 import QtQuick 2.15 @@ -29,7 +27,7 @@ SecondColumnLayout { else return colorEditor.backendValue.value } - property alias gradientPropertyName: cePopup.gradientPropertyName + property alias gradientPropertyName: popupLoader.gradientPropertyName property alias gradientThumbnail: gradientThumbnail property alias shapeGradientThumbnail: shapeGradientThumbnail @@ -45,7 +43,7 @@ SecondColumnLayout { } function initEditor() { - cePopup.initEditor() + colorEditor.color = colorEditor.value } Connections { @@ -53,12 +51,12 @@ SecondColumnLayout { target: colorEditor function onValueChanged() { - if (cePopup.isNotInGradientMode()) + if (popupLoader.isNotInGradientMode()) colorEditor.color = colorEditor.value } function onBackendValueChanged() { - if (cePopup.isNotInGradientMode()) + if (popupLoader.isNotInGradientMode()) colorEditor.color = colorEditor.value } } @@ -85,17 +83,13 @@ SecondColumnLayout { } onColorChanged: { - if (!cePopup.isInValidState) + if (!popupLoader.isInValidState) return - if (colorEditor.supportGradient && cePopup.gradientModel.hasGradient) { - var hexColor = convertColorToString(colorEditor.color) - hexTextField.text = hexColor - cePopup.commitGradientColor() - } + popupLoader.commitToGradient() // Delay setting the color to keep ui responsive - if (cePopup.isNotInGradientMode()) + if (popupLoader.isNotInGradientMode()) colorEditorTimer.restart() } @@ -115,16 +109,16 @@ SecondColumnLayout { id: gradientThumbnail anchors.fill: parent anchors.margins: StudioTheme.Values.border - visible: !cePopup.isNotInGradientMode() + visible: !popupLoader.isNotInGradientMode() && !colorEditor.shapeGradients - && cePopup.hasLinearGradient() + && popupLoader.hasLinearGradient() } Shape { id: shape anchors.fill: parent anchors.margins: StudioTheme.Values.border - visible: !cePopup.isNotInGradientMode() + visible: !popupLoader.isNotInGradientMode() && colorEditor.shapeGradients ShapePath { @@ -159,15 +153,80 @@ SecondColumnLayout { MouseArea { anchors.fill: parent onClicked: { - cePopup.opened ? cePopup.close() : cePopup.open() + popupLoader.opened ? popupLoader.close() : popupLoader.open() forceActiveFocus() } } - ColorEditorPopup { - id: cePopup - x: cePopup.__defaultX - y: cePopup.__defaultY + QtObject { + id: popupLoader + + property bool isInValidState: popupLoader.active ? popupLoader.dialog.isInValidState : true + + property QtObject dialog: popupLoader.loader.item + + property bool opened: popupLoader.active ? popupLoader.dialog.opened : false + + property string gradientPropertyName + + function commitToGradient() { + if (!popupLoader.active) + return + + if (colorEditor.supportGradient && popupLoader.dialog.gradientModel.hasGradient) { + var hexColor = convertColorToString(colorEditor.color) + hexTextField.text = hexColor + popupLoader.dialog.commitGradientColor() + } + } + + function isNotInGradientMode() { + if (!popupLoader.active) + return true + return popupLoader.dialog.isNotInGradientMode() + } + + function hasLinearGradient(){ + if (!popupLoader.active) + return false + return popupLoader.dialog.hasLinearGradient() + } + + function ensureLoader() { + if (!popupLoader.active) + popupLoader.active = true + } + + function open() { + popupLoader.ensureLoader() + popupLoader.dialog.open() + } + + function close() { + popupLoader.ensureLoader() + popupLoader.dialog.close() + } + + function determineActiveColorMode() { + if (popupLoader.active && popupLoader.dialog) + popupLoader.dialog.determineActiveColorMode() + else + colorEditor.color = colorEditor.value + } + + property alias active: popupLoader.loader.active + property Loader loader: Loader { + parent: colorEditor + active: colorEditor.supportGradient + sourceComponent: ColorEditorPopup { + id: cePopup + x: cePopup.__defaultX + y: cePopup.__defaultY + } + onLoaded: { + popupLoader.dialog.initEditor() + } + } } } @@ -180,7 +239,7 @@ SecondColumnLayout { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth width: implicitWidth - enabled: cePopup.isNotInGradientMode() + enabled: popupLoader.isNotInGradientMode() writeValueManually: true validator: RegExpValidator { regExp: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g @@ -191,7 +250,7 @@ SecondColumnLayout { onAccepted: colorEditor.color = colorFromString(hexTextField.text) onCommitData: { colorEditor.color = colorFromString(hexTextField.text) - if (cePopup.isNotInGradientMode()) { + if (popupLoader.isNotInGradientMode()) { if (colorEditor.isVector3D) { backendValue.value = Qt.vector3d(colorEditor.color.r, colorEditor.color.g, @@ -216,9 +275,9 @@ SecondColumnLayout { } } - Component.onCompleted: cePopup.determineActiveColorMode() + Component.onCompleted: popupLoader.determineActiveColorMode() onBackendValueChanged: { - cePopup.determineActiveColorMode() + popupLoader.determineActiveColorMode() } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml index df98ebe4107..773f08f6bb2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml @@ -53,11 +53,10 @@ Section { Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft - icon: StudioTheme.Constants.closeCross onClicked: colorEditorControl.remove() } + ExpandingSpacer {} } } @@ -79,24 +78,17 @@ Section { + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } Item { height: 10 - implicitWidth: { - return StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - } + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutInt.remove() } @@ -124,24 +116,17 @@ Section { + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } Item { height: 10 - implicitWidth: { - return StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - } + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutReal.remove() } @@ -165,12 +150,9 @@ Section { + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutString.remove() } @@ -192,27 +174,20 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth text: layoutBool.backendValue.value - backendValue: layoutBool.backendValue + backendValue: layoutBool.backendValue } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } Item { height: 10 - implicitWidth: { - return StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - } + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutBool.remove() } @@ -238,7 +213,6 @@ Section { } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutUrl.remove() } @@ -270,12 +244,9 @@ Section { showTranslateCheckBox: false } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutAlias.remove() } @@ -301,12 +272,9 @@ Section { + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutTextureInput.remove() } @@ -411,7 +379,6 @@ Section { Spacer { implicitWidth: StudioTheme.Values.controlGap } IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutVector.remove() } @@ -462,16 +429,12 @@ Section { Item { height: 10 - implicitWidth: { - return StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - } + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth visible: vecSize === 2 // Placeholder for last spinbox } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } ExpandingSpacer {} } @@ -497,11 +460,9 @@ Section { + StudioTheme.Values.actionIndicatorWidth } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } + IconIndicator { - Layout.alignment: Qt.AlignLeft icon: StudioTheme.Constants.closeCross onClicked: layoutReadonly.remove() } @@ -541,8 +502,9 @@ Section { active: repeater.loadActive width: loader.item ? loader.item.width : 0 height: loader.item ? loader.item.height : 0 + Layout.fillWidth: true - sourceComponent: { + sourceComponent: { if (propertyType == "color") return colorEditor if (propertyType == "int") @@ -615,7 +577,6 @@ Section { Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } StudioControls.AbstractButton { - id: plusButton buttonIcon: StudioTheme.Constants.plus onClicked: { @@ -691,7 +652,7 @@ Section { RowLayout { width: cePopup.width - 8 PropertyLabel { - text: "Add New Property" + text: qsTr("Add New Property") horizontalAlignment: Text.AlignLeft leftPadding: 8 width: cePopup.width - closeIndicator.width - 24 @@ -707,7 +668,7 @@ Section { RowLayout { PropertyLabel { id: textLabel - text: "Name" + text: qsTr("Name") width: cePopup.labelWidth } StudioControls.TextField { @@ -720,7 +681,7 @@ Section { } RowLayout { PropertyLabel { - text: "Type" + text: qsTr("Type") width: cePopup.labelWidth } StudioControls.ComboBox { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 0a0055535da..b87005fe4bb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -54,6 +54,13 @@ Row { // when the combobox is closed by focusing on some other control. property int hoverIndex: -1 + onCurrentIndexChanged: { + // This is needed to correctly update root.absoluteFilePath in cases where selection + // changes between two nodes of same type. + if (currentIndex !== -1 && !root.backendValue.isBound) + root.absoluteFilePath = fileModel.resolve(root.backendValue.value) + } + DropArea { id: dropArea diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml index 02c9477ba5a..8dcf6a0b73e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml @@ -12,12 +12,11 @@ T.MenuSeparator { implicitContentWidth + leftPadding + rightPadding) implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding) - padding: 0 contentItem: Rectangle { - width: control.parent.width - height: StudioTheme.Values.border + implicitWidth: control.parent.width + implicitHeight: StudioTheme.Values.border color: StudioTheme.Values.themeControlOutline } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 3ca90fe60c3..39c600217b9 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/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index ed3b74ad6ae..ef8af878f31 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -951,7 +951,7 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId, if (checkTypeForQmlUiSupport(typeId)) addMessage(ErrUnsupportedTypeInQmlUi, typeErrorLocation, typeName); - if (m_typeStack.count() > 1 && typeName == "State") { + if (m_typeStack.count() > 1 && typeName == "State" && m_typeStack.last() != "StateGroup") { addMessage(WarnStatesOnlyInRootItemForVisualDesigner, typeErrorLocation); addMessage(ErrStatesOnlyInRootItemInQmlUi, typeErrorLocation); } diff --git a/src/libs/tracing/timelinemodel.cpp b/src/libs/tracing/timelinemodel.cpp index c48514c2d61..bc3b6a8ec8e 100644 --- a/src/libs/tracing/timelinemodel.cpp +++ b/src/libs/tracing/timelinemodel.cpp @@ -422,7 +422,10 @@ int TimelineModel::insert(qint64 startTime, qint64 duration, int selectionId) int index = d->insertStart(TimelineModelPrivate::Range(startTime, duration, selectionId)); if (index < d->ranges.size() - 1) d->incrementStartIndices(index); - d->insertEnd(TimelineModelPrivate::RangeEnd(index, startTime + duration)); + int endIndex = d->insertEnd(TimelineModelPrivate::RangeEnd(index, startTime + duration)); + d->setEndIndex(index, endIndex); + if (endIndex < d->endTimes.size() - 1) + d->incrementEndIndices(endIndex); return index; } @@ -446,7 +449,10 @@ int TimelineModel::insertStart(qint64 startTime, int selectionId) void TimelineModel::insertEnd(int index, qint64 duration) { d->ranges[index].duration = duration; - d->insertEnd(TimelineModelPrivate::RangeEnd(index, d->ranges[index].start + duration)); + int endIndex = d->insertEnd(TimelineModelPrivate::RangeEnd(index, d->ranges[index].start + duration)); + d->setEndIndex(index, endIndex); + if (endIndex < d->endTimes.size() - 1) + d->incrementEndIndices(endIndex); } bool TimelineModel::expanded() const diff --git a/src/libs/tracing/timelinemodel_p.h b/src/libs/tracing/timelinemodel_p.h index 0cd3b133272..e27992de1b5 100644 --- a/src/libs/tracing/timelinemodel_p.h +++ b/src/libs/tracing/timelinemodel_p.h @@ -30,13 +30,14 @@ public: }; struct Range { - Range() : start(-1), duration(-1), selectionId(-1), parent(-1) {} + Range() : start(-1), duration(-1), selectionId(-1), parent(-1), endIndex(-1) {} Range(qint64 start, qint64 duration, int selectionId) : - start(start), duration(duration), selectionId(selectionId), parent(-1) {} + start(start), duration(duration), selectionId(selectionId), parent(-1), endIndex(-1) {} qint64 start; qint64 duration; int selectionId; int parent; + int endIndex; inline qint64 timestamp() const {return start;} }; @@ -55,11 +56,21 @@ public: void incrementStartIndices(int index) { - for (RangeEnd &endTime : endTimes) { - if (endTime.startIndex >= index) - ++(endTime.startIndex); + for (index = index + 1; index < ranges.count(); index++) { + if (ranges[index].endIndex >= 0) + endTimes[ranges[index].endIndex].startIndex++; } } + void incrementEndIndices(int index) + { + for (index = index + 1; index < endTimes.count(); index++) + ranges[endTimes[index].startIndex].endIndex++; + } + + inline void setEndIndex(int index, int endIndex) + { + ranges[index].endIndex = endIndex; + } inline int insertStart(const Range &start) { diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index 7028968e5e4..4b376268387 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -1,6 +1,8 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + #include "utils_global.h" #include diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 3c7c5292198..58932a2c8e4 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -800,4 +800,26 @@ FilePaths FileUtils::toFilePathList(const QStringList &paths) { } +qint64 FileUtils::bytesAvailableFromDFOutput(const QByteArray &dfOutput) +{ + const auto lines = filtered(dfOutput.split('\n'), + [](const QByteArray &line) { return line.size() > 0; }); + + QTC_ASSERT(lines.size() == 2, return -1); + const auto headers = filtered(lines[0].split(' '), + [](const QByteArray &field) { return field.size() > 0; }); + QTC_ASSERT(headers.size() >= 4, return -1); + QTC_ASSERT(headers[3] == QByteArray("Available"), return -1); + + const auto fields = filtered(lines[1].split(' '), + [](const QByteArray &field) { return field.size() > 0; }); + QTC_ASSERT(fields.size() >= 4, return -1); + + bool ok = false; + const quint64 result = QString::fromUtf8(fields[3]).toULongLong(&ok); + if (ok) + return result; + return -1; +} + } // namespace Utils diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 80f91390f40..7b78a0a7ae9 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -82,6 +82,8 @@ public: const FileFilter &filter, const std::function &callBack); + static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); + #ifdef QT_WIDGETS_LIB static void setDialogParentGetter(const std::function &getter); diff --git a/src/plugins/autotest/gtest/gtestconfiguration.cpp b/src/plugins/autotest/gtest/gtestconfiguration.cpp index 82f838eddeb..2ff3b5c6b1e 100644 --- a/src/plugins/autotest/gtest/gtestconfiguration.cpp +++ b/src/plugins/autotest/gtest/gtestconfiguration.cpp @@ -61,7 +61,7 @@ QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) con const QStringList &testSets = testCases(); if (!testSets.isEmpty()) - arguments << "--gtest_filter=" + testSets.join(':'); + arguments << "--gtest_filter=\"" + testSets.join(':') + '"'; auto gSettings = static_cast(framework()->testSettings()); if (!gSettings) diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp index 6dba107be96..a0deb3a9193 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp @@ -329,7 +329,7 @@ void CompilerOptionsBuilder::insertWrappedHeaders(const QStringList &relPaths) static const QString baseDir = creatorResourcePath() + "/cplusplus"; const QString fullPath = baseDir + '/' + relPath; QTC_ASSERT(QDir(fullPath).exists(), continue); - args << includeUserPathOption << QDir::toNativeSeparators(fullPath); + args << (includeUserPathOption + QDir::toNativeSeparators(fullPath)); } const int index = m_options.indexOf(QRegularExpression("\\A-I.*\\z")); @@ -686,7 +686,7 @@ void CompilerOptionsBuilder::addIncludeDirOptionForPath(const HeaderPath &path) return; } - add({includeUserPathOption, QDir::toNativeSeparators(path.path)}); + add(includeUserPathOption + QDir::toNativeSeparators(path.path)); } bool CompilerOptionsBuilder::excludeDefineDirective(const Macro ¯o) const diff --git a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp index a2de9d9c818..2bc07375842 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp @@ -191,8 +191,8 @@ void CompilerOptionsBuilderTest::testHeaderPathOptionsOrder() compilerOptionsBuilder.addHeaderPathOptions(); QCOMPARE(compilerOptionsBuilder.options(), - (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), "-isystem", t.toNative("/dummy"), + (QStringList{"-nostdinc", "-nostdinc++", "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "-isystem", t.toNative("/dummy"), "-isystem", t.toNative("/tmp/builtin_path")})); } @@ -207,8 +207,8 @@ void CompilerOptionsBuilderTest::testHeaderPathOptionsOrderMsvc() compilerOptionsBuilder.addHeaderPathOptions(); QCOMPARE(compilerOptionsBuilder.options(), - (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), "/clang:-isystem", + (QStringList{"-nostdinc", "-nostdinc++", "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "/clang:-isystem", "/clang:" + t.toNative("/dummy"), "/clang:-isystem", "/clang:" + t.toNative("/tmp/builtin_path")})); } @@ -222,7 +222,7 @@ void CompilerOptionsBuilderTest::testUseSystemHeader() compilerOptionsBuilder.addHeaderPathOptions(); QCOMPARE(compilerOptionsBuilder.options(), - (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), + (QStringList{"-nostdinc", "-nostdinc++", "-I" + t.toNative("/tmp/path"), "-isystem", t.toNative("/tmp/system_path"), "-isystem", t.toNative("/dummy"), "-isystem", t.toNative("/tmp/builtin_path")})); @@ -235,7 +235,7 @@ void CompilerOptionsBuilderTest::testNoClangHeadersPath() t.compilerOptionsBuilder->addHeaderPathOptions(); QCOMPARE(t.compilerOptionsBuilder->options(), - (QStringList{"-I", t.toNative("/tmp/path"), "-I", t.toNative("/tmp/system_path")})); + (QStringList{"-I" + t.toNative("/tmp/path"), "-I" + t.toNative("/tmp/system_path")})); } void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderMacOs() @@ -255,8 +255,8 @@ void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderMacOs() compilerOptionsBuilder.addHeaderPathOptions(); QCOMPARE(compilerOptionsBuilder.options(), - (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), + (QStringList{"-nostdinc", "-nostdinc++", "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "-isystem", t.toNative("/usr/include/c++/4.2.1"), "-isystem", t.toNative("/usr/include/c++/4.2.1/backward"), "-isystem", t.toNative("/usr/local/include"), @@ -588,10 +588,11 @@ void CompilerOptionsBuilderTest::testBuildAllOptions() QCOMPARE(compilerOptionsBuilder.options(), (QStringList{"-nostdinc", "-nostdinc++", "-arch", "x86_64", "-fsyntax-only", "-m64", "--target=x86_64-apple-darwin10", "-x", "c++", "-std=c++17", - "-DprojectFoo=projectBar", "-I", wrappedQtHeadersPath, - "-I", wrappedQtCoreHeadersPath, - "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), + "-DprojectFoo=projectBar", + wrappedQtHeadersPath, // contains -I already + wrappedQtCoreHeadersPath, // contains -I already + "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "-isystem", t.toNative("/dummy"), "-isystem", t.toNative("/tmp/builtin_path")})); } @@ -616,10 +617,10 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvc() "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"", "-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"", - "-I", wrappedQtHeadersPath, - "-I", wrappedQtCoreHeadersPath, - "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), + wrappedQtHeadersPath, // contains -I already + wrappedQtCoreHeadersPath, // contains -I already + "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "/clang:-isystem", "/clang:" + t.toNative("/dummy"), "/clang:-isystem", "/clang:" + t.toNative("/tmp/builtin_path")})); } @@ -646,10 +647,10 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvcWithExceptions() "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"", "-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"", - "-I", wrappedQtHeadersPath, - "-I", wrappedQtCoreHeadersPath, - "-I", t.toNative("/tmp/path"), - "-I", t.toNative("/tmp/system_path"), + wrappedQtHeadersPath, // contains -I already + wrappedQtCoreHeadersPath, // contains -I already + "-I" + t.toNative("/tmp/path"), + "-I" + t.toNative("/tmp/system_path"), "/clang:-isystem", "/clang:" + t.toNative("/dummy"), "/clang:-isystem", "/clang:" + t.toNative("/tmp/builtin_path")})); } diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 69f908018ad..1133af10651 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -70,6 +70,7 @@ const char removeGroupItemCommandId[] = "RemoveToGroupItem"; const char fitRootToScreenCommandId[] = "FitRootToScreen"; const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen"; const char editAnnotationCommandId[] = "EditAnnotation"; +const char addMouseAreaFillCommandId[] = "AddMouseAreaFill"; const char openSignalDialogCommandId[] = "OpenSignalDialog"; const char update3DAssetCommandId[] = "Update3DAsset"; @@ -115,6 +116,7 @@ const char addSignalHandlerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContext const char moveToComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Move Component into Separate File"); const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material"); const char editAnnotationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotation"); +const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog"); const char update3DAssetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Update 3D Asset"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index d0e16ab7fad..8861c2499d6 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1449,6 +1449,17 @@ void DesignerActionManager::createDefaultDesignerActions() &singleSelection, &singleSelection)); + addDesignerAction(new ModelNodeContextMenuAction( + addMouseAreaFillCommandId, + addMouseAreaFillDisplayName, + {}, + rootCategory, + QKeySequence(), + (priorityLast+7), + &addMouseAreaFill, + &addMouseAreaFillCheck, + &singleSelection)); + const bool standaloneMode = QmlProjectManager::QmlProject::isQtDesignStudio(); if (!standaloneMode) { diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 6b5539e843e..1d62fd54903 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -42,6 +42,18 @@ inline bool singleSelection(const SelectionContext &selectionState) return selectionState.singleNodeIsSelected(); } +inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext) +{ + if (selectionContext.isValid() && selectionContext.singleNodeIsSelected()) { + ModelNode node = selectionContext.currentSingleSelectedNode(); + if (node.hasMetaInfo()) { + NodeMetaInfo nodeInfo = node.metaInfo(); + return nodeInfo.isSuitableForMouseAreaFill(); + } + } + return false; +} + inline bool isModel(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 78d22a9e9f9..1ab3548b994 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1616,6 +1616,34 @@ void editAnnotation(const SelectionContext &selectionContext) ModelNodeEditorProxy::fromModelNode(selectedNode); } +void addMouseAreaFill(const SelectionContext &selectionContext) +{ + if (!selectionContext.isValid()) { + return; + } + + if (!selectionContext.singleNodeIsSelected()) { + return; + } + + selectionContext.view()->executeInTransaction("DesignerActionManager|addMouseAreaFill", [selectionContext]() { + ModelNode modelNode = selectionContext.currentSingleSelectedNode(); + if (modelNode.isValid()) { + NodeMetaInfo itemMetaInfo = selectionContext.view()->model()->metaInfo("QtQuick.MouseArea", -1, -1); + QTC_ASSERT(itemMetaInfo.isValid(), return); + + QmlDesigner::ModelNode mouseAreaNode = + selectionContext.view()->createModelNode("QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion()); + + modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode); + QmlItemNode mouseAreaItemNode(mouseAreaNode); + if (mouseAreaItemNode.isValid()) { + mouseAreaItemNode.anchors().fill(); + } + } + }); +} + QVariant previewImageDataForGenericNode(const ModelNode &modelNode) { if (modelNode.isValid()) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index c73f930647e..27a68d55236 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -71,6 +71,7 @@ void selectFlowEffect(const SelectionContext &selectionContext); void mergeWithTemplate(const SelectionContext &selectionContext); void removeGroup(const SelectionContext &selectionContext); void editAnnotation(const SelectionContext &selectionContext); +void addMouseAreaFill(const SelectionContext &selectionContext); void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 3fe17abeac2..dfcf496133f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -89,6 +89,8 @@ void ConnectionView::propertiesRemoved(const QList &propertyLi for (const AbstractProperty &property : propertyList) { if (property.isDefaultProperty()) connectionModel()->resetModel(); + + dynamicPropertiesModel()->dispatchPropertyChanges(property); } } @@ -116,8 +118,9 @@ void ConnectionView::variantPropertiesChanged(const QList &prop backendModel()->resetModel(); connectionModel()->variantPropertyChanged(variantProperty); - } + dynamicPropertiesModel()->dispatchPropertyChanges(variantProperty); + } } void ConnectionView::bindingPropertiesChanged(const QList &propertyList, @@ -131,6 +134,8 @@ void ConnectionView::bindingPropertiesChanged(const QList &prop backendModel()->resetModel(); connectionModel()->bindingPropertyChanged(bindingProperty); + + dynamicPropertiesModel()->dispatchPropertyChanges(bindingProperty); } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index c5092d13e83..8951ffb6b82 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -5,14 +5,14 @@ #include "connectionview.h" +#include #include #include -#include -#include -#include #include -#include +#include +#include #include +#include #include #include @@ -117,6 +117,44 @@ bool DynamicPropertiesModel::isValueType(const TypeName &type) return valueTypes.contains(type); } +QVariant DynamicPropertiesModel::defaultValueForType(const TypeName &type) +{ + QVariant value; + if (type == "int") + value = 0; + else if (type == "real") + value = 0.0; + else if (type == "color") + value = QColor(255, 255, 255); + else if (type == "string") + value = "This is a string"; + else if (type == "bool") + value = false; + else if (type == "url") + value = ""; + else if (type == "variant") + value = ""; + + return value; +} + +QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type) +{ + QString expression; + if (type == "alias") + expression = "null"; + else if (type == "TextureInput") + expression = "null"; + else if (type == "vector2d") + expression = "Qt.vector2d(0, 0)"; + else if (type == "vector3d") + expression = "Qt.vector3d(0, 0, 0)"; + else if (type == "vector4d") + expression = "Qt.vector4d(0, 0, 0 ,0)"; + + return expression; +} + DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent) : QStandardItemModel(parent) , m_view(parent) @@ -217,6 +255,20 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name) } } +void DynamicPropertiesModel::dispatchPropertyChanges(const AbstractProperty &abstractProperty) +{ + if (abstractProperty.parentModelNode().simplifiedTypeName() == "PropertyChanges") { + QmlPropertyChanges changes(abstractProperty.parentModelNode()); + if (changes.target().isValid()) { + const ModelNode target = changes.target(); + const PropertyName propertyName = abstractProperty.name(); + const AbstractProperty targetProperty = target.variantProperty(propertyName); + if (target.hasProperty(propertyName) && targetProperty.isDynamic()) + abstractPropertyChanged(targetProperty); + } + } +} + void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty) { if (!bindingProperty.isDynamic()) @@ -240,6 +292,27 @@ void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindi m_handleDataChanged = true; } +void DynamicPropertiesModel::abstractPropertyChanged(const AbstractProperty &property) +{ + if (!property.isDynamic()) + return; + + m_handleDataChanged = false; + + const QList nodes = selectedNodes(); + if (!nodes.contains(property.parentModelNode())) + return; + int rowNumber = findRowForProperty(property); + if (rowNumber > -1) { + if (property.isVariantProperty()) + updateVariantProperty(rowNumber); + else + updateBindingProperty(rowNumber); + } + + m_handleDataChanged = true; +} + void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty) { if (!variantProperty.isDynamic()) @@ -312,12 +385,13 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const { const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); + const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2) + .toString(); if (!m_view->isAttached()) return AbstractProperty(); - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); + ModelNode modelNode = m_view->modelNodeForInternalId(internalId); if (modelNode.isValid()) return modelNode.property(targetPropertyName.toUtf8()); @@ -383,7 +457,7 @@ void DynamicPropertiesModel::addDynamicPropertyForCurrentNode() const ModelNode modelNode = selectedNodes().constFirst(); if (modelNode.isValid()) { try { - modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none")); + modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", "This is a string"); } catch (RewritingException &e) { m_exceptionError = e.description(); QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); @@ -434,16 +508,35 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber) { - m_view->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() { - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - if (bindingProperty.isValid()) { - bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); - } else { - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - if (variantProperty.isValid()) - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - } - }); + m_view->executeInTransaction( + "DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() { + const AbstractProperty property = abstractPropertyForRow(rowNumber); + const PropertyName propertyName = property.name(); + BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); + if (bindingProperty.isValid()) { + bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); + } else { + VariantProperty variantProperty = variantPropertyForRow(rowNumber); + if (variantProperty.isValid()) + variantProperty.parentModelNode().removeProperty(variantProperty.name()); + } + + if (property.isValid()) { + QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode()); + const auto stateOperations = objectNode.allAffectingStatesOperations(); + for (const QmlModelStateOperation &stateOperation : stateOperations) { + if (stateOperation.modelNode().hasProperty(propertyName)) + stateOperation.modelNode().removeProperty(propertyName); + } + + const auto timelineNodes = objectNode.allTimelines(); + for (auto &timelineNode : timelineNodes) { + QmlTimeline timeline(timelineNode); + timeline.removeKeyframesForTargetAndProperty(objectNode.modelNode(), + propertyName); + } + } + }); resetModel(); } @@ -502,6 +595,12 @@ void DynamicPropertiesModel::updateBindingProperty(int rowNumber) QString type = QString::fromUtf8(bindingProperty.dynamicTypeName()); updateDisplayRole(rowNumber, PropertyTypeRow, type); updateDisplayRole(rowNumber, PropertyValueRow, value); + + const QmlObjectNode objectNode = QmlObjectNode(bindingProperty.parentModelNode()); + if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) + value = objectNode.expression(bindingProperty.name()); + + updateDisplayRole(rowNumber, PropertyValueRow, value); } } @@ -515,6 +614,10 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber) QVariant value = variantProperty.value(); QString type = QString::fromUtf8(variantProperty.dynamicTypeName()); updateDisplayRole(rowNumber, PropertyTypeRow, type); + const QmlObjectNode objectNode = QmlObjectNode(variantProperty.parentModelNode()); + if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) + value = objectNode.modelValue(variantProperty.name()); + updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value); } @@ -525,16 +628,21 @@ void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode) if (!modelNode.isValid()) return; - const QList bindingProperties = modelNode.bindingProperties(); - for (const BindingProperty &bindingProperty : bindingProperties) { - if (bindingProperty.isDynamic()) - addBindingProperty(bindingProperty); - } + auto properties = modelNode.properties(); - const QList variantProperties = modelNode.variantProperties(); - for (const VariantProperty &variantProperty : variantProperties) { - if (variantProperty.isDynamic()) - addVariantProperty(variantProperty); + auto dynamicProperties = Utils::filtered(properties, [](const AbstractProperty &p) { + return p.isDynamic(); + }); + + Utils::sort(dynamicProperties, [](const AbstractProperty &a, const AbstractProperty &b) { + return a.name() < b.name(); + }); + + for (const AbstractProperty &property : qAsConst(dynamicProperties)) { + if (property.isBindingProperty()) + addBindingProperty(property.toBindingProperty()); + else if (property.isVariantProperty()) + addVariantProperty(property.toVariantProperty()); } } @@ -713,6 +821,16 @@ int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &var return -1; } +int DynamicPropertiesModel::findRowForProperty(const AbstractProperty &abstractProperty) const +{ + for (int i = 0; i < rowCount(); i++) { + if ((abstractPropertyForRow(i).name() == abstractProperty.name())) + return i; + } + //not found + return -1; +} + bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty) { //### todo we assume no expressions yet diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index f86eb73357f..41dee31a84a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -28,6 +28,7 @@ public: }; DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); void bindingPropertyChanged(const BindingProperty &bindingProperty); + void abstractPropertyChanged(const AbstractProperty &bindingProperty); void variantPropertyChanged(const VariantProperty &variantProperty); void bindingRemoved(const BindingProperty &bindingProperty); void variantRemoved(const VariantProperty &variantProperty); @@ -51,9 +52,13 @@ public: BindingProperty replaceVariantWithBinding(const PropertyName &name, bool copyValue = false); void resetProperty(const PropertyName &name); + void dispatchPropertyChanges(const AbstractProperty &abstractProperty); + QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode); static bool isValueType(const TypeName &type); + static QVariant defaultValueForType(const TypeName &type); + static QString defaultExpressionForType(const TypeName &type); protected: void addProperty(const QVariant &propertyValue, @@ -72,6 +77,7 @@ protected: void updateCustomData(int row, const AbstractProperty &property); int findRowForBindingProperty(const BindingProperty &bindingProperty) const; int findRowForVariantProperty(const VariantProperty &variantProperty) const; + int findRowForProperty(const AbstractProperty &abstractProperty) const; bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index d056377b170..f0789bd6d9d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -268,15 +268,16 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, * Response from puppet process for the model at requested position * * @param modelNode Node picked at the requested position or invalid node if nothing could be picked + * @param pos3d 3D scene position of the requested view position */ -void Edit3DView::nodeAtPosReady(const ModelNode &modelNode) +void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) { if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) { // Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item // while the context-menu is shown doesn't select the item. if (modelNode.isValid() && !modelNode.isSelected()) setSelectedModelNode(modelNode); - m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode); + m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode, pos3d); } else if (m_nodeAtPosReqType == NodeAtPosReqType::MaterialDrop) { // TODO: this is from 8.0 branch that doesn't apply anymore: // const bool isModel = modelNode.isSubclassOf("QtQuick3D.Model"); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 3df7b263afc..bd866098e5d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -41,7 +41,7 @@ public: void modelAboutToBeDetached(Model *model) override; void importsChanged(const QList &addedImports, const QList &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; - void nodeAtPosReady(const ModelNode &modelNode) override; + void nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) override; void sendInputEvent(QInputEvent *e) const; void edit3DViewResized(const QSize &size) const; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 7f49b3376cf..2c497a425f3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -215,7 +215,7 @@ void Edit3DWidget::onCreateAction() // int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt(); // auto modelNode = QmlVisualNode::createQml3DNode(m_view, m_nameToEntry.value(action->data().toString()), - // activeScene).modelNode(); + // activeScene, m_contextMenuPos3d).modelNode(); // QTC_ASSERT(modelNode.isValid(), return); // m_view->setSelectedModelNode(modelNode); @@ -273,9 +273,10 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) m_backgroundColorMenu->close(); } -void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode) +void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d) { m_contextMenuTarget = modelNode; + m_contextMenuPos3d = pos3d; const bool isValid = modelNode.isValid(); // TODO: this is from 8.0 branch that doesn't apply anymore: diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index d36e9e3db32..24c702617d0 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,7 @@ public: QMenu *backgroundColorMenu() const; void showBackgroundColorMenu(bool show, const QPoint &pos); - void showContextMenu(const QPoint &pos, const ModelNode &modelNode); + void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d); void updateCreateSubMenu(const QStringList &keys, const QHash> &entriesMap); @@ -63,6 +64,7 @@ private: QPointer m_deleteAction; QPointer m_createSubMenu; ModelNode m_contextMenuTarget; + QVector3D m_contextMenuPos3d; QHash m_nameToEntry; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 7b6f3a37409..c4338484970 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -799,6 +799,8 @@ void MaterialEditorView::propertiesRemoved(const QList &proper setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); changed = true; } + + dynamicPropertiesModel()->dispatchPropertyChanges(property); } if (changed) requestPreviewRender(); @@ -822,6 +824,8 @@ void MaterialEditorView::variantPropertiesChanged(const QList & changed = true; } + + dynamicPropertiesModel()->dispatchPropertyChanges(property); } if (changed) requestPreviewRender(); @@ -849,6 +853,8 @@ void MaterialEditorView::bindingPropertiesChanged(const QList & changed = true; } + + dynamicPropertiesModel()->dispatchPropertyChanges(property); } if (changed) requestPreviewRender(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 34d50ed7666..9dd87e54c99 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -106,8 +106,10 @@ QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) c QmlObjectNode objectNode = property.parentQmlObjectNode(); return objectNode.modelValue(property.name()); } else if (role == propertyBindingRole) { - if (property.isBindingProperty()) - return property.toBindingProperty().expression(); + if (property.isBindingProperty()) { + QmlObjectNode objectNode = property.parentQmlObjectNode(); + return objectNode.expression(property.name()); + } return QVariant(); } qWarning() << Q_FUNC_INFO << "invalid role"; @@ -142,45 +144,23 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); + TypeName typeName = type.toUtf8(); + const auto selectedNodes = dynamicPropertiesModel()->selectedNodes(); if (selectedNodes.count() == 1) { const ModelNode modelNode = selectedNodes.constFirst(); if (modelNode.isValid()) { try { - if (Internal::DynamicPropertiesModel::isValueType(type.toUtf8())) { - QVariant value; - if (type == "int") - value = 0; - else if (type == "real") - value = 0.0; - else if (type == "color") - value = QColor(255, 255, 255); - else if (type == "string") - value = ""; - else if (type == "bool") - value = false; - else if (type == "url") - value = ""; - else if (type == "variant") - value = ""; - + if (Internal::DynamicPropertiesModel::isValueType(typeName)) { + QVariant value = Internal::DynamicPropertiesModel::defaultValueForType(typeName); modelNode.variantProperty(name.toUtf8()) - .setDynamicTypeNameAndValue(type.toUtf8(), value); + .setDynamicTypeNameAndValue(typeName, value); } else { - QString expression; - if (type == "alias") - expression = "null"; - else if (type == "TextureInput") - expression = "null"; - else if (type == "vector2d") - expression = "Qt.vector2d(0, 0)"; - else if (type == "vector3d") - expression = "Qt.vector3d(0, 0, 0)"; - else if (type == "vector4d") - expression = "Qt.vector4d(0, 0, 0 ,0)"; + QString expression = Internal::DynamicPropertiesModel::defaultExpressionForType( + typeName); modelNode.bindingProperty(name.toUtf8()) - .setDynamicTypeNameAndExpression(type.toUtf8(), expression); + .setDynamicTypeNameAndExpression(typeName, expression); } } catch (Exception &e) { e.showException(); @@ -203,7 +183,12 @@ DynamicPropertyRow::DynamicPropertyRow() QObject::connect(m_backendValue, &PropertyEditorValue::expressionChanged, this, - [this](const QString &) { commitExpression(m_backendValue->expression()); }); + [this](const QString &name) { + if (!name.isEmpty()) //If name is empty the notifer is only for QML + commitExpression(m_backendValue->expression()); + else if (m_backendValue->expression().isEmpty()) + resetValue(); + }); } DynamicPropertyRow::~DynamicPropertyRow() @@ -301,13 +286,16 @@ void DynamicPropertyRow::setupBackendValue() m_backendValue->setModelNode(node); QVariant modelValue = property.parentQmlObjectNode().modelValue(property.name()); + + const bool isBound = property.parentQmlObjectNode().hasBindingProperty(property.name()); + if (modelValue != m_backendValue->value()) { m_backendValue->setValue({}); m_backendValue->setValue(modelValue); } - if (property.isBindingProperty()) { - QString expression = property.toBindingProperty().expression(); + if (isBound) { + QString expression = property.parentQmlObjectNode().expression(property.name()); if (m_backendValue->expression() != expression) m_backendValue->setExpression(expression); } @@ -323,6 +311,9 @@ void DynamicPropertyRow::commitValue(const QVariant &value) if (m_row < 0) return; + if (!value.isValid()) + return; + auto propertiesModel = m_model->dynamicPropertiesModel(); VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row); @@ -336,11 +327,11 @@ void DynamicPropertyRow::commitValue(const QVariant &value) RewriterTransaction transaction = view->beginRewriterTransaction( QByteArrayLiteral("DynamicPropertiesModel::commitValue")); try { - if (view->currentState().isBaseState()) { + QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); + if (view->currentState().isBaseState() && !objectNode.timelineIsActive()) { if (variantProperty.value() != value) variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); } else { - QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); QTC_CHECK(objectNode.isValid()); PropertyName name = variantProperty.name(); if (objectNode.isValid() && objectNode.modelValue(name) != value) @@ -360,24 +351,43 @@ void DynamicPropertyRow::commitExpression(const QString &expression) if (m_row < 0) return; + auto propertiesModel = m_model->dynamicPropertiesModel(); + AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + + BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name()); + + const QVariant literal = BindingProperty::convertToLiteral(bindingProperty.dynamicTypeName(), + expression); + + if (literal.isValid()) { //If the string can be converted to a literal we set it as a literal/value + commitValue(literal); + return; + } + m_lock = true; auto unlock = qScopeGuard([this] { m_lock = false; }); - auto propertiesModel = m_model->dynamicPropertiesModel(); - BindingProperty bindingProperty = propertiesModel->bindingPropertyForRow(m_row); - auto view = propertiesModel->view(); RewriterTransaction transaction = view->beginRewriterTransaction( - QByteArrayLiteral("DynamicPropertiesModel::commitExpression")); + QByteArrayLiteral("DynamicPropertyRow::commitExpression")); try { QString theExpression = expression; if (theExpression.isEmpty()) theExpression = "null"; - if (bindingProperty.expression() != theExpression) { - bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), - theExpression); + if (view->currentState().isBaseState()) { + if (bindingProperty.expression() != theExpression) { + bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), + theExpression); + } + } else { + QmlObjectNode objectNode = bindingProperty.parentQmlObjectNode(); + QTC_CHECK(objectNode.isValid()); + PropertyName name = bindingProperty.name(); + if (objectNode.isValid() && objectNode.expression(name) != theExpression) + objectNode.setBindingProperty(name, theExpression); } + transaction.commit(); //committing in the try block } catch (Exception &e) { e.showException(); @@ -390,3 +400,46 @@ void DynamicPropertyRow::handleDataChanged(const QModelIndex &topLeft, const QMo if (topLeft.row() == m_row) setupBackendValue(); } + +void DynamicPropertyRow::resetValue() +{ + if (m_lock) + return; + + if (m_row < 0) + return; + + auto propertiesModel = m_model->dynamicPropertiesModel(); + auto view = propertiesModel->view(); + + AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + TypeName typeName = property.dynamicTypeName(); + + if (view->currentState().isBaseState()) { + if (Internal::DynamicPropertiesModel::isValueType(typeName)) { + QVariant value = Internal::DynamicPropertiesModel::defaultValueForType(typeName); + commitValue(value); + } else { + QString expression = Internal::DynamicPropertiesModel::defaultExpressionForType( + typeName); + commitExpression(expression); + } + } else { + m_lock = true; + auto unlock = qScopeGuard([this] { m_lock = false; }); + + RewriterTransaction transaction = view->beginRewriterTransaction( + QByteArrayLiteral("DynamicPropertyRow::resetValue")); + try { + QmlObjectNode objectNode = property.parentQmlObjectNode(); + QTC_CHECK(objectNode.isValid()); + PropertyName name = property.name(); + if (objectNode.isValid() && objectNode.propertyAffectedByCurrentState(name)) + objectNode.removeProperty(name); + + transaction.commit(); //committing in the try block + } catch (Exception &e) { + e.showException(); + } + } +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h index 4c33f1b9026..16a59d2cf2d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h @@ -100,6 +100,7 @@ private: void commitValue(const QVariant &value); void commitExpression(const QString &expression); void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &, const QList &); + void resetValue(); int m_row = -1; PropertyEditorValue *m_backendValue = nullptr; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index aa8996bf751..d3fcbb644d2 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -181,7 +181,7 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression) if ( m_expression != expression) { setExpression(expression); m_value.clear(); - emit expressionChanged(nameAsQString()); + emit expressionChanged(nameAsQString()); //Note that we set the name in this case } } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 6279773e47b..e7199170dab 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -135,7 +135,11 @@ signals: void valueChanged(const QString &name, const QVariant&); void valueChangedQml(); - void expressionChanged(const QString &name); + void expressionChanged(const QString &name); //HACK - We use the same notifer + //for the backend and frontend. + //If name is empty the signal is + //used for QML. + void exportPropertyAsAliasRequested(const QString &name); void removeAliasExportRequested(const QString &name); diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 3a17a2f00f6..8d141a47bfe 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -145,7 +146,7 @@ public: void emitUpdateActiveScene3D(const QVariantMap &sceneState); void emitModelNodelPreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); void emitImport3DSupportChanged(const QVariantMap &supportMap); - void emitNodeAtPosResult(const ModelNode &modelNode); + void emitNodeAtPosResult(const ModelNode &modelNode, const QVector3D &pos3d); void emitView3DAction(View3DActionType type, const QVariant &value); void sendTokenToInstances(const QString &token, int number, const QVector &nodeVector); @@ -214,7 +215,7 @@ public: virtual void renderImage3DChanged(const QImage &image); virtual void updateActiveScene3D(const QVariantMap &sceneState); virtual void updateImport3DSupport(const QVariantMap &supportMap); - virtual void nodeAtPosReady(const ModelNode &modelNode); + virtual void nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d); virtual void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); virtual void view3DAction(View3DActionType type, const QVariant &value); diff --git a/src/plugins/qmldesigner/designercore/include/bindingproperty.h b/src/plugins/qmldesigner/designercore/include/bindingproperty.h index 18e0c8119a3..811abc742e0 100644 --- a/src/plugins/qmldesigner/designercore/include/bindingproperty.h +++ b/src/plugins/qmldesigner/designercore/include/bindingproperty.h @@ -37,6 +37,7 @@ public: bool isAlias() const; bool isAliasExport() const; + static QVariant convertToLiteral(const TypeName &typeName, const QString &expression); protected: BindingProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 76d0e840822..c014259757f 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -87,6 +87,7 @@ public: bool isFlowViewFlowWildcard() const; bool isFlowViewItem() const; bool isFont() const; + bool isSuitableForMouseAreaFill() const; bool isGraphicalItem() const; bool isInteger() const; bool isLayoutable() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h index f61e5c064d2..33d836f8639 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h @@ -119,6 +119,8 @@ public: QmlModelStateGroup states() const; + QList allTimelines() const; + protected: NodeInstance nodeInstance() const; QmlObjectNode nodeForInstance(const NodeInstance &instance) const; diff --git a/src/plugins/qmldesigner/designercore/include/qmltimeline.h b/src/plugins/qmldesigner/designercore/include/qmltimeline.h index dffe741cdff..8662e8d6665 100644 --- a/src/plugins/qmldesigner/designercore/include/qmltimeline.h +++ b/src/plugins/qmldesigner/designercore/include/qmltimeline.h @@ -44,6 +44,10 @@ public: QList allTargets() const; QList keyframeGroupsForTarget(const ModelNode &target) const; void destroyKeyframesForTarget(const ModelNode &target); + + void removeKeyframesForTargetAndProperty(const ModelNode &target, + const PropertyName &propertyName); + static bool hasActiveTimeline(AbstractView *view); bool isRecording() const; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 0a3a10c0bbc..53e7fa8751a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1737,8 +1737,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand const QVariantMap supportMap = qvariant_cast(command.data()); emitImport3DSupportChanged(supportMap); } else if (command.type() == PuppetToCreatorCommand::NodeAtPos) { - ModelNode modelNode = modelNodeForInternalId(command.data().toUInt()); - emitNodeAtPosResult(modelNode); + auto data = qvariant_cast(command.data()); + if (data.size() == 2) { + ModelNode modelNode = modelNodeForInternalId(data[0].toInt()); + QVector3D pos3d = data[1].value(); + emitNodeAtPosResult(modelNode, pos3d); + } } } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 285cc456a37..d4d7c4d43b8 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1689,6 +1689,12 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino return false; } +bool NodeMetaInfo::isSuitableForMouseAreaFill() const +{ + return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") + && !isSubclassOf("QtQuick.Controls.Control") && !isSubclassOf("QtQuick.Templates.Control"); +} + bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index cb3f45dbfe1..f111b6efff2 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -388,8 +388,9 @@ void AbstractView::updateImport3DSupport(const QVariantMap & /*supportMap*/) { } -// a Quick3DModel that is picked at the requested position in the 3D Editor -void AbstractView::nodeAtPosReady(const ModelNode & /*modelNode*/) {} +// a Quick3DNode that is picked at the requested view position in the 3D Editor and the 3D scene +// position of the requested view position. +void AbstractView::nodeAtPosReady(const ModelNode & /*modelNode*/, const QVector3D &/*pos3d*/) {} void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, const QPixmap & /*pixmap*/) { @@ -783,10 +784,10 @@ void AbstractView::emitImport3DSupportChanged(const QVariantMap &supportMap) model()->d->notifyImport3DSupportChanged(supportMap); } -void AbstractView::emitNodeAtPosResult(const ModelNode &modelNode) +void AbstractView::emitNodeAtPosResult(const ModelNode &modelNode, const QVector3D &pos3d) { if (model()) - model()->d->notifyNodeAtPosResult(modelNode); + model()->d->notifyNodeAtPosResult(modelNode, pos3d); } void AbstractView::emitView3DAction(View3DActionType type, const QVariant &value) diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 3f854b20415..23c149511d1 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -269,6 +269,52 @@ bool BindingProperty::isAliasExport() const && parentModelNode().view()->modelNodeForId(expression()).isValid(); } +static bool isTrueFalseLiteral(const QString &expression) +{ + return (expression.compare("false", Qt::CaseInsensitive) == 0) + || (expression.compare("true", Qt::CaseInsensitive) == 0); +} + +QVariant BindingProperty::convertToLiteral(const TypeName &typeName, const QString &testExpression) +{ + if ("QColor" == typeName || "color" == typeName) { + QString unquoted = testExpression; + unquoted.remove('"'); + if (QColor(unquoted).isValid()) + return QColor(unquoted); + } else if ("bool" == typeName) { + if (isTrueFalseLiteral(testExpression)) { + if (testExpression.compare("true", Qt::CaseInsensitive) == 0) + return true; + else + return false; + } + } else if ("int" == typeName) { + bool ok; + int intValue = testExpression.toInt(&ok); + if (ok) + return intValue; + } else if ("qreal" == typeName || "real" == typeName) { + bool ok; + qreal realValue = testExpression.toDouble(&ok); + if (ok) + return realValue; + } else if ("QVariant" == typeName || "variant" == typeName) { + bool ok; + qreal realValue = testExpression.toDouble(&ok); + if (ok) { + return realValue; + } else if (isTrueFalseLiteral(testExpression)) { + if (testExpression.compare("true", Qt::CaseInsensitive) == 0) + return true; + else + return false; + } + } + + return {}; +} + void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName, const QString &expression) { Internal::WriteLocker locker(model()); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 8513b79f0ea..8aacbe0a213 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -571,9 +571,9 @@ void ModelPrivate::notifyImport3DSupportChanged(const QVariantMap &supportMap) notifyInstanceChanges([&](AbstractView *view) { view->updateImport3DSupport(supportMap); }); } -void ModelPrivate::notifyNodeAtPosResult(const ModelNode &modelNode) +void ModelPrivate::notifyNodeAtPosResult(const ModelNode &modelNode, const QVector3D &pos3d) { - notifyInstanceChanges([&](AbstractView *view) { view->nodeAtPosReady(modelNode); }); + notifyInstanceChanges([&](AbstractView *view) { view->nodeAtPosReady(modelNode, pos3d); }); } void ModelPrivate::notifyView3DAction(View3DActionType type, const QVariant &value) diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index dac6a4f5d96..51b94cb0eca 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "modelnode.h" #include "abstractview.h" @@ -170,7 +171,7 @@ public: void notifyUpdateActiveScene3D(const QVariantMap &sceneState); void notifyModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); void notifyImport3DSupportChanged(const QVariantMap &supportMap); - void notifyNodeAtPosResult(const ModelNode &modelNode); + void notifyNodeAtPosResult(const ModelNode &modelNode, const QVector3D &pos3d); void notifyView3DAction(View3DActionType type, const QVariant &value); void notifyDragStarted(QMimeData *mimeData); diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index 7da3939ebe9..ad4d40202a9 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -554,6 +554,12 @@ QList QmlObjectNode::allDefinedStates() const for (const QmlVisualNode &node : qAsConst(allVisualNodes)) returnList.append(node.states().allStates()); + const auto allNodes = view()->allModelNodes(); + for (const ModelNode &node : allNodes) { + if (node.simplifiedTypeName() == "StateGroup") + returnList.append(QmlModelStateGroup(node).allStates()); + } + return returnList; } @@ -575,6 +581,18 @@ QmlModelStateGroup QmlObjectNode::states() const return QmlModelStateGroup(); } +QList QmlObjectNode::allTimelines() const +{ + QList timelineNodes; + const auto allNodes = view()->allModelNodes(); + for (const auto &timelineNode : allNodes) { + if (QmlTimeline::isValidQmlTimeline(timelineNode)) + timelineNodes.append(timelineNode); + } + + return timelineNodes; +} + /*! Removes a variant property of the object specified by \a name from the model. diff --git a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp index 260720d2a6f..2c7ba32a77a 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp @@ -185,6 +185,15 @@ void QmlTimeline::destroyKeyframesForTarget(const ModelNode &target) frames.destroy(); } +void QmlTimeline::removeKeyframesForTargetAndProperty(const ModelNode &target, + const PropertyName &propertyName) +{ + for (QmlTimelineKeyframeGroup frames : keyframeGroupsForTarget(target)) { + if (frames.propertyName() == propertyName) + frames.destroy(); + } +} + bool QmlTimeline::hasActiveTimeline(AbstractView *view) { if (view && view->isAttached()) { diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index e039e0e3f3b..f8b004e99ab 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -268,7 +268,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, QList propertyBindingList; QList propertyEnumList; if (itemLibraryEntry.qmlSource().isEmpty()) { - QList > propertyPairList = position.propertyPairList(); + QList > propertyPairList; for (const auto &property : itemLibraryEntry.properties()) { if (property.type() == "binding") { @@ -282,6 +282,8 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, propertyPairList.append({property.name(), property.value()}); } } + // Add position last so it'll override any default position specified in the entry + propertyPairList.append(position.propertyPairList()); ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource; if (itemLibraryEntry.typeName() == "QtQml.Component") diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index 933596003e3..5074abd1a58 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -219,7 +219,7 @@ void FileSystemAccessTest::testFileTransfer() QVERIFY2(dirForFilesToDownload.isValid(), qPrintable(dirForFilesToDownload.errorString())); QVERIFY2(dir2ForFilesToDownload.isValid(), qPrintable(dirForFilesToDownload.errorString())); static const auto getRemoteFilePath = [this](const QString &localFileName) { - return m_device->filePath(QString("/tmp/").append(localFileName).append(".upload")); + return m_device->filePath(QString("/tmp/foo/bar/").append(localFileName).append(".upload")); }; FilesToTransfer filesToUpload; std::srand(QDateTime::currentDateTime().toSecsSinceEpoch()); @@ -255,11 +255,11 @@ void FileSystemAccessTest::testFileTransfer() getRemoteFilePath(bigFileName)}; fileTransfer.setFilesToTransfer(filesToUpload); - QString jobError; + ProcessResultData result; QEventLoop loop; - connect(&fileTransfer, &FileTransfer::done, [&jobError, &loop] + connect(&fileTransfer, &FileTransfer::done, [&result, &loop] (const ProcessResultData &resultData) { - jobError = resultData.m_errorString; + result = resultData; loop.quit(); }); QTimer timer; @@ -271,7 +271,15 @@ void FileSystemAccessTest::testFileTransfer() loop.exec(); QVERIFY(timer.isActive()); timer.stop(); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); + QCOMPARE(result.m_exitStatus, QProcess::NormalExit); + QVERIFY2(result.m_errorString.isEmpty(), qPrintable(result.m_errorString)); + QCOMPARE(result.m_exitCode, 0); + QCOMPARE(result.m_error, QProcess::UnknownError); + + // Cleanup remote + const FilePath remoteDir = m_device->filePath(QString("/tmp/foo/")); + QString errorString; + QVERIFY2(remoteDir.removeRecursively(&errorString), qPrintable(errorString)); } } // Internal diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index ec09e7b6932..72efd9cbc61 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1269,13 +1269,9 @@ qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const QTC_ASSERT(handlesFile(filePath), return -1); CommandLine cmd("df", {"-k"}); cmd.addArg(filePath.path()); - cmd.addArgs("|tail -n 1 |sed 's/ */ /g'|cut -d ' ' -f 4", CommandLine::Raw); const QByteArray output = d->outputForRunInShell(cmd).value_or(QByteArray()); - bool ok = false; - const qint64 size = output.toLongLong(&ok); - if (ok) - return size * 1024; - return -1; + + return FileUtils::bytesAvailableFromDFOutput(output); } QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const @@ -1499,18 +1495,12 @@ private: return; } - m_batchFile.reset(new QTemporaryFile(this)); - if (!m_batchFile->isOpen() && !m_batchFile->open()) { - startFailed(Tr::tr("Could not create temporary file: %1") - .arg(m_batchFile->errorString())); - return; - } + QByteArray batchData; const FilePaths dirs = dirsToCreate(m_setup.m_files); for (const FilePath &dir : dirs) { if (direction() == FileTransferDirection::Upload) { - m_batchFile->write("-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() - + '\n'); + batchData += "-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n'; } else if (direction() == FileTransferDirection::Download) { if (!QDir::root().mkpath(dir.path())) { startFailed(Tr::tr("Failed to create local directory \"%1\".") @@ -1527,26 +1517,23 @@ private: const QFileInfo fi(file.m_source.toFileInfo()); if (fi.isSymLink()) { link = true; - m_batchFile->write("-rm " + ProcessArgs::quoteArgUnix( - file.m_target.path()).toLocal8Bit() + '\n'); + batchData += "-rm " + ProcessArgs::quoteArgUnix( + file.m_target.path()).toLocal8Bit() + '\n'; // see QTBUG-5817. sourceFileOrLinkTarget = sourceFileOrLinkTarget.withNewPath(fi.dir().relativeFilePath(fi.symLinkTarget())); } } - m_batchFile->write(transferCommand(direction(), link) + ' ' - + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' ' - + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n'); + batchData += transferCommand(direction(), link) + ' ' + + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' ' + + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n'; } - m_batchFile->close(); - process().setCommand(CommandLine(sftpBinary, fullConnectionOptions() - << "-b" << m_batchFile->fileName() << host())); + process().setCommand({sftpBinary, fullConnectionOptions() << "-b" << "-" << host()}); + process().setWriteData(batchData); process().start(); } void doneImpl() final { handleDone(); } - - std::unique_ptr m_batchFile; }; class RsyncTransferImpl : public SshTransferInterface diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index d22f03cef4d..0c9bbe95c73 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -60,6 +60,9 @@ private slots: void resolvePath(); void relativeChildPath_data(); void relativeChildPath(); + void bytesAvailableFromDF_data(); + void bytesAvailableFromDF(); + void asyncLocalCopy(); void startsWithDriveLetter(); void startsWithDriveLetter_data(); @@ -888,5 +891,32 @@ void tst_fileutils::url_data() QTest::newRow("http-qt.io-index.html") << QString("http://qt.io/index.html") << QString("http") << QString("qt.io") << QString("/index.html"); } +void tst_fileutils::bytesAvailableFromDF_data() +{ + QTest::addColumn("dfOutput"); + QTest::addColumn("expected"); + + QTest::newRow("empty") << QByteArray("") << qint64(-1); + QTest::newRow("mac") << QByteArray("Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on\n/dev/disk3s5 971350180 610014564 342672532 65% 4246780 3426725320 0% /System/Volumes/Data\n") << qint64(342672532); + QTest::newRow("alpine") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\noverlay 569466448 163526072 376983360 30% /\n") << qint64(376983360); + QTest::newRow("alpine-no-trailing-br") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\noverlay 569466448 163526072 376983360 30% /") << qint64(376983360); + QTest::newRow("alpine-missing-line") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\n") << qint64(-1); + QTest::newRow("wrong-header") << QByteArray("Filesystem 1K-blocks Used avail Use% Mounted on\noverlay 569466448 163526072 376983360 30% /\n") << qint64(-1); + QTest::newRow("not-enough-fields") << QByteArray("Filesystem 1K-blocks Used avail Use% Mounted on\noverlay 569466448\n") << qint64(-1); + QTest::newRow("not-enough-header-fields") << QByteArray("Filesystem 1K-blocks Used \noverlay 569466448 163526072 376983360 30% /\n") << qint64(-1); +} + +void tst_fileutils::bytesAvailableFromDF() +{ + if (HostOsInfo::isWindowsHost()) + QSKIP("Test only valid on unix-ish systems"); + + QFETCH(QByteArray, dfOutput); + QFETCH(qint64, expected); + + const auto result = FileUtils::bytesAvailableFromDFOutput(dfOutput); + QCOMPARE(result, expected); +} + QTEST_GUILESS_MAIN(tst_fileutils) #include "tst_fileutils.moc" diff --git a/tests/system/shared/debugger.py b/tests/system/shared/debugger.py index ea6a3b840e4..338d83cd758 100644 --- a/tests/system/shared/debugger.py +++ b/tests/system/shared/debugger.py @@ -152,6 +152,8 @@ def doSimpleDebugging(currentKit, currentConfigName, expectedBPOrder=[], enableQ # param currentKit specifies the ID of the kit to use (see class Targets) def isMsvcConfig(currentKit): + if platform.system() not in ('Microsoft' 'Windows'): + return False switchViewTo(ViewConstants.PROJECTS) switchToBuildOrRunSettingsFor(currentKit, ProjectSettings.BUILD) @@ -164,7 +166,8 @@ def isMsvcConfig(currentKit): raise Exception("Kit '%s' is not activated in the project." % wantedKitName) index = waitForObject(wantedKitIndexString) toolTip = str(index.data(Qt.ToolTipRole).toString()) - compilerPattern = re.compile("Compiler:(?P.+)") + compilerPattern = re.compile('
Compiler:
(?P.+)' + '
Environment:') match = compilerPattern.search(toolTip) if match is None: test.warning("UI seems to have changed - failed to check for compiler.")