diff options
84 files changed, 2922 insertions, 451 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml index 7481d24225..7ae7bd44d9 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml @@ -121,7 +121,7 @@ View3D { pick(mouse); if (pickObj) { axisHelperView.editCameraCtrl.focusObject(axisHelperView.selectedNode, - pickObj.cameraRotation, false); + pickObj.cameraRotation, false, false); } else { mouse.accepted = false; } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml index 207dc94199..20dd112d61 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml @@ -87,14 +87,15 @@ Item { } - function focusObject(targetObject, rotation, updateZoom) + function focusObject(targetObject, rotation, updateZoom, closeUp) { if (!camera) return; camera.eulerRotation = rotation; var newLookAtAndZoom = _generalHelper.focusObjectToCamera( - camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, updateZoom); + camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, + updateZoom, closeUp); _lookAtPoint = newLookAtAndZoom.toVector3d(); _zoomFactor = newLookAtAndZoom.w; storeCameraState(0); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index db8d4fb4ea..6527d06e6e 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -146,7 +146,7 @@ Item { if (editView) { var targetNode = selectionBoxes.length > 0 ? selectionBoxes[0].model : null; - cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true); + cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true, false); } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml index b16b27406d..afaf8481ca 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml @@ -42,7 +42,7 @@ Item { function fitAndHideBox() : bool { - cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true); + cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true, true); if (cameraControl._zoomFactor < 0.1) { view3D.importScene.scale = view3D.importScene.scale.times(10); return false; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index f99d219c5c..f62a7e5173 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -148,7 +148,7 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de // Return value contains new lookAt point (xyz) and zoom factor (w) QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance, QQuick3DNode *targetObject, QQuick3DViewport *viewPort, - float oldZoom, bool updateZoom) + float oldZoom, bool updateZoom, bool closeUp) { if (!camera) return QVector4D(0.f, 0.f, 0.f, 1.f); @@ -200,7 +200,9 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau camera->setPosition(lookAt + newLookVector); - float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / 900.), 100.f) : oldZoom; + qreal divisor = closeUp ? 900. : 725.; + + float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / divisor), 100.f) : oldZoom; float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); return QVector4D(lookAt, cameraZoomFactor); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index e9d8e52545..15795ac28b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -66,8 +66,9 @@ public: float defaultLookAtDistance, const QVector3D &lookAt, float zoomFactor, bool relative); Q_INVOKABLE QVector4D focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance, - QQuick3DNode *targetObject, QQuick3DViewport *viewPort, - float oldZoom, bool updateZoom = true); + QQuick3DNode *targetObject, QQuick3DViewport *viewPort, + float oldZoom, bool updateZoom = true, + bool closeUp = false); Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property, const QVariant& value); Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 87e0cc4d71..e018e7bf31 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -768,6 +768,8 @@ QObject *ObjectNodeInstance::createComponent(const QString &componentPath, QQmlC qWarning() << error; } + object->setProperty("__designer_url__", QUrl::fromLocalFile(componentPath)); + return object; } diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 37f9712678..cd3ddef34d 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -47,6 +47,8 @@ Item { width: itemLibraryIconWidth // to be set in Qml context height: itemLibraryIconHeight // to be set in Qml context source: itemLibraryIconPath // to be set by model + + cache: false // Allow thumbnail to be dynamically updated } Text { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml index 4eb7420d74..5434e4705b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml @@ -100,14 +100,17 @@ Section { } Label { - text: "State" + text: qsTr("State") } SecondColumnLayout { - LineEdit { + + ComboBox { + Layout.fillWidth: true backendValue: backendValues.state - showTranslateCheckBox: false - enabled: anchorBackend.hasParent || isBaseState + model: allStateNames + valueType: ComboBox.String } + ExpandingSpacer { } } @@ -158,5 +161,56 @@ Section { } } + Label { + text: qsTr("Focus") + tooltip: qsTr("Holds whether the item has focus within the enclosing FocusScope.") + disabledState: !backendValues.focus.isAvailable + } + SecondColumnLayout { + CheckBox { + backendValue: backendValues.focus + text: backendValues.focus.valueToString + enabled: backendValues.focus.isAvailable + implicitWidth: 180 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Active focus on tab") + tooltip: qsTr("Holds whether the item wants to be in the tab focus chain.") + disabledState: !backendValues.activeFocusOnTab.isAvailable + } + SecondColumnLayout { + CheckBox { + backendValue: backendValues.activeFocusOnTab + text: backendValues.activeFocusOnTab.valueToString + enabled: backendValues.activeFocusOnTab.isAvailable + implicitWidth: 180 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Baseline offset") + tooltip: qsTr("Specifies the position of the item's baseline in local coordinates.") + disabledState: !backendValues.baselineOffset.isAvailable + } + SecondColumnLayout { + SpinBox { + sliderIndicatorVisible: true + backendValue: backendValues.baselineOffset + hasSlider: true + decimals: 0 + minimumValue: -1000 + maximumValue: 1000 + Layout.preferredWidth: 140 + enabled: backendValues.baselineOffset.isAvailable + } + ExpandingSpacer { + } + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml new file mode 100644 index 0000000000..787e390cbd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.1 +import HelperWidgets 2.0 +import QtQuick.Layouts 1.0 + +Column { + anchors.left: parent.left + anchors.right: parent.right + + ImageSpecifics { + } + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Animated Image") + + SectionLayout { + Label { + text: qsTr("Speed") + disabledState: !backendValues.speed.isAvailable + } + SecondColumnLayout { + SpinBox { + sliderIndicatorVisible: true + backendValue: backendValues.speed + hasSlider: true + decimals: 2 + minimumValue: 0 + maximumValue: 100 + Layout.preferredWidth: 140 + enabled: backendValues.speed.isAvailable + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Paused") + tooltip: qsTr("Holds whether the animated image is paused.") + disabledState: !backendValues.paused.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.paused.isAvailable + text: backendValues.paused.valueToString + backendValue: backendValues.paused + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Playing") + tooltip: qsTr("Holds whether the animated image is playing.") + disabledState: !backendValues.playing.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.playing.isAvailable + text: backendValues.playing.valueToString + backendValue: backendValues.playing + implicitWidth: 180 + } + ExpandingSpacer {} + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml index 9641d74b1b..abf69756fa 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml @@ -26,6 +26,8 @@ import HelperWidgets 2.0 import QtQuick 2.1 import QtQuick.Layouts 1.1 +import StudioTheme 1.0 as StudioTheme + Section { id: section caption: qsTr("Animation") @@ -33,6 +35,7 @@ Section { anchors.right: parent.right property bool showDuration: true + property bool showEasingCurve: false SectionLayout { Label { @@ -100,5 +103,22 @@ Section { text: backendValues.alwaysRunToEnd.valueToString backendValue: backendValues.alwaysRunToEnd } + + Label { + visible: section.showEasingCurve + text: qsTr("Easing Curve") + tooltip: qsTr("Define custom easing curve") + } + + BoolButtonRowButton { + visible: section.showEasingCurve + buttonIcon: StudioTheme.Constants.curveDesigner + EasingCurveEditor { + id: easingCurveEditor + modelNodeBackendProperty: modelNodeBackend + } + onClicked: easingCurveEditor.runDialog() + + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml index 3d39d44e8f..362ba144c5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml @@ -124,7 +124,7 @@ Column { } Label { - text: qsTr("Horizontal Fill mode") + text: qsTr("Horizontal Tile mode") } SecondColumnLayout { @@ -138,7 +138,7 @@ Column { } Label { - text: qsTr("Vertical Fill mode") + text: qsTr("Vertical Tile mode") } SecondColumnLayout { @@ -186,6 +186,70 @@ Column { } } + + Label { + text: qsTr("Mirror") + tooltip: qsTr("Specifies whether the image should be horizontally inverted.") + disabledState: !backendValues.mirror.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mirror.isAvailable + text: backendValues.mirror.valueToString + backendValue: backendValues.mirror + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Smooth") + tooltip: qsTr("Specifies whether the image is smoothly filtered when scaled or transformed.") + disabledState: !backendValues.smooth.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.smooth.isAvailable + text: backendValues.smooth.valueToString + backendValue: backendValues.smooth + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Cache") + tooltip: qsTr("Specifies whether the image should be cached.") + disabledState: !backendValues.cache.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.cache.isAvailable + text: backendValues.cache.valueToString + backendValue: backendValues.cache + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Asynchronous") + tooltip: qsTr("Specifies that images on the local filesystem should be loaded asynchronously in a separate thread.") + disabledState: !backendValues.asynchronous.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.asynchronous.isAvailable + text: backendValues.asynchronous.valueToString + backendValue: backendValues.asynchronous + implicitWidth: 180 + } + ExpandingSpacer {} + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColorAnimationSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColorAnimationSpecifics.qml index 26a38d4211..398515b7e0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColorAnimationSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColorAnimationSpecifics.qml @@ -57,6 +57,7 @@ Column { } AnimationSection { + showEasingCurve: true } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml index 37be5e5770..284849a415 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml @@ -95,7 +95,32 @@ Column { Layout.fillWidth: true scope: "Qt" } + } + Label { + text: qsTr("Horizontal item alignment") + } + + SecondColumnLayout { + ComboBox { + model: ["AlignLeft", "AlignRight" ,"AlignHCenter"] + backendValue: backendValues.horizontalItemAlignment + Layout.fillWidth: true + scope: "Grid" + } + } + + Label { + text: qsTr("Vertical item alignment") + } + + SecondColumnLayout { + ComboBox { + model: ["AlignTop", "AlignBottom" ,"AlignVCenter"] + backendValue: backendValues.verticalItemAlignment + Layout.fillWidth: true + scope: "Grid" + } } Label { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml index 8f742f06e2..60b97a1179 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml @@ -58,7 +58,7 @@ Column { SecondColumnLayout { ComboBox { scope: "Image" - model: ["Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically", "TileHorizontally"] + model: ["Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically", "TileHorizontally", "Pad"] backendValue: backendValues.fillMode implicitWidth: 180 Layout.fillWidth: true @@ -105,6 +105,136 @@ Column { ExpandingSpacer { } } + + Label { + text: qsTr("Horizontal alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignLeft", "AlignRight", "AlignHCenter"] + backendValue: backendValues.horizontalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Vertical alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignTop", "AlignBottom", "AlignVCenter"] + backendValue: backendValues.verticalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Asynchronous") + tooltip: qsTr("Specifies that images on the local filesystem should be loaded asynchronously in a separate thread.") + disabledState: !backendValues.asynchronous.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.asynchronous.isAvailable + text: backendValues.asynchronous.valueToString + backendValue: backendValues.asynchronous + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Auto transform") + tooltip: qsTr("Specifies whether the image should automatically apply image transformation metadata such as EXIF orientation.") + disabledState: !backendValues.autoTransform.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.autoTransform.isAvailable + text: backendValues.autoTransform.valueToString + backendValue: backendValues.autoTransform + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Cache") + tooltip: qsTr("Specifies whether the image should be cached.") + disabledState: !backendValues.cache.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.cache.isAvailable + text: backendValues.cache.valueToString + backendValue: backendValues.cache + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mipmap") + tooltip: qsTr("Specifies whether the image uses mipmap filtering when scaled or transformed.") + disabledState: !backendValues.mipmap.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mipmap.isAvailable + text: backendValues.mipmap.valueToString + backendValue: backendValues.mipmap + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mirror") + tooltip: qsTr("Specifies whether the image should be horizontally inverted.") + disabledState: !backendValues.mirror.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mirror.isAvailable + text: backendValues.mirror.valueToString + backendValue: backendValues.mirror + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Smooth") + tooltip: qsTr("Specifies whether the image is smoothly filtered when scaled or transformed.") + disabledState: !backendValues.smooth.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.smooth.isAvailable + text: backendValues.smooth.valueToString + backendValue: backendValues.smooth + implicitWidth: 180 + } + ExpandingSpacer {} + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml index 2a05bb6622..41abe776ce 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml @@ -325,13 +325,11 @@ Rectangle { frameVisible: false id: tabView - height: Math.max(layoutSectionHeight, specficsHeight) + height: Math.max(layoutSectionHeight, specficsHeight, advancedHeight) + extraHeight - property int layoutSectionHeight: 400 - property int specficsOneHeight: 0 - property int specficsTwoHeight: 0 - - property int specficsHeight: Math.max(specficsOneHeight, specficsTwoHeight) + property int advancedHeight: 0 + property int layoutSectionHeight: 0 + property int specficsHeight: 0 property int extraHeight: 40 @@ -341,6 +339,9 @@ Rectangle { component: Column { anchors.left: parent.left anchors.right: parent.right + + onImplicitHeightChanged: tabView.specficsHeight = implicitHeight + Loader { anchors.left: parent.left anchors.right: parent.right @@ -355,13 +356,6 @@ Rectangle { active = false active = true } - - property int loaderHeight: specificsTwo.item.height + tabView.extraHeight - onLoaderHeightChanged: tabView.specficsTwoHeight = loaderHeight - - onLoaded: { - tabView.specficsTwoHeight = loaderHeight - } } Loader { @@ -387,11 +381,9 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right + onImplicitHeightChanged: tabView.layoutSectionHeight = implicitHeight + LayoutSection { - property int childRectHeight: childrenRect.height - onChildRectHeightChanged: { - tabView.layoutSectionHeight = childRectHeight + tabView.extraHeight - } } MarginSection { @@ -720,6 +712,9 @@ Rectangle { component: Column { anchors.left: parent.left anchors.right: parent.right + + onImplicitHeightChanged: tabView.advancedHeight = implicitHeight + AdvancedSection { } LayerSection { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml index 71ff0260ba..0ca7f3d888 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml @@ -54,7 +54,7 @@ Column { } Label { - text: qsTr("Hover Enabled") + text: qsTr("Hover enabled") tooltip: qsTr("This property holds whether hover events are handled.") } @@ -68,6 +68,197 @@ Column { ExpandingSpacer { } } + + Label { + text: qsTr("Accepted buttons") + tooltip: qsTr("This property holds the mouse buttons that the mouse area reacts to.") + } + + SecondColumnLayout { + ComboBox { + backendValue: backendValues.acceptedButtons + model: ["LeftButton", "RightButton", "MiddleButton", "BackButton", "ForwardButton", "AllButtons"] + Layout.fillWidth: true + scope: "Qt" + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Press and hold interval") + tooltip: qsTr("This property overrides the elapsed time in milliseconds before pressAndHold is emitted.") + } + + SecondColumnLayout { + SpinBox { + backendValue: backendValues.pressAndHoldInterval + minimumValue: 0 + maximumValue: 2000 + decimals: 0 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Scroll gesture enabled") + tooltip: qsTr("This property controls whether this MouseArea responds to scroll gestures from non-mouse devices.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.scrollGestureEnabled + text: backendValues.scrollGestureEnabled.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Cursor shape") + tooltip: qsTr("This property holds the cursor shape for this mouse area.") + } + + SecondColumnLayout { + ComboBox { + backendValue: backendValues.cursorShape + model: ["ArrowCursor", "UpArrowCursor", "CrossCursor", "WaitCursor", + "IBeamCursor", "SizeVerCursor", "SizeHorCursor", "SizeBDiagCursor", + "SizeFDiagCursor", "SizeAllCursor", "BlankCursor", "SplitVCursor", + "SplitHCursor", "PointingHandCursor", "ForbiddenCursor", "WhatsThisCursor", + "BusyCursor", "OpenHandCursor", "ClosedHandCursor", "DragCopyCursor", + "DragMoveCursor", "DragLinkCursor"] + Layout.fillWidth: true + scope: "Qt" + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Prevent stealing") + tooltip: qsTr("This property controls whether the mouse events may be stolen from this MouseArea.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.preventStealing + text: backendValues.preventStealing.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Propagate composed events") + tooltip: qsTr("This property controls whether composed mouse events will automatically propagate to other MouseAreas.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.propagateComposedEvents + text: backendValues.propagateComposedEvents.valueToString + } + + ExpandingSpacer { + } + } + } + } + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Drag") + + SectionLayout { + Label { + text: qsTr("Target") + tooltip: qsTr("Sets the id of the item to drag.") + } + SecondColumnLayout { + ItemFilterComboBox { + typeFilter: "QtQuick.QtObject" + validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ } + backendValue: backendValues.drag_target + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Axis") + tooltip: qsTr("Specifies whether dragging can be done horizontally, vertically, or both.") + } + SecondColumnLayout { + ComboBox { + scope: "Drag" + model: ["XAxis", "YAxis", "XAndYAxis"] + backendValue: backendValues.drag_axis + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Filter children") + tooltip: qsTr("Specifies whether a drag overrides descendant MouseAreas.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.drag_filterChildren + text: backendValues.drag_filterChildren.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Threshold") + tooltip: qsTr("Determines the threshold in pixels of when the drag operation should start.") + } + + SecondColumnLayout { + SpinBox { + backendValue: backendValues.drag_threshold + minimumValue: 0 + maximumValue: 5000 + decimals: 0 + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Smoothed") + tooltip: qsTr("If set to true, the target will be moved only after the drag operation has started.\n" + + "If set to false, the target will be moved straight to the current mouse position.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.drag_smoothed + text: backendValues.drag_smoothed.valueToString + } + + ExpandingSpacer { + } + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml index 19f159526b..f6cbab67db 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/NumberAnimationSpecifics.qml @@ -79,6 +79,7 @@ Column { } AnimationSection { + showEasingCurve: true } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PropertyAnimationSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PropertyAnimationSpecifics.qml index 271f6bae67..ccb48c084f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PropertyAnimationSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PropertyAnimationSpecifics.qml @@ -35,6 +35,7 @@ Column { } AnimationSection { + showEasingCurve: true } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml index cfd972b085..5c0e84d039 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml @@ -114,6 +114,19 @@ Section { } Label { + visible: textInputSection.isTextInput + text: qsTr("Maximum length") + tooltip: qsTr("Sets the maximum permitted length of the text in the TextInput.") + } + SpinBox { + visible: textInputSection.isTextInput + Layout.fillWidth: true + backendValue: backendValues.maximumLength + minimumValue: 0 + maximumValue: 32767 + } + + Label { text: qsTr("Flags") Layout.alignment: Qt.AlignTop } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 2d70e40b1d..bb44927318 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -163,4 +163,9 @@ StudioControls.ComboBox { colorLogic.invalidate() comboBox.__isCompleted = true } + + Connections { + target: modelNodeBackend + onSelectionToBeChanged: comboBox.popup.close() + } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml index 2b97df0646..11f46d29c5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml @@ -47,7 +47,7 @@ Section { SecondColumnLayout { ComboBox { backendValue: backendValues.flickableDirection - model: ["AutoFlickDirection", "HorizontalFlick", "VerticalFlick", "HorizontalAndVerticalFlick"] + model: ["AutoFlickDirection", "AutoFlickIfNeeded", "HorizontalFlick", "VerticalFlick", "HorizontalAndVerticalFlick"] Layout.fillWidth: true scope: "Flickable" } @@ -63,7 +63,7 @@ Section { SecondColumnLayout { ComboBox { backendValue: backendValues.boundsBehavior - model: ["StopAtBounds", "DragOverBounds", "DragAndOvershootBounds"] + model: ["StopAtBounds", "DragOverBounds", "OvershootBounds", "DragAndOvershootBounds"] Layout.fillWidth: true scope: "Flickable" } @@ -167,6 +167,23 @@ Section { } Label { + text: qsTr("Synchronous drag") + tooltip: qsTr("If set to true, then when the mouse or touchpoint moves far enough to begin dragging\n" + + "the content, the content will jump, such that the content pixel which was under the\n" + + "cursor or touchpoint when pressed remains under that point.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.synchronousDrag + text: backendValues.synchronousDrag.valueToString + } + ExpandingSpacer { + } + } + + Label { text: qsTr("Content size") } @@ -247,6 +264,45 @@ Section { } Label { + text: qsTr("Origin") + } + + SecondColumnLayout { + Label { + text: "X" + width: root.labelWidth + } + + SpinBox { + backendValue: backendValues.originX + minimumValue: -8000 + maximumValue: 8000 + implicitWidth: root.spinBoxWidth + Layout.fillWidth: true + } + + Item { + width: 4 + height: 4 + } + + Label { + text: "Y" + width: root.labelWidth + } + + SpinBox { + backendValue: backendValues.originY + minimumValue: -8000 + maximumValue: 8000 + implicitWidth: root.spinBoxWidth + Layout.fillWidth: true + } + ExpandingSpacer { + } + } + + Label { text: qsTr("Margins") } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml index a97923a032..8f1c130089 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml @@ -68,9 +68,12 @@ Section { text: qsTr("Font") } FontComboBox { + id: fontComboBox backendValue: fontSection.fontFamily Layout.fillWidth: true width: 160 + property string familyName: backendValue.value + onFamilyNameChanged: print(styleNamesForFamily(familyName)) } Label { @@ -151,6 +154,7 @@ Section { italic: fontSection.italicStyle underline: fontSection.underlineStyle strikeout: fontSection.strikeoutStyle + enabled: !styleComboBox.styleSet } Label { @@ -175,6 +179,21 @@ Section { backendValue: getBackendValue("weight") model: ["Normal", "Light", "ExtraLight", "Thin", "Medium", "DemiBold", "Bold", "ExtraBold", "Black"] scope: "Font" + enabled: !styleComboBox.styleSet + } + + Label { + text: qsTr("Style name") + toolTip: qsTr("Sets the font's style.") + } + + ComboBox { + id: styleComboBox + property bool styleSet: backendValue.isInModel + Layout.fillWidth: true + backendValue: getBackendValue("styleName") + model: styleNamesForFamily(fontComboBox.familyName) + valueType: ComboBox.String } Label { @@ -257,5 +276,17 @@ Section { "Latin script,\n it is merely a cosmetic feature. Setting the preferShaping property to false will disable all such features\nwhen they are not required, which will improve performance in most cases.") } } + + Label { + text: qsTr("Hinting preference") + toolTip: qsTr("Sets the preferred hinting on the text.") + } + + ComboBox { + Layout.fillWidth: true + backendValue: getBackendValue("hintingPreference") + model: ["PreferDefaultHinting", "PreferNoHinting", "PreferVerticalHinting", "PreferFullHinting"] + scope: "Font" + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml index 6ecc8d525c..6c5e6fde86 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml @@ -61,7 +61,7 @@ Section { Layout.fillWidth: true backendValue: backendValues.wrapMode scope: "Text" - model: ["NoWrap", "WordWrap", "WrapAnywhere", "WrapAtWordBoundaryOrAnywhere"] + model: ["NoWrap", "WordWrap", "WrapAnywhere", "Wrap"] } Label { @@ -78,6 +78,21 @@ Section { } Label { + visible: showElide + text: qsTr("Maximum line count") + tooltip: qsTr("Limits the number of lines that the text item will show.") + } + + SpinBox { + visible: showElide + Layout.fillWidth: true + backendValue: backendValues.maximumLineCount + minimumValue: 0 + maximumValue: 10000 + decimals: 0 + } + + Label { text: qsTr("Alignment") } @@ -124,6 +139,7 @@ Section { toolTip: qsTr("Specifies how the font size of the displayed text is determined.") } ComboBox { + id: fontSizeMode visible: showFontSizeMode scope: "Text" model: ["FixedSize", "HorizontalFit", "VerticalFit", "Fit"] @@ -131,6 +147,48 @@ Section { Layout.fillWidth: true } + Label { + visible: showFontSizeMode + text: qsTr("Minimum size") + } + SecondColumnLayout { + visible: showFontSizeMode + + SpinBox { + enabled: fontSizeMode.currentIndex !== 0 + minimumValue: 0 + maximumValue: 500 + decimals: 0 + backendValue: backendValues.minimumPixelSize + Layout.fillWidth: true + Layout.minimumWidth: 60 + } + Label { + text: qsTr("Pixel") + tooltip: qsTr("Specifies the minimum font pixel size of scaled text.") + width: 42 + } + + Item { + width: 4 + height: 4 + } + + SpinBox { + enabled: fontSizeMode.currentIndex !== 0 + minimumValue: 0 + maximumValue: 500 + decimals: 0 + backendValue: backendValues.minimumPointSize + Layout.fillWidth: true + Layout.minimumWidth: 60 + } + Label { + text: qsTr("Point") + tooltip: qsTr("Specifies the minimum font point size of scaled text.") + width: 42 + } + } Label { visible: showLineHeight @@ -148,5 +206,17 @@ Section { stepSize: 0.1 } + Label { + visible: showLineHeight + text: qsTr("Line height mode") + toolTip: qsTr("Determines how the line height is specified.") + } + ComboBox { + visible: showLineHeight + scope: "Text" + model: ["ProportionalHeight", "FixedHeight"] + backendValue: backendValues.lineHeightMode + Layout.fillWidth: true + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml index 42d7b3eda8..a9b0a2b81c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml @@ -36,9 +36,11 @@ ButtonRow { property alias checked: myAbstractButton.checked signal onCheckedChanged() + signal clicked AbstractButton { id: myAbstractButton onCheckedChanged: myButtonRow.onCheckedChanged() + onClicked: myButtonRow.clicked() } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml index 3d692af671..74edf9ccee 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml @@ -26,98 +26,4 @@ pragma Singleton import QtQuick 2.10 -QtObject { - readonly property int width: 1920 - readonly property int height: 1080 - readonly property FontLoader mySystemFont: FontLoader { - name: "Arial" - } - readonly property FontLoader controlIcons: FontLoader { - source: "icons.ttf" - } - - readonly property string actionIcon: "\u0021" - readonly property string actionIconBinding: "\u0022" - readonly property string addColumnAfter: "\u0023" - readonly property string addColumnBefore: "\u0024" - readonly property string addFile: "\u0025" - readonly property string addRowAfter: "\u0026" - readonly property string addRowBefore: "\u0027" - readonly property string addTable: "\u0028" - readonly property string alignBottom: "\u0029" - readonly property string alignCenterHorizontal: "\u002A" - readonly property string alignCenterVertical: "\u002B" - readonly property string alignLeft: "\u002C" - readonly property string alignRight: "\u002D" - readonly property string alignTo: "\u002E" - readonly property string alignTop: "\u002F" - readonly property string anchorBaseline: "\u0030" - readonly property string anchorBottom: "\u0031" - readonly property string anchorFill: "\u0032" - readonly property string anchorLeft: "\u0033" - readonly property string anchorRight: "\u0034" - readonly property string anchorTop: "\u0035" - readonly property string annotationBubble: "\u0036" - readonly property string annotationDecal: "\u0037" - readonly property string centerHorizontal: "\u0038" - readonly property string centerVertical: "\u0039" - readonly property string closeCross: "\u003A" - readonly property string deleteColumn: "\u003B" - readonly property string deleteRow: "\u003C" - readonly property string deleteTable: "\u003D" - readonly property string distributeBottom: "\u003E" - readonly property string distributeCenterHorizontal: "\u003F" - readonly property string distributeCenterVertical: "\u0040" - readonly property string distributeLeft: "\u0041" - readonly property string distributeOriginBottomRight: "\u0042" - readonly property string distributeOriginCenter: "\u0043" - readonly property string distributeOriginNone: "\u0044" - readonly property string distributeOriginTopLeft: "\u0045" - readonly property string distributeRight: "\u0046" - readonly property string distributeSpacingHorizontal: "\u0047" - readonly property string distributeSpacingVertical: "\u0048" - readonly property string distributeTop: "\u0049" - readonly property string edit: "\u004A" - readonly property string fontStyleBold: "\u004B" - readonly property string fontStyleItalic: "\u004C" - readonly property string fontStyleStrikethrough: "\u004D" - readonly property string fontStyleUnderline: "\u004E" - readonly property string mergeCells: "\u004F" - readonly property string redo: "\u0050" - readonly property string splitColumns: "\u0051" - readonly property string splitRows: "\u0052" - readonly property string testIcon: "\u0053" - readonly property string textAlignBottom: "\u0054" - readonly property string textAlignCenter: "\u0055" - readonly property string textAlignLeft: "\u0056" - readonly property string textAlignMiddle: "\u0057" - readonly property string textAlignRight: "\u0058" - readonly property string textAlignTop: "\u0059" - readonly property string textBulletList: "\u005A" - readonly property string textFullJustification: "\u005B" - readonly property string textNumberedList: "\u005C" - readonly property string tickIcon: "\u005D" - readonly property string triState: "\u005E" - readonly property string undo: "\u005F" - readonly property string upDownIcon: "\u0060" - readonly property string upDownSquare2: "\u0061" - - readonly property font iconFont: Qt.font({ - "family": controlIcons.name, - "pixelSize": 12 - }) - - readonly property font font: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize - }) - - readonly property font largeFont: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize * 1.6 - }) - - readonly property color backgroundColor: "#c2c2c2" - - readonly property bool showActionIndicatorBackground: false -} +InternalConstants {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml new file mode 100644 index 0000000000..c326cc08f1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.10 + +QtObject { + readonly property int width: 1920 + readonly property int height: 1080 + readonly property FontLoader mySystemFont: FontLoader { + name: "Arial" + } + readonly property FontLoader controlIcons: FontLoader { + source: "icons.ttf" + } + + objectName: "internalConstantsObject" + + readonly property string actionIcon: "\u0021" + readonly property string actionIconBinding: "\u0022" + readonly property string addColumnAfter: "\u0023" + readonly property string addColumnBefore: "\u0024" + readonly property string addFile: "\u0025" + readonly property string addRowAfter: "\u0026" + readonly property string addRowBefore: "\u0027" + readonly property string addTable: "\u0028" + readonly property string adsClose: "\u0029" + readonly property string adsDetach: "\u002A" + readonly property string adsDropDown: "\u002B" + readonly property string aliasAnimated: "\u002C" + readonly property string aliasProperty: "\u002D" + readonly property string alignBottom: "\u002E" + readonly property string alignCenterHorizontal: "\u002F" + readonly property string alignCenterVertical: "\u0030" + readonly property string alignLeft: "\u0031" + readonly property string alignRight: "\u0032" + readonly property string alignTo: "\u0033" + readonly property string alignTop: "\u0034" + readonly property string anchorBaseline: "\u0035" + readonly property string anchorBottom: "\u0036" + readonly property string anchorFill: "\u0037" + readonly property string anchorLeft: "\u0038" + readonly property string anchorRight: "\u0039" + readonly property string anchorTop: "\u003A" + readonly property string animatedProperty: "\u003B" + readonly property string annotationBubble: "\u003C" + readonly property string annotationDecal: "\u003D" + readonly property string assign: "\u003E" + readonly property string centerHorizontal: "\u003F" + readonly property string centerVertical: "\u0040" + readonly property string closeCross: "\u0041" + readonly property string curveDesigner: "\u0042" + readonly property string curveEditor: "\u0043" + readonly property string decisionNode: "\u0044" + readonly property string deleteColumn: "\u0045" + readonly property string deleteRow: "\u0046" + readonly property string deleteTable: "\u0047" + readonly property string detach: "\u0048" + readonly property string distributeBottom: "\u0049" + readonly property string distributeCenterHorizontal: "\u004A" + readonly property string distributeCenterVertical: "\u004B" + readonly property string distributeLeft: "\u004C" + readonly property string distributeOriginBottomRight: "\u004D" + readonly property string distributeOriginCenter: "\u004E" + readonly property string distributeOriginNone: "\u004F" + readonly property string distributeOriginTopLeft: "\u0050" + readonly property string distributeRight: "\u0051" + readonly property string distributeSpacingHorizontal: "\u0052" + readonly property string distributeSpacingVertical: "\u0053" + readonly property string distributeTop: "\u0054" + readonly property string edit: "\u0055" + readonly property string fontStyleBold: "\u0056" + readonly property string fontStyleItalic: "\u0057" + readonly property string fontStyleStrikethrough: "\u0058" + readonly property string fontStyleUnderline: "\u0059" + readonly property string mergeCells: "\u005A" + readonly property string redo: "\u005B" + readonly property string splitColumns: "\u005C" + readonly property string splitRows: "\u005D" + readonly property string startNode: "\u005E" + readonly property string testIcon: "\u005F" + readonly property string textAlignBottom: "\u0060" + readonly property string textAlignCenter: "\u0061" + readonly property string textAlignLeft: "\u0062" + readonly property string textAlignMiddle: "\u0063" + readonly property string textAlignRight: "\u0064" + readonly property string textAlignTop: "\u0065" + readonly property string textBulletList: "\u0066" + readonly property string textFullJustification: "\u0067" + readonly property string textNumberedList: "\u0068" + readonly property string tickIcon: "\u0069" + readonly property string triState: "\u006A" + readonly property string undo: "\u006B" + readonly property string upDownIcon: "\u006C" + readonly property string upDownSquare2: "\u006D" + readonly property string wildcard: "\u006E" + + readonly property font iconFont: Qt.font({ + "family": controlIcons.name, + "pixelSize": 12 + }) + + readonly property font font: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize + }) + + readonly property font largeFont: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize * 1.6 + }) + + readonly property color backgroundColor: "#c2c2c2" + + readonly property bool showActionIndicatorBackground: false +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf Binary files differindex d65b065195..47becb6873 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index b768fe63a2..4f689f9f63 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -1,2 +1,4 @@ singleton Values 1.0 Values.qml singleton Constants 1.0 Constants.qml +InternalConstants 1.0 InternalConstants.qml + diff --git a/src/libs/advanceddockingsystem/ads_globals.h b/src/libs/advanceddockingsystem/ads_globals.h index 4c0b8d6896..e61bfa7ceb 100644 --- a/src/libs/advanceddockingsystem/ads_globals.h +++ b/src/libs/advanceddockingsystem/ads_globals.h @@ -107,10 +107,11 @@ enum eDragState { * The different icons used in the UI */ enum eIcon { - TabCloseIcon, //!< TabCloseIcon - DockAreaMenuIcon, //!< DockAreaMenuIcon - DockAreaUndockIcon, //!< DockAreaUndockIcon - DockAreaCloseIcon, //!< DockAreaCloseIcon + TabCloseIcon, //!< TabCloseIcon + DockAreaMenuIcon, //!< DockAreaMenuIcon + DockAreaUndockIcon, //!< DockAreaUndockIcon + DockAreaCloseIcon, //!< DockAreaCloseIcon + FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon IconCount, //!< just a delimiter for range checks }; diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 9ee647265d..79ba7ae70d 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -132,6 +132,7 @@ namespace ADS void DockAreaTitleBarPrivate::createButtons() { + const QSize iconSize(14, 14); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Tabs menu button m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton)); @@ -149,6 +150,7 @@ namespace ADS m_tabsMenuButton->setMenu(tabsMenu); internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs")); m_tabsMenuButton->setSizePolicy(sizePolicy); + m_tabsMenuButton->setIconSize(iconSize); m_layout->addWidget(m_tabsMenuButton, 0); QObject::connect(m_tabsMenuButton->menu(), &QMenu::triggered, @@ -164,6 +166,7 @@ namespace ADS QStyle::SP_TitleBarNormalButton, ADS::DockAreaUndockIcon); m_undockButton->setSizePolicy(sizePolicy); + m_undockButton->setIconSize(iconSize); m_layout->addWidget(m_undockButton, 0); QObject::connect(m_undockButton, &QToolButton::clicked, @@ -183,7 +186,7 @@ namespace ADS internal::setToolTip(m_closeButton, QObject::tr("Close Group")); } m_closeButton->setSizePolicy(sizePolicy); - m_closeButton->setIconSize(QSize(16, 16)); + m_closeButton->setIconSize(iconSize); m_layout->addWidget(m_closeButton, 0); QObject::connect(m_closeButton, &QToolButton::clicked, diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index 611580d368..b46d172050 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -168,12 +168,18 @@ namespace ADS m_titleLabel->setText(m_dockWidget->windowTitle()); m_titleLabel->setObjectName("dockWidgetTabLabel"); m_titleLabel->setAlignment(Qt::AlignCenter); - QObject::connect(m_titleLabel, &ElidingLabel::elidedChanged, q, &DockWidgetTab::elidedChanged); + QObject::connect(m_titleLabel, + &ElidingLabel::elidedChanged, + q, + &DockWidgetTab::elidedChanged); m_closeButton = createCloseButton(); m_closeButton->setObjectName("tabCloseButton"); - internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + TabCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); q->onDockWidgetFeaturesChanged(); internal::setToolTip(m_closeButton, QObject::tr("Close Tab")); QObject::connect(m_closeButton, @@ -189,11 +195,11 @@ namespace ADS boxLayout->setContentsMargins(2 * spacing, 0, 0, 0); boxLayout->setSpacing(0); q->setLayout(boxLayout); - boxLayout->addWidget(m_titleLabel, 1); + boxLayout->addWidget(m_titleLabel, 1, Qt::AlignVCenter); boxLayout->addSpacing(spacing); - boxLayout->addWidget(m_closeButton); + boxLayout->addWidget(m_closeButton, 0, Qt::AlignVCenter); boxLayout->addSpacing(qRound(spacing * 4.0 / 3.0)); - boxLayout->setAlignment(Qt::AlignCenter); + boxLayout->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); m_titleLabel->setVisible(true); } diff --git a/src/libs/advanceddockingsystem/iconprovider.cpp b/src/libs/advanceddockingsystem/iconprovider.cpp index 6a6f46752b..f220418220 100644 --- a/src/libs/advanceddockingsystem/iconprovider.cpp +++ b/src/libs/advanceddockingsystem/iconprovider.cpp @@ -51,7 +51,7 @@ namespace ADS { */ IconProviderPrivate(IconProvider *parent); }; - // struct LedArrayPanelPrivate + // struct IconProviderPrivate IconProviderPrivate::IconProviderPrivate(IconProvider *parent) : q(parent) diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp index 64e71a3b1c..7d92952d70 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp @@ -41,7 +41,7 @@ namespace ADS { using TabLabelType = ElidingLabel; -using tCloseButton = QPushButton; +using CloseButtonType = QPushButton; /** * @brief Private data class of public interface CFloatingWidgetTitleBar @@ -52,7 +52,7 @@ public: FloatingWidgetTitleBar *q; ///< public interface class QLabel *m_iconLabel = nullptr; TabLabelType *m_titleLabel = nullptr; - tCloseButton *m_closeButton = nullptr; + CloseButtonType *m_closeButton = nullptr; FloatingDockContainer *m_floatingWidget = nullptr; eDragState m_dragState = DraggingInactive; @@ -74,22 +74,20 @@ void FloatingWidgetTitleBarPrivate::createLayout() m_titleLabel->setObjectName("floatingTitleLabel"); m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - m_closeButton = new tCloseButton(); + m_closeButton = new CloseButtonType(); m_closeButton->setObjectName("floatingTitleCloseButton"); m_closeButton->setFlat(true); - - // The standard icons do does not look good on high DPI screens - QIcon closeIcon; - QPixmap normalPixmap = q->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, - nullptr, - m_closeButton); - closeIcon.addPixmap(normalPixmap, QIcon::Normal); - closeIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + ADS::FloatingWidgetCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); m_closeButton->setVisible(true); m_closeButton->setFocusPolicy(Qt::NoFocus); - q->connect(m_closeButton, &QPushButton::clicked, q, &FloatingWidgetTitleBar::closeRequested); + QObject::connect(m_closeButton, + &QPushButton::clicked, + q, + &FloatingWidgetTitleBar::closeRequested); QFontMetrics fontMetrics(m_titleLabel->font()); int spacing = qRound(fontMetrics.height() / 4.0); diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index f91c24b52a..68d40accbc 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -28,6 +28,8 @@ #include "theme/theme.h" #include "hostosinfo.h" +#include <utils/qtcassert.h> + #include <QPixmapCache> #include <QPainter> #include <QApplication> @@ -35,6 +37,7 @@ #include <QCommonStyle> #include <QStyleOption> #include <QWindow> +#include <QFontDatabase> #include <qmath.h> // Clamps float color values within (0, 255) @@ -542,6 +545,48 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect) return grad; } +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color) +{ + QFontDatabase a; + + QTC_ASSERT(a.hasFamily(fontName), {}); + + if (a.hasFamily(fontName)) { + + QIcon icon; + QSize size(iconSize, iconSize); + + const int maxDpr = qRound(qApp->devicePixelRatio()); + for (int dpr = 1; dpr <= maxDpr; dpr++) { + QPixmap pixmap(size * dpr); + pixmap.setDevicePixelRatio(dpr); + pixmap.fill(Qt::transparent); + + QFont font(fontName); + font.setPixelSize(fontSize * dpr); + + QPainter painter(&pixmap); + painter.save(); + painter.setPen(color); + painter.setFont(font); + painter.drawText(QRectF(QPoint(0, 0), size), iconSymbol); + painter.restore(); + + icon.addPixmap(pixmap); + } + + return icon; + } + + return {}; +} + +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize) +{ + QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText); + return getIconFromIconFont(fontName, iconSymbol, fontSize, iconSize, penColor); +} + QString StyleHelper::dpiSpecificImageFile(const QString &fileName) { // See QIcon::addFile() diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 4bcec960ad..09f3253435 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -93,6 +93,9 @@ public: static void tintImage(QImage &img, const QColor &tintColor); static QLinearGradient statusBarGradient(const QRect &statusBarRect); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize); + static QString dpiSpecificImageFile(const QString &fileName); static QString imageFileWithResolution(const QString &fileName, int dpr); static QList<int> availableImageResolutions(const QString &fileName); diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 9c32da72c4..ad7ad2831f 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -247,6 +247,7 @@ extend_qtc_plugin(QmlDesigner snapper.cpp snapper.h snappinglinecreator.cpp snappinglinecreator.h toolbox.cpp toolbox.h + transitiontool.cpp transitiontool.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 2965bcd90b..e76b5c1ff7 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -39,8 +39,10 @@ const char qmlPreviewCategory[] = "QmlPreview"; const char editCategory[] = "Edit"; const char anchorsCategory[] = "Anchors"; const char positionCategory[] = "Position"; +const char groupCategory[] = "Group"; const char layoutCategory[] = "Layout"; const char flowCategory[] = "Flow"; +const char flowEffectCategory[] = "FlowEffect"; const char flowConnectionCategory[] = "FlowConnection"; const char stackedContainerCategory[] = "StackedContainer"; const char genericToolBarCategory[] = "GenericToolBar"; @@ -57,6 +59,8 @@ const char anchorsFillCommandId[] = "AnchorsFill"; const char anchorsResetCommandId[] = "AnchorsReset"; const char removePositionerCommandId[] = "RemovePositioner"; const char createFlowActionAreaCommandId[] = "CreateFlowActionArea"; +const char setFlowStartCommandId[] = "SetFlowStart"; +const char selectFlowEffectCommandId[] = "SelectFlowEffect"; const char layoutRowPositionerCommandId[] = "LayoutRowPositioner"; const char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner"; const char layoutGridPositionerCommandId[] = "LayoutGridPositioner"; @@ -76,15 +80,19 @@ const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedContainer"; const char flowAssignEffectCommandId[] = "AssignFlowEffect"; +const char addToGroupItemCommandId[] = "AddToGroupItem"; const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); +const char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select Effect"); const char stackCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stack (z)"); const char editCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit"); const char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Anchors"); const char positionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position"); +const char groupCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group"); const char layoutCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout"); const char flowCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow"); +const char flowEffectCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow Effects"); const char stackedContainerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stacked Container"); const char cutSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Cut"); @@ -124,8 +132,11 @@ const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerCon const char layoutFlowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position in Flow"); const char removePositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Positioner"); const char createFlowActionAreaDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Flow Action"); +const char setFlowStartDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Flow Start"); const char removeLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Layout"); +const char addToGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group in GroupItem"); + const char addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Item"); const char addTabBarToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Tab Bar"); const char increaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Increase Index"); @@ -165,6 +176,7 @@ const int priorityStackCategory = 180; const int priorityEditCategory = 160; const int priorityAnchorsCategory = 140; const int priorityFlowCategory = 240; +const int priorityGroupCategory = 140; const int priorityPositionCategory = 130; const int priorityLayoutCategory = 120; const int priorityStackedContainerCategory = priorityLayoutCategory; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 42f95952db..07246f175c 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -346,12 +346,28 @@ bool isFlowItem(const SelectionContext &context) && QmlFlowItemNode::isValidQmlFlowItemNode(context.currentSingleSelectedNode()); } +bool isFlowTarget(const SelectionContext &context) +{ + return context.singleNodeIsSelected() + && QmlFlowTargetNode::isFlowEditorTarget(context.currentSingleSelectedNode()); +} + bool isFlowTransitionItem(const SelectionContext &context) { return context.singleNodeIsSelected() && QmlFlowItemNode::isFlowTransition(context.currentSingleSelectedNode()); } +bool isFlowTransitionItemWithEffect(const SelectionContext &context) +{ + if (!isFlowTransitionItem(context)) + return false; + + ModelNode node = context.currentSingleSelectedNode(); + + return node.hasNodeProperty("effect"); +} + bool isFlowActionItemItem(const SelectionContext &context) { const ModelNode selectedNode = context.currentSingleSelectedNode(); @@ -362,9 +378,9 @@ bool isFlowActionItemItem(const SelectionContext &context) || QmlVisualNode::isFlowWildcard(selectedNode)); } -bool isFlowItemOrTransition(const SelectionContext &context) +bool isFlowTargetOrTransition(const SelectionContext &context) { - return isFlowItem(context) || isFlowTransitionItem(context); + return isFlowTarget(context) || isFlowTransitionItem(context); } class FlowActionConnectAction : public ActionGroup @@ -642,6 +658,12 @@ bool positionOptionVisible(const SelectionContext &context) || isPositioner(context); } +bool studioComponentsAvailable(const SelectionContext &context) +{ + const Import import = Import::createLibraryImport("QtQuick.Studio.Components", "1.0"); + return context.view()->model()->isImportPossible(import, true, true); +} + bool singleSelectedAndUiFile(const SelectionContext &context) { if (!singleSelection(context)) @@ -853,15 +875,31 @@ void DesignerActionManager::createDefaultDesignerActions() priorityLayoutCategory, &layoutOptionVisible)); - //isFlowTransitionItem + addDesignerAction(new ActionGroup( + groupCategoryDisplayName, + groupCategory, + priorityGroupCategory, + &positionOptionVisible, + &studioComponentsAvailable)); addDesignerAction(new ActionGroup( flowCategoryDisplayName, flowCategory, priorityFlowCategory, - &isFlowItemOrTransition, + &isFlowTargetOrTransition, &flowOptionVisible)); + + auto effectMenu = new ActionGroup( + flowEffectCategoryDisplayName, + flowEffectCategory, + priorityFlowCategory, + &isFlowTransitionItem, + &flowOptionVisible); + + effectMenu->setCategory(flowCategory); + addDesignerAction(effectMenu); + addDesignerAction(new ModelNodeFormEditorAction( createFlowActionAreaCommandId, createFlowActionAreaDisplayName, @@ -874,26 +912,41 @@ void DesignerActionManager::createDefaultDesignerActions() &isFlowItem, &flowOptionVisible)); + addDesignerAction(new ModelNodeContextMenuAction( + setFlowStartCommandId, + setFlowStartDisplayName, + {}, + flowCategory, + priorityFirst, + {}, + &setFlowStartItem, + &isFlowItem, + &flowOptionVisible)); + addDesignerAction(new FlowActionConnectAction( flowConnectionCategoryDisplayName, flowConnectionCategory, priorityFlowCategory)); - const QList<TypeName> types = {"FlowActionArea", - "FlowFadeEffect", - "FlowPushRightEffect", - "FlowPushLeftEffect", - "FlowPushUpEffect", - "FlowSlideDownEffect", - "FlowSlideLeftEffect", - "FlowSlideRightEffect", - "FlowSlideUpEffect", + const QList<TypeName> transitionTypes = {"FlowFadeEffect", + "FlowPushEffect", + "FlowMoveEffect", "None"}; - for (const TypeName &typeName : types) + for (const TypeName &typeName : transitionTypes) addTransitionEffectAction(typeName); + addDesignerAction(new ModelNodeContextMenuAction( + selectFlowEffectCommandId, + selectEffectDisplayName, + {}, + flowCategory, + {}, + priorityFlowCategory, + &selectFlowEffect, + &isFlowTransitionItemWithEffect)); + addDesignerAction(new ActionGroup( stackedContainerCategoryDisplayName, stackedContainerCategory, @@ -968,6 +1021,18 @@ void DesignerActionManager::createDefaultDesignerActions() &isLayout, &isLayout)); + addDesignerAction(new ModelNodeContextMenuAction( + addToGroupItemCommandId, + addToGroupItemDisplayName, + {}, + groupCategory, + QKeySequence(), + 110, + &addToGroupItem, + &selectionCanBeLayouted, + &selectionCanBeLayouted)); + + addDesignerAction(new ModelNodeFormEditorAction( addItemToStackedContainerCommandId, addItemToStackedContainerDisplayName, @@ -1175,7 +1240,7 @@ void DesignerActionManager::addTransitionEffectAction(const TypeName &typeName) QByteArray(ComponentCoreConstants::flowAssignEffectCommandId) + typeName, QLatin1String(ComponentCoreConstants::flowAssignEffectDisplayName) + typeName, {}, - ComponentCoreConstants::flowCategory, + ComponentCoreConstants::flowEffectCategory, {}, typeName == "None" ? 100 : 140, [typeName](const SelectionContext &context) @@ -1186,6 +1251,10 @@ void DesignerActionManager::addTransitionEffectAction(const TypeName &typeName) DesignerActionToolBar::DesignerActionToolBar(QWidget *parentWidget) : Utils::StyledBar(parentWidget), m_toolBar(new QToolBar("ActionToolBar", this)) { + QWidget* empty = new QWidget(); + empty->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); + m_toolBar->addWidget(empty); + m_toolBar->setContentsMargins(0, 0, 0, 0); m_toolBar->setFloatable(true); m_toolBar->setMovable(true); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp index 6876684504..8a6c06fcdc 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp @@ -25,6 +25,8 @@ #include "designeractionmanagerview.h" +#include <customnotifications.h> + #include <selectioncontext.h> #include <actioninterface.h> #include <variantproperty.h> @@ -53,7 +55,7 @@ void DesignerActionManagerView::modelAboutToBeDetached(Model *model) void DesignerActionManagerView::nodeCreated(const ModelNode &) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::NodeCreated); } void DesignerActionManagerView::nodeRemoved(const ModelNode &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags) @@ -63,17 +65,17 @@ void DesignerActionManagerView::nodeRemoved(const ModelNode &, const NodeAbstrac void DesignerActionManagerView::nodeAboutToBeReparented(const ModelNode &, const NodeAbstractProperty &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::NodeHierachy); } void DesignerActionManagerView::nodeReparented(const ModelNode &, const NodeAbstractProperty &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::NodeHierachy); } void DesignerActionManagerView::propertiesRemoved(const QList<AbstractProperty> &) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); } void DesignerActionManagerView::rootNodeTypeChanged(const QString &, int, int) @@ -112,7 +114,7 @@ void DesignerActionManagerView::selectedNodesChanged(const QList<ModelNode> &sel void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &, const ModelNode &, int) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::NodeHierachy); } void DesignerActionManagerView::importsChanged(const QList<Import> &, const QList<Import> &) @@ -122,27 +124,38 @@ void DesignerActionManagerView::importsChanged(const QList<Import> &, const QLis void DesignerActionManagerView::signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &, AbstractView::PropertyChangeFlags) { - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); } void DesignerActionManagerView::variantPropertiesChanged(const QList<VariantProperty> &, AbstractView::PropertyChangeFlags propertyChangeFlag) { if (propertyChangeFlag == AbstractView::PropertiesAdded) - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); else if (hasSingleSelectedModelNode()) - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); } void DesignerActionManagerView::bindingPropertiesChanged(const QList<BindingProperty> &, AbstractView::PropertyChangeFlags propertyChangeFlag) { if (propertyChangeFlag == AbstractView::PropertiesAdded) - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); } void DesignerActionManagerView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &) { if (hasSingleSelectedModelNode()) - setupContext(SelectionContext::UpdateMode::Fast); + setupContext(SelectionContext::UpdateMode::Properties); +} + +void DesignerActionManagerView::customNotification(const AbstractView * /*view*/, + const QString &identifier, + const QList<ModelNode> & /* nodeList */, + const QList<QVariant> & /*data */) +{ + if (identifier == StartRewriterAmend) + m_isInRewriterTransaction = true; + else if (identifier == EndRewriterAmend) + m_isInRewriterTransaction = false; } DesignerActionManager &DesignerActionManagerView::designerActionManager() diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h index 5eb0efe542..a7a34271ea 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h @@ -71,6 +71,10 @@ public: void emitSelectionChanged(); void setupContext(SelectionContext::UpdateMode updateMode = SelectionContext::UpdateMode::Normal); + void customNotification(const AbstractView *, + const QString &identifier, + const QList<ModelNode> &, + const QList<QVariant> &) override; signals: void selectionChanged(bool itemsSelected, bool rootItemIsSelected); diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index a1629c0b5b..e106fc9357 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -167,7 +167,6 @@ void LayoutInGridLayout::doIt() const TypeName layoutType = "QtQuick.Layouts.GridLayout"; if (!m_selectionContext.view() - || !m_selectionContext.hasSingleSelectedModelNode() || !m_selectionContext.view()->model()->hasNodeMetaInfo(layoutType)) return; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 67a53ef997..59029400e5 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -132,16 +132,22 @@ public: bool isVisible(const SelectionContext &m_selectionState) const override { return m_visibility(m_selectionState); } bool isEnabled(const SelectionContext &m_selectionState) const override { return m_enabled(m_selectionState); } - QByteArray category() const override { return QByteArray(); } + QByteArray category() const override { return m_category; } QByteArray menuId() const override { return m_menuId; } int priority() const override { return m_priority; } Type type() const override { return ContextMenu; } + void setCategory(const QByteArray &catageoryId) + { + m_category = catageoryId; + } + private: const QByteArray m_menuId; const int m_priority; SelectionContextPredicate m_enabled; SelectionContextPredicate m_visibility; + QByteArray m_category; }; class SeperatorDesignerAction : public AbstractAction diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 2c239a8aa9..00e2ac028a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -311,8 +311,10 @@ void resetSize(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetSize",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("width"); - itemNode.removeProperty("height"); + if (itemNode.isValid()) { + itemNode.removeProperty("width"); + itemNode.removeProperty("height"); + } } }); } @@ -325,8 +327,10 @@ void resetPosition(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetPosition",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("x"); - itemNode.removeProperty("y"); + if (itemNode.isValid()) { + itemNode.removeProperty("x"); + itemNode.removeProperty("y"); + } } }); } @@ -348,7 +352,8 @@ void resetZ(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetZ",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("z"); + if (itemNode.isValid()) + itemNode.removeProperty("z"); } }); } @@ -452,7 +457,6 @@ static void layoutHelperFunction(const SelectionContext &selectionContext, const LessThan &lessThan) { if (!selectionContext.view() - || !selectionContext.hasSingleSelectedModelNode() || !selectionContext.view()->model()->hasNodeMetaInfo(layoutType)) return; @@ -1095,7 +1099,122 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ container.nodeProperty("effect").reparentHere(effectNode); view->setSelectedModelNode(effectNode); } - }); + }); +} + +void setFlowStartItem(const SelectionContext &selectionContext) +{ + AbstractView *view = selectionContext.view(); + + QTC_ASSERT(view && selectionContext.hasSingleSelectedModelNode(), return); + ModelNode node = selectionContext.currentSingleSelectedNode(); + QTC_ASSERT(node.isValid(), return); + QTC_ASSERT(node.metaInfo().isValid(), return); + QmlFlowItemNode flowItem(node); + QTC_ASSERT(flowItem.isValid(), return); + QTC_ASSERT(flowItem.flowView().isValid(), return); + view->executeInTransaction("DesignerActionManager:setFlowStartItem", + [&flowItem](){ + flowItem.flowView().setStartFlowItem(flowItem); + }); +} + + +bool static hasStudioComponentsImport(const SelectionContext &context) +{ + if (context.view() && context.view()->model()) { + Import import = Import::createLibraryImport("QtQuick.Studio.Components", "1.0"); + return context.view()->model()->hasImport(import, true, true); + } + + return false; +} + +static inline void setAdjustedPos(const QmlDesigner::ModelNode &modelNode) +{ + if (modelNode.hasParentProperty()) { + ModelNode parentNode = modelNode.parentProperty().parentModelNode(); + + const QPointF instancePos = QmlItemNode(modelNode).instancePosition(); + const int x = instancePos.x() - parentNode.variantProperty("x").value().toInt(); + const int y = instancePos.y() - parentNode.variantProperty("y").value().toInt(); + + modelNode.variantProperty("x").setValue(x); + modelNode.variantProperty("y").setValue(y); + } +} + +void reparentToNodeAndAdjustPosition(const ModelNode &parentModelNode, + const QList<ModelNode> &modelNodeList) +{ + for (ModelNode modelNode : modelNodeList) { + reparentTo(modelNode, parentModelNode); + setAdjustedPos(modelNode); + + for (const VariantProperty &variantProperty : modelNode.variantProperties()) { + if (variantProperty.name().contains("anchors.")) + modelNode.removeProperty(variantProperty.name()); + } + for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) { + if (bindingProperty.name().contains("anchors.")) + modelNode.removeProperty(bindingProperty.name()); + } + } +} + +void addToGroupItem(const SelectionContext &selectionContext) +{ + const TypeName typeName = "QtQuick.Studio.Components.GroupItem"; + + try { + if (!hasStudioComponentsImport(selectionContext)) { + Import studioImport = Import::createLibraryImport("QtQuick.Studio.Components", "1.0"); + selectionContext.view()-> model()->changeImports({studioImport}, {}); + } + + if (!selectionContext.view()) + return; + + if (QmlItemNode::isValidQmlItemNode(selectionContext.firstSelectedModelNode())) { + const QmlItemNode qmlItemNode = QmlItemNode(selectionContext.firstSelectedModelNode()); + + if (qmlItemNode.hasInstanceParentItem()) { + ModelNode groupNode; + selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem1",[=, &groupNode](){ + + QmlItemNode parentNode = qmlItemNode.instanceParentItem(); + NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName); + groupNode = selectionContext.view()->createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); + reparentTo(groupNode, parentNode); + }); + selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem2",[=](){ + + QList<ModelNode> selectedNodes = selectionContext.selectedModelNodes(); + setUpperLeftPostionToNode(groupNode, selectedNodes); + + reparentToNodeAndAdjustPosition(groupNode, selectedNodes); + }); + } + } + } catch (RewritingException &e) { + e.showException(); + } +} + +void selectFlowEffect(const SelectionContext &selectionContext) +{ + if (!selectionContext.singleNodeIsSelected()) + return; + + ModelNode node = selectionContext.currentSingleSelectedNode(); + QmlVisualNode transition(node); + + QTC_ASSERT(transition.isValid(), return); + QTC_ASSERT(transition.isFlowTransition(), return); + + if (node.hasNodeProperty("effect")) { + selectionContext.view()->setSelectedModelNode(node.nodeProperty("effect").modelNode()); + } } } // namespace Mode diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 994110297e..220afe9d4e 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -77,6 +77,9 @@ bool addFontToProject(const QStringList &fileNames, const QString &directory); void createFlowActionArea(const SelectionContext &selectionContext); void addTransition(const SelectionContext &selectionState); void addFlowEffect(const SelectionContext &selectionState, const TypeName &typeName); +void setFlowStartItem(const SelectionContext &selectionContext); +void addToGroupItem(const SelectionContext &selectionContext); +void selectFlowEffect(const SelectionContext &selectionContext); } // namespace ModelNodeOperationso } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp b/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp index 59a9454b09..80201b315b 100644 --- a/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp +++ b/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp @@ -124,12 +124,17 @@ bool SelectionContext::isValid() const bool SelectionContext::fastUpdate() const { - return m_updateMode == UpdateMode::Fast; + return m_updateReason != UpdateMode::Normal; } void SelectionContext::setUpdateMode(UpdateMode mode) { - m_updateMode = mode; + m_updateReason = mode; +} + +SelectionContext::UpdateMode SelectionContext::updateReason() const +{ + return m_updateReason; } } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/selectioncontext.h b/src/plugins/qmldesigner/components/componentcore/selectioncontext.h index 929bf4eebb..3fe2157788 100644 --- a/src/plugins/qmldesigner/components/componentcore/selectioncontext.h +++ b/src/plugins/qmldesigner/components/componentcore/selectioncontext.h @@ -35,7 +35,14 @@ namespace QmlDesigner { class QMLDESIGNERCORE_EXPORT SelectionContext { public: - enum class UpdateMode {Normal, Fast}; + enum class UpdateMode { + Normal, + Fast, + Properties, + NodeCreated, + NodeHierachy, + Selection + }; SelectionContext(); SelectionContext(AbstractView *view); @@ -68,13 +75,15 @@ public: bool fastUpdate() const; void setUpdateMode(UpdateMode mode); + UpdateMode updateReason() const; + private: QPointer<AbstractView> m_view; ModelNode m_targetNode; QPointF m_scenePosition; bool m_showSelectionTools = false; bool m_toggled = false; - UpdateMode m_updateMode = UpdateMode::Normal; + UpdateMode m_updateReason = UpdateMode::Normal; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index ec4128479f..29b937a502 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -28,19 +28,46 @@ #include <qmldesignerplugin.h> +#include <coreplugin/icore.h> + #include <utils/stylehelper.h> #include <QApplication> #include <QRegExp> #include <QScreen> #include <QPointer> +#include <QQmlEngine> +#include <QQmlComponent> +#include <QQmlProperty> #include <qqml.h> +static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg) + namespace QmlDesigner { Theme::Theme(Utils::Theme *originTheme, QObject *parent) : Utils::Theme(originTheme, parent) + , m_constants(nullptr) { + QString constantsPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml"); + + QQmlEngine* engine = new QQmlEngine(this); + QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath)); + + if (component.status() == QQmlComponent::Ready) { + m_constants = component.create(); + } + else if (component.status() == QQmlComponent::Error ) { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "due to the following error(s):"; + for (QQmlError error : component.errors()) + qCWarning(themeLog) << error.toString(); + } + else { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "the status of the QQmlComponent is" << component.status(); + } } QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName) @@ -129,6 +156,25 @@ QPixmap Theme::getPixmap(const QString &id) return QmlDesignerIconProvider::getPixmap(id); } +QString Theme::getIconUnicode(Theme::Icon i) +{ + if (!instance()->m_constants) + return QString(); + + const QMetaObject *m = instance()->metaObject(); + const char *enumName = "Icon"; + int enumIndex = m->indexOfEnumerator(enumName); + + if (enumIndex == -1) { + qCWarning(themeLog) << "Couldn't find enum" << enumName; + return QString(); + } + + QMetaEnum e = m->enumerator(enumIndex); + + return instance()->m_constants->property(e.valueToKey(i)).toString(); +} + QColor Theme::qmlDesignerBackgroundColorDarker() const { return getColor(QmlDesigner_BackgroundColorDarker); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 6940c0c1cf..4cc4deb298 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -41,12 +41,95 @@ namespace QmlDesigner { class QMLDESIGNERCORE_EXPORT Theme : public Utils::Theme { Q_OBJECT + + Q_ENUMS(Icon) + public: + enum Icon { + actionIcon, + actionIconBinding, + addColumnAfter, + addColumnBefore, + addFile, + addRowAfter, + addRowBefore, + addTable, + adsClose, + adsDetach, + adsDropDown, + aliasAnimated, + aliasProperty, + alignBottom, + alignCenterHorizontal, + alignCenterVertical, + alignLeft, + alignRight, + alignTo, + alignTop, + assign, + anchorBaseline, + anchorBottom, + anchorFill, + anchorLeft, + anchorRight, + anchorTop, + annotationBubble, + annotationDecal, + centerHorizontal, + centerVertical, + curveEditor, + closeCross, + decisionNode, + deleteColumn, + deleteRow, + deleteTable, + detach, + distributeBottom, + distributeCenterHorizontal, + distributeCenterVertical, + distributeLeft, + distributeOriginBottomRight, + distributeOriginCenter, + distributeOriginNone, + distributeOriginTopLeft, + distributeRight, + distributeSpacingHorizontal, + distributeSpacingVertical, + distributeTop, + edit, + fontStyleBold, + fontStyleItalic, + fontStyleStrikethrough, + fontStyleUnderline, + mergeCells, + redo, + splitColumns, + splitRows, + startNode, + testIcon, + textAlignBottom, + textAlignCenter, + textAlignLeft, + textAlignMiddle, + textAlignRight, + textAlignTop, + textBulletList, + textFullJustification, + textNumberedList, + tickIcon, + triState, + undo, + upDownIcon, + upDownSquare2, + wildcard + }; + static Theme *instance(); static QString replaceCssColors(const QString &input); static void setupTheme(QQmlEngine *engine); static QColor getColor(Color role); static QPixmap getPixmap(const QString &id); + static QString getIconUnicode(Theme::Icon i); Q_INVOKABLE QColor qmlDesignerBackgroundColorDarker() const; Q_INVOKABLE QColor qmlDesignerBackgroundColorDarkAlternate() const; @@ -58,9 +141,12 @@ public: Q_INVOKABLE int smallFontPixelSize() const; Q_INVOKABLE int captionFontPixelSize() const; Q_INVOKABLE bool highPixelDensity() const; + private: Theme(Utils::Theme *originTheme, QObject *parent); QColor evaluateColorAtThemeInstance(const QString &themeColorName); + + QObject *m_constants; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index c8e1e222e5..9e882be2fd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -179,6 +179,17 @@ void Edit3DView::importsChanged(const QList<Import> &addedImports, checkImports(); } +void Edit3DView::customNotification(const AbstractView *view, const QString &identifier, + const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + Q_UNUSED(view) + Q_UNUSED(nodeList) + Q_UNUSED(data) + + if (identifier == "asset_import_update") + resetPuppet(); +} + void Edit3DView::sendInputEvent(QInputEvent *e) const { if (nodeInstanceView()) @@ -301,14 +312,16 @@ QVector<Edit3DAction *> Edit3DView::rightActions() const void Edit3DView::addQuick3DImport() { - const QList<Import> imports = model()->possibleImports(); - for (const auto &import : imports) { - if (import.url() == "QtQuick3D") { - model()->changeImports({import}, {}); - - // Subcomponent manager update needed to make item library entries appear - QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); - return; + if (model()) { + const QList<Import> imports = model()->possibleImports(); + for (const auto &import : imports) { + if (import.url() == "QtQuick3D") { + model()->changeImports({import}, {}); + + // Subcomponent manager update needed to make item library entries appear + QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); + return; + } } } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index d1646470b7..6c3ae892a9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -58,6 +58,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) 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 c948227233..3d0d3f2f64 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -106,12 +106,11 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : // Onboarding label contains instructions for new users how to get 3D content into the project m_onboardingLabel = new QLabel(this); QString labelText = - "No 3D import here yet!<br><br>" - "To create a 3D View you need to add the QtQuick3D import to your file.<br>" - "You can add the import via the QML Imports tab of the Library view, or alternatively click" - " <a href=\"#add_import\"><span style=\"text-decoration:none;color:%1\">here</span></a> " - "to add it straight away.<br><br>" - "If you want to import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view."; + tr("Your file does not import Qt Quick 3D.<br><br>" + "To create a 3D view, add the QtQuick3D import to your file in the QML Imports tab of the Library view. Or click" + " <a href=\"#add_import\"><span style=\"text-decoration:none;color:%1\">here</span></a> " + "here to add it immediately.<br><br>" + "To import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view."); m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name())); m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); connect(m_onboardingLabel, &QLabel::linkActivated, this, &Edit3DWidget::linkActivated); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri index 3464ba3afe..4609b277f9 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditor.pri +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri @@ -36,7 +36,8 @@ SOURCES += formeditoritem.cpp \ contentnoteditableindicator.cpp \ backgroundaction.cpp \ formeditortoolbutton.cpp \ - formeditorannotationicon.cpp + formeditorannotationicon.cpp \ + transitiontool.cpp HEADERS += formeditorscene.h \ formeditorwidget.h \ @@ -75,6 +76,7 @@ HEADERS += formeditorscene.h \ contentnoteditableindicator.h \ backgroundaction.h \ formeditortoolbutton.h \ - formeditorannotationicon.h + formeditorannotationicon.h \ + transitiontool.h RESOURCES += formeditor.qrc diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 96a4cc5adf..0c022431c9 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -32,10 +32,13 @@ #include <nodehints.h> #include <nodemetainfo.h> +#include <theme.h> + #include <utils/theme/theme.h> #include <utils/qtcassert.h> #include <QDebug> +#include <QFontDatabase> #include <QPainter> #include <QPainterPath> #include <QStyleOptionGraphicsItem> @@ -47,6 +50,36 @@ namespace QmlDesigner { const int flowBlockSize = 200; +const int blockRadius = 18; +const int blockAdjust = 40; + +const char startNodeIcon[] = "\u0055"; + +void drawIcon(QPainter *painter, + int x, + int y, + const QString &iconSymbol, + int fontSize, int iconSize, + const QColor &penColor) +{ + static QFontDatabase a; + + const QString fontName = "qtds_propertyIconFont.ttf"; + + Q_ASSERT(a.hasFamily(fontName)); + + if (a.hasFamily(fontName)) { + QFont font(fontName); + font.setPixelSize(fontSize); + + painter->save(); + painter->setPen(penColor); + painter->setFont(font); + painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol); + + painter->restore(); + } +} FormEditorScene *FormEditorItem::scene() const { return qobject_cast<FormEditorScene*>(QGraphicsItem::scene()); @@ -410,6 +443,8 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, || painterTransform.isRotating()) painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->setClipRegion(boundingRect().toRect()); + if (m_blurContent) painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceBlurredRenderPixmap()); else @@ -419,6 +454,7 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, } } + painter->setClipping(false); if (!qmlItemNode().isRootModelNode()) paintBoundingRect(painter); @@ -578,6 +614,7 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi return; painter->save(); + painter->setRenderHint(QPainter::Antialiasing); QPen pen; pen.setJoinStyle(Qt::MiterJoin); @@ -622,10 +659,9 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value<QColor>(); if (fillColor.alpha() > 0) - painter->fillRect(boundingRect(), fillColor); - - painter->drawRect(boundingRect()); + painter->setBrush(fillColor); + painter->drawRoundedRect(boundingRect(), blockRadius, blockRadius); painter->restore(); } @@ -719,7 +755,7 @@ void FormEditorTransitionItem::updateGeometry() QPointF toP = QmlItemNode(resolved.to).flowPosition(); if (QmlItemNode(resolved.to).isFlowDecision()) - sizeTo = QRectF(0, 0, flowBlockSize, flowBlockSize); + sizeTo = QRectF(0, 0, flowBlockSize * 2, flowBlockSize * 2); qreal x1 = fromP.x(); qreal x2 = toP.x(); @@ -757,10 +793,10 @@ QPointF FormEditorTransitionItem::instancePosition() const static bool verticalOverlap(const QRectF &from, const QRectF &to) { - if (from.top() < to.bottom() && (from.top() + from.height()) > to.top()) + if (from.top() < to.bottom() && from.bottom() > to.top()) return true; - if (to.top() < from.bottom() && (to.top() + to.height()) > from.top()) + if (to.top() < from.bottom() && to.bottom() > from.top()) return true; return false; @@ -769,25 +805,231 @@ static bool verticalOverlap(const QRectF &from, const QRectF &to) static bool horizontalOverlap(const QRectF &from, const QRectF &to) { - if (from.left() < to.right() && (from.left() + from.width()) > to.left()) + if (from.left() < to.right() && from.right() > to.left()) return true; - if (to.left() < from.right() && (to.left() + to.width()) > from.left()) + if (to.left() < from.right() && to.right() > from.left()) return true; return false; } +static void drawArrow(QPainter *painter, + const QLineF &line, + int arrowLength, + int arrowWidth) +{ + const QPointF peakP(0, 0); + const QPointF leftP(-arrowLength, -arrowWidth * 0.5); + const QPointF rightP(-arrowLength, arrowWidth * 0.5); + + painter->save(); + + painter->translate(line.p2()); + painter->rotate(-line.angle()); + painter->drawLine(leftP, peakP); + painter->drawLine(rightP, peakP); + + painter->restore(); +} + +static QPainterPath roundedCorner(const QPointF &s, + const QPointF &m, + const QPointF &e, + int radius) +{ + const QVector2D sm(m - s); + const QVector2D me(e - m); + const float smLength = sm.length(); + const float meLength = me.length(); + const int actualRadius = qMin(radius, static_cast<int>(qMin(smLength, meLength))); + const QVector2D smNorm = sm.normalized(); + const QVector2D meNorm = me.normalized(); + QRectF rect(m, QSizeF(actualRadius * 2, actualRadius * 2)); + + QPainterPath path(s); + + if (smNorm.y() < 0 && meNorm.x() > 0) { + rect.moveTopLeft(m); + path.arcTo(rect, 180, -90); + } else if (smNorm.x() < 0 && meNorm.y() > 0) { + rect.moveTopLeft(m); + path.arcTo(rect, 90, 90); + } else if (smNorm.y() > 0 && meNorm.x() > 0) { + rect.moveBottomLeft(m); + path.arcTo(rect, 180, 90); + } else if (smNorm.x() < 0 && meNorm.y() < 0) { + rect.moveBottomLeft(m); + path.arcTo(rect, 270, -90); + } else if (smNorm.x() > 0 && meNorm.y() > 0) { + rect.moveTopRight(m); + path.arcTo(rect, 90, -90); + } else if (smNorm.y() < 0 && meNorm.x() < 0) { + rect.moveTopRight(m); + path.arcTo(rect, 0, 90); + } else if (smNorm.y() > 0 && meNorm.x() < 0) { + rect.moveBottomRight(m); + path.arcTo(rect, 0, -90); + } else if (smNorm.x() > 0 && meNorm.y() < 0) { + rect.moveBottomRight(m); + path.arcTo(rect, 270, 90); + } + + path.lineTo(e); + return path; +} + +// This function determines whether the vertices are in cw or ccw order. +// It finds the lowest and rightmost vertex, and computes the cross-product +// of the vectors along its incident edges. +// Written by Joseph O'Rourke, 25 August 1995. orourke@cs.smith.edu +// 1: ccw +// 0: default +// -1: cw + +static int counterClockWise(const std::vector<QPointF> &points) +{ + if (points.empty()) + return 0; + + // FindLR finds the lowest, rightmost point. + auto findLR = [](const std::vector<QPointF> &points) { + int i = 0; + int m = 0; + QPointF min = points.front(); + + for (const auto p : points) { + if ((p.y() < min.y()) || ((p.y() == min.y()) && (p.x() > min.x()))) { + m = i; + min = p; + } + ++i; + } + return m; + }; + + const int m = findLR(points); + const int n = points.size(); + + // Determine previous and next point to m (the lowest, rightmost point). + const QPointF a = points[(m + (n - 1)) % n]; + const QPointF b = points[m]; + const QPointF c = points[(m + 1) % n]; + + const int area = a.x() * b.y() - a.y() * b.x() + + a.y() * c.x() - a.x() * c.y() + + b.x() * c.y() - c.x() * b.y(); + + if (area > 0) + return 1; + else if (area < 0) + return -1; + else + return 0; +} + +static QPainterPath quadBezier(const QPointF &s, + const QPointF &c, + const QPointF &e, + int bezier, + int breakOffset) +{ + QLineF se(s, e); + QPointF breakPoint = se.pointAt(breakOffset / 100.0); + QLineF breakLine; + + if (counterClockWise({s, c, e}) == 1) + breakLine = QLineF(breakPoint, breakPoint + QPointF(se.dy(), -se.dx())); + else + breakLine = QLineF(breakPoint, breakPoint + QPointF(-se.dy(), se.dx())); + + breakLine.setLength(se.length()); + + const QPointF controlPoint = breakLine.pointAt(bezier / 100.0); + + QPainterPath path(s); + path.quadTo(controlPoint, e); + + return path; +} + +static QPainterPath cubicBezier(const QPointF &s, + const QPointF &c1, + const QPointF &c2, + const QPointF &e, + int bezier) +{ + QPainterPath path(s); + const QPointF adjustedC1 = QLineF(s, c1).pointAt(bezier / 100.0); + const QPointF adjustedC2 = QLineF(e, c2).pointAt(bezier / 100.0); + + path.cubicTo(adjustedC1, adjustedC2, e); + + return path; +} + + +static QPainterPath lShapedConnection(const QPointF &start, + const QPointF &end, + Qt::Orientation orientation, + const ConnectionStyle &style) +{ + const QPointF mid = (orientation == Qt::Horizontal) ? QPointF(end.x(), start.y()) + : QPointF(start.x(), end.y()); + + if (style.type == ConnectionType::Default) { + if (style.radius == 0) { + QPainterPath path(start); + path.lineTo(mid); + path.lineTo(end); + return path; + } else { + return roundedCorner(start, mid, end, style.radius); + } + } else { + return quadBezier(start, mid, end, style.bezier, style.breakOffset); + } +} + +static QPainterPath sShapedConnection(const QPointF &start, + const QPointF &end, + Qt::Orientation orientation, + const ConnectionStyle &style) +{ + const qreal middleFactor = style.breakOffset / 100.0; + QPointF mid1; + QPointF mid2; + + if (orientation == Qt::Horizontal) { + mid1 = QPointF(start.x() * middleFactor + end.x() * (1 - middleFactor), start.y()); + mid2 = QPointF(mid1.x(), end.y()); + } else { + mid1 = QPointF(start.x(), start.y() * middleFactor + end.y() * (1 - middleFactor)); + mid2 = QPointF(end.x(), mid1.y()); + } + + if (style.type == ConnectionType::Default) { + if (style.radius == 0) { + QPainterPath path(start); + path.lineTo(mid1); + path.lineTo(mid2); + path.lineTo(end); + return path; + } else { + const QLineF breakLine(mid1, mid2); + QPainterPath path1 = roundedCorner(start, mid1, breakLine.center(), style.radius); + QPainterPath path2 = roundedCorner(breakLine.center(), mid2, end, style.radius); + return path1 + path2; + } + } else { + return cubicBezier(start, mid1, mid2, end, style.bezier); + } +} + static void paintConnection(QPainter *painter, const QRectF &from, const QRectF &to, - qreal width, - qreal adjustedWidth, - const QColor &color, - bool dash, - int startOffset, - int endOffset, - int breakOffset) + const ConnectionStyle &style) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); @@ -796,23 +1038,22 @@ static void paintConnection(QPainter *painter, pen.setCosmetic(true); pen.setJoinStyle(Qt::MiterJoin); pen.setCapStyle(Qt::RoundCap); + pen.setColor(style.color); - pen.setColor(color); - - if (dash) + if (style.dash) pen.setStyle(Qt::DashLine); else pen.setStyle(Qt::SolidLine); - pen.setWidthF(width); + pen.setWidthF(style.width); painter->setPen(pen); //const bool forceVertical = false; //const bool forceHorizontal = false; - const int padding = 2 * width + 2 * adjustedWidth; + const int padding = 2 * style.width + 2 * style.adjustedWidth; - const int arrowLength = 4 * adjustedWidth; - const int arrowWidth = 8 * adjustedWidth; + const int arrowLength = 4 * style.adjustedWidth; + const int arrowWidth = 8 * style.adjustedWidth; const bool boolExitRight = from.right() < to.center().x(); const bool boolExitBottom = from.bottom() < to.center().y(); @@ -824,10 +1065,6 @@ static void paintConnection(QPainter *painter, horizontalFirst = false; */ - const qreal middleFactor = breakOffset / 100.0; - - QPointF startP; - bool extraLine = false; if (horizontalFirst) { @@ -849,131 +1086,50 @@ static void paintConnection(QPainter *painter, } } - if (horizontalFirst) { - const qreal startY = from.center().y() + startOffset; - qreal startX = from.x() - padding; - if (boolExitRight) - startX = from.right() + padding; + QPointF startP; + QPointF endP; + QPainterPath path; + if (horizontalFirst) { + const qreal startX = boolExitRight ? from.right() + padding : from.x() - padding; + const qreal startY = from.center().y() + style.outOffset; startP = QPointF(startX, startY); - qreal endY = to.top() - padding; - - if (from.bottom() > to.y()) - endY = to.bottom() + padding; - if (!extraLine) { - - - const qreal endX = to.center().x() + endOffset; - - const QPointF midP(endX, startY); - - const QPointF endP(endX, endY); - - painter->drawLine(startP, midP); - painter->drawLine(midP, endP); - - int flip = 1; - - if (midP.y() < endP.y()) - flip = -1; - - pen.setStyle(Qt::SolidLine); - painter->setPen(pen); - painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP); - painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP); + const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding; + endP = QPointF(to.center().x() + style.inOffset, endY); + path = lShapedConnection(startP, endP, Qt::Horizontal, style); } else { - - qreal endX = to.left() - padding; - - if (from.right() > to.x()) - endX = to.right() + padding; - - const qreal midX = startX * middleFactor + endX * (1-middleFactor); - const QPointF midP(midX, startY); - const QPointF midP2(midX, to.center().y() + endOffset); - const QPointF endP(endX, to.center().y() + endOffset); - painter->drawLine(startP, midP); - painter->drawLine(midP, midP2); - painter->drawLine(midP2, endP); - - int flip = 1; - - if (midP2.x() < endP.x()) - flip = -1; - - pen.setStyle(Qt::SolidLine); - painter->setPen(pen); - painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP); - painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP); + const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding; + endP = QPointF(endX, to.center().y() + style.inOffset); + path = sShapedConnection(startP, endP, Qt::Horizontal, style); } - } else { - const qreal startX = from.center().x() + startOffset; - - qreal startY = from.top() - padding; - if (boolExitBottom) - startY = from.bottom() + padding; - + const qreal startX = from.center().x() + style.outOffset; + const qreal startY = boolExitBottom ? from.bottom() + padding : from.top() - padding; startP = QPointF(startX, startY); - qreal endX = to.left() - padding; - - if (from.right() > to.x()) - endX = to.right() + padding; if (!extraLine) { - const qreal endY = to.center().y() + endOffset; - - const QPointF midP(startX, endY); - - const QPointF endP(endX, endY); - - painter->drawLine(startP, midP); - painter->drawLine(midP, endP); - - int flip = 1; - - if (midP.x() < endP.x()) - flip = -1; - - pen.setStyle(Qt::SolidLine); - painter->setPen(pen); - painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP); - painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP); + const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding; + endP = QPointF(endX, to.center().y() + style.inOffset); + path = lShapedConnection(startP, endP, Qt::Vertical, style); } else { - - qreal endY = to.top() - padding; - - if (from.bottom() > to.y()) - endY = to.bottom() + padding; - - const qreal midY = startY * middleFactor + endY * (1-middleFactor); - const QPointF midP(startX, midY); - const QPointF midP2(to.center().x() + endOffset, midY); - const QPointF endP(to.center().x() + endOffset, endY); - - painter->drawLine(startP, midP); - painter->drawLine(midP, midP2); - painter->drawLine(midP2, endP); - - int flip = 1; - - if (midP2.y() < endP.y()) - flip = -1; - - pen.setStyle(Qt::SolidLine); - painter->setPen(pen); - painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP); - painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP); + const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding; + endP = QPointF(to.center().x() + style.inOffset, endY); + path = sShapedConnection(startP, endP, Qt::Vertical, style); } } - pen.setWidthF(width); + painter->drawPath(path); + + pen.setWidthF(style.width); pen.setStyle(Qt::SolidLine); painter->setPen(pen); + + drawArrow(painter, QLineF(path.pointAtPercent(0.9), endP), arrowLength, arrowWidth); + painter->setBrush(Qt::white); - painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3); + painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3); painter->restore(); } @@ -991,6 +1147,8 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + ResolveConnection resolved(qmlItemNode()); if (!resolved.from.modelNode().isValid()) @@ -1022,77 +1180,108 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi toRect.translate(QmlItemNode(resolved.to).flowPosition()); if (resolved.isStartLine) { - fromRect = QRectF(0,0,50,50); - fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-120, toRect.height() / 2 - 25)); + fromRect = QRectF(0, 0, 96, 96); + fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-180, toRect.height() / 2 - 96 / 2)); } toRect.translate(-pos()); fromRect.translate(-pos()); - qreal width = 2; + ConnectionStyle style; + + style.width = 2; const qreal scaleFactor = viewportTransform().m11(); if (qmlItemNode().modelNode().hasAuxiliaryData("width")) - width = qmlItemNode().modelNode().auxiliaryData("width").toInt(); + style.width = qmlItemNode().modelNode().auxiliaryData("width").toInt(); - qreal adjustedWidth = width / scaleFactor; + style.adjustedWidth = style.width / scaleFactor; if (qmlItemNode().modelNode().isSelected()) - width += 2; + style.width += 2; if (m_hitTest) - width *= 8; + style.width *= 8; - QColor color = "#e71919"; + style.color = "#e71919"; if (resolved.isStartLine) - color = "blue"; + style.color = "blue"; if (resolved.isWildcardLine) - color = "green"; - - bool dash = false; + style.color = "green"; if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionColor")) - color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>(); + style.color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>(); if (qmlItemNode().modelNode().hasAuxiliaryData("color")) - color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>(); + style.color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>(); + + style.dash = false; if (qmlItemNode().modelNode().hasAuxiliaryData("dash")) - dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool(); + style.dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool(); - int outOffset = 0; - int inOffset = 0; + style.outOffset = 0; + style.inOffset = 0; if (qmlItemNode().modelNode().hasAuxiliaryData("outOffset")) - outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt(); + style.outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt(); if (qmlItemNode().modelNode().hasAuxiliaryData("inOffset")) - inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt(); + style.inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt(); - int breakOffset = 50; + style.breakOffset = 50; if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint")) - breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt(); + style.breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt(); + + style.radius = 8; + + if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionRadius")) + style.radius = qmlItemNode().rootModelNode().auxiliaryData("transitionRadius").toInt(); + + if (qmlItemNode().modelNode().hasAuxiliaryData("radius")) + style.radius = qmlItemNode().modelNode().auxiliaryData("radius").toInt(); + + style.bezier = 50; + + if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionBezier")) + style.bezier = qmlItemNode().rootModelNode().auxiliaryData("transitionBezier").toInt(); + + if (qmlItemNode().modelNode().hasAuxiliaryData("bezier")) + style.bezier = qmlItemNode().modelNode().auxiliaryData("bezier").toInt(); + + style.type = ConnectionType::Default; - paintConnection(painter, fromRect, toRect, width, adjustedWidth ,color, dash, outOffset, inOffset, breakOffset); + if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionType")) + style.type = static_cast<ConnectionType>(qmlItemNode().rootModelNode().auxiliaryData("transitionType").toInt()); + + if (qmlItemNode().modelNode().hasAuxiliaryData("type")) + style.type = static_cast<ConnectionType>(qmlItemNode().modelNode().auxiliaryData("type").toInt()); + + if (resolved.isStartLine) + fromRect.translate(0, style.outOffset); + + paintConnection(painter, fromRect, toRect, style); if (resolved.isStartLine) { + + const QString icon = Theme::getIconUnicode(Theme::startNode); + QPen pen; pen.setCosmetic(true); - - pen.setColor(color); + pen.setColor(style.color); painter->setPen(pen); - painter->drawRect(fromRect); - - if (scaleFactor > 0.4) { - painter->drawLine(fromRect.topRight() + QPoint(20,10), fromRect.bottomRight() + QPoint(20,-10)); - painter->drawLine(fromRect.topRight() + QPoint(25,12), fromRect.bottomRight() + QPoint(25,-12)); - painter->drawLine(fromRect.topRight() + QPoint(30,15), fromRect.bottomRight() + QPoint(30,-15)); - painter->drawLine(fromRect.topRight() + QPoint(35,17), fromRect.bottomRight() + QPoint(35,-17)); - painter->drawLine(fromRect.topRight() + QPoint(40,20), fromRect.bottomRight() + QPoint(40,-20)); - } + + const int iconAdjust = 48; + const int offset = 96; + const int size = fromRect.width(); + const int iconSize = size - iconAdjust; + const int x = fromRect.topRight().x() - offset; + const int y = fromRect.topRight().y(); + painter->drawRoundedRect(x, y , size - 10, size, size / 2, iconSize / 2); + drawIcon(painter, x + iconAdjust / 2, y + iconAdjust / 2, icon, iconSize, iconSize, style.color); } painter->restore(); @@ -1141,6 +1330,9 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setRenderHint(QPainter::SmoothPixmapTransform); + QPen pen; pen.setJoinStyle(Qt::MiterJoin); pen.setCosmetic(true); @@ -1179,20 +1371,37 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap if (qmlItemNode().modelNode().hasAuxiliaryData("fillColor")) fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value<QColor>(); + painter->save(); + + if (m_iconType == DecisionIcon) { + painter->translate(boundingRect().center()); + painter->rotate(45); + painter->translate(-boundingRect().center()); + } + if (fillColor.alpha() > 0) - painter->fillRect(boundingRect(), fillColor); + painter->setBrush(fillColor); - painter->drawLine(boundingRect().left(), boundingRect().center().y(), - boundingRect().center().x(), boundingRect().top()); + int radius = blockRadius; - painter->drawLine(boundingRect().center().x(), boundingRect().top(), - boundingRect().right(), boundingRect().center().y()); + const QRectF adjustedRect = boundingRect().adjusted(blockAdjust, + blockAdjust, + -blockAdjust, + -blockAdjust); + + painter->drawRoundedRect(adjustedRect, radius, radius); + + const int iconDecrement = 32; + const int iconSize = adjustedRect.width() - iconDecrement; + const int offset = iconDecrement / 2 + blockAdjust; + + painter->restore(); - painter->drawLine(boundingRect().right(), boundingRect().center().y(), - boundingRect().center().x(), boundingRect().bottom()); + const QString icon = (m_iconType == + WildcardIcon) ? Theme::getIconUnicode(Theme::wildcard) + : Theme::getIconUnicode(Theme::decisionNode); - painter->drawLine(boundingRect().center().x(), boundingRect().bottom(), - boundingRect().left(), boundingRect().center().y()); + drawIcon(painter, offset, offset, icon, iconSize, iconSize, flowColor); painter->restore(); } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index d725afd0b0..8569059b7a 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -47,6 +47,27 @@ namespace Internal { class MoveController; } +enum ConnectionType +{ + Default = 0, + Bezier +}; + +class ConnectionStyle +{ +public: + qreal width; + qreal adjustedWidth; + QColor color; + bool dash; + int outOffset; + int inOffset; + int breakOffset; + int radius; + int bezier; + ConnectionType type; +}; + class QMLDESIGNERCORE_EXPORT FormEditorItem : public QGraphicsItem { friend class QmlDesigner::FormEditorScene; @@ -207,9 +228,17 @@ public: bool flowHitTest(const QPointF &point) const override; protected: - FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) - : FormEditorFlowItem(qmlItemNode, scene) + enum IconType { + DecisionIcon, + WildcardIcon + }; + + FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode, + FormEditorScene* scene, + IconType iconType = DecisionIcon) + : FormEditorFlowItem(qmlItemNode, scene), m_iconType(iconType) {} + IconType m_iconType; }; class FormEditorFlowWildcardItem : FormEditorFlowDecisionItem @@ -221,8 +250,9 @@ public: protected: FormEditorFlowWildcardItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) - : FormEditorFlowDecisionItem(qmlItemNode, scene) - {} + : FormEditorFlowDecisionItem(qmlItemNode, scene, WildcardIcon) + { + } }; inline int FormEditorItem::type() const diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp index 49bb98db4a..8907aab2bc 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp @@ -97,8 +97,11 @@ void ResizeManipulator::begin(const QPointF &/*beginPoint*/) // return QSizeF(sizeAsPoint.x(), sizeAsPoint.y()); //} -void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping) +void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping, Qt::KeyboardModifiers keyMods) { + const bool preserveAspectRatio = keyMods.testFlag(Qt::ShiftModifier); + const bool resizeFromCenter = keyMods.testFlag(Qt::AltModifier); + const double minimumWidth = 0.0; const double minimumHeight = 0.0; @@ -118,6 +121,16 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use QmlAnchors anchors(formEditorItem->qmlItemNode().anchors()); QRectF boundingRect(m_beginBoundingRect); + + auto getRatioSizes = [&](){ + double ratio = std::min(boundingRect.width() / m_beginBoundingRect.width(), + boundingRect.height() / m_beginBoundingRect.height()); + double newW = m_beginBoundingRect.width() * ratio; + double newH = m_beginBoundingRect.height() * ratio; + + return QSizeF(newW, newH); + }; + if (m_resizeHandle->isBottomRightHandle()) { boundingRect.setBottomRight(updatePointInLocalSpace); @@ -132,6 +145,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use } boundingRect.setBottomRight(updatePointInLocalSpace); + if (preserveAspectRatio) { + QSizeF newSize = getRatioSizes(); + + updatePointInLocalSpace.rx() = (boundingRect.topLeft().x() + newSize.width()); + updatePointInLocalSpace.ry() = (boundingRect.topLeft().y() + newSize.height()); + + boundingRect.setBottomRight(updatePointInLocalSpace); + } + + if (resizeFromCenter) { + QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(), + boundingRect.height() - m_beginBoundingRect.height() }; + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineTop)) + boundingRect.setTop(boundingRect.top() - grow.y()); + if (!anchors.instanceHasAnchor(AnchorLineLeft)) + boundingRect.setLeft(boundingRect.left() - grow.x()); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); @@ -144,6 +179,7 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setHeight(minimumHeight); formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); if (anchors.instanceHasAnchor(AnchorLineBottom)) { anchors.setMargin(AnchorLineBottom, @@ -168,6 +204,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use } boundingRect.setTopLeft(updatePointInLocalSpace); + if (preserveAspectRatio) { + QSizeF newSize = getRatioSizes(); + + updatePointInLocalSpace.rx() = (boundingRect.bottomRight().x() - newSize.width()); + updatePointInLocalSpace.ry() = (boundingRect.bottomRight().y() - newSize.height()); + + boundingRect.setTopLeft(updatePointInLocalSpace); + } + + if (resizeFromCenter) { + QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(), + boundingRect.height() - m_beginBoundingRect.height() }; + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineBottom)) + boundingRect.setBottom(boundingRect.bottom() + grow.y()); + if (!anchors.instanceHasAnchor(AnchorLineRight)) + boundingRect.setRight(boundingRect.right() + grow.x()); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); @@ -208,6 +266,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use } boundingRect.setTopRight(updatePointInLocalSpace); + if (preserveAspectRatio) { + QSizeF newSize = getRatioSizes(); + + updatePointInLocalSpace.rx() = (boundingRect.bottomLeft().x() + newSize.width()); + updatePointInLocalSpace.ry() = (boundingRect.bottomLeft().y() - newSize.height()); + + boundingRect.setTopRight(updatePointInLocalSpace); + } + + if (resizeFromCenter) { + QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(), + boundingRect.height() - m_beginBoundingRect.height() }; + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineBottom)) + boundingRect.setBottom(boundingRect.bottom() + grow.y()); + if (!anchors.instanceHasAnchor(AnchorLineLeft)) + boundingRect.setLeft(boundingRect.left() - grow.x()); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); @@ -246,6 +326,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setBottomLeft(updatePointInLocalSpace); + if (preserveAspectRatio) { + QSizeF newSize = getRatioSizes(); + + updatePointInLocalSpace.rx() = (boundingRect.topRight().x() - newSize.width()); + updatePointInLocalSpace.ry() = (boundingRect.topRight().y() + newSize.height()); + + boundingRect.setBottomLeft(updatePointInLocalSpace); + } + + if (resizeFromCenter) { + QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(), + boundingRect.height() - m_beginBoundingRect.height() }; + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineTop)) + boundingRect.setTop(boundingRect.top() - grow.y()); + if (!anchors.instanceHasAnchor(AnchorLineRight)) + boundingRect.setRight(boundingRect.right() + grow.x()); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); @@ -280,13 +382,30 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setBottom(updatePointInLocalSpace.y()); + if (resizeFromCenter) { + double grow = boundingRect.height() - m_beginBoundingRect.height(); + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineTop)) + boundingRect.setTop(boundingRect.top() - grow); + if (!anchors.instanceHasAnchor(AnchorLineLeft)) + boundingRect.setLeft(boundingRect.left() - grow); + if (!anchors.instanceHasAnchor(AnchorLineRight)) + boundingRect.setRight(boundingRect.right() + grow); + } + } + if (anchors.instanceHasAnchor(AnchorLineVerticalCenter)) boundingRect.setTop(boundingRect.top() - (updatePointInLocalSpace.y() - m_beginBoundingRect.bottom())); + if (boundingRect.width() < minimumWidth) + boundingRect.setWidth(minimumWidth); if (boundingRect.height() < minimumHeight) boundingRect.setHeight(minimumHeight); formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); if (anchors.instanceHasAnchor(AnchorLineBottom)) { anchors.setMargin(AnchorLineBottom, @@ -303,9 +422,25 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setTop(updatePointInLocalSpace.y()); + if (resizeFromCenter) { + double grow = boundingRect.height() - m_beginBoundingRect.height(); + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineBottom)) + boundingRect.setBottom(boundingRect.bottom() + grow); + if (!anchors.instanceHasAnchor(AnchorLineLeft)) + boundingRect.setLeft(boundingRect.left() - grow); + if (!anchors.instanceHasAnchor(AnchorLineRight)) + boundingRect.setRight(boundingRect.right() + grow); + } + } + if (anchors.instanceHasAnchor(AnchorLineVerticalCenter)) boundingRect.setBottom(boundingRect.bottom() - (updatePointInLocalSpace.y() - m_beginBoundingRect.top())); + if (boundingRect.width() < minimumWidth) + boundingRect.setWidth(minimumWidth); if (boundingRect.height() < minimumHeight) boundingRect.setTop(boundingRect.top() - minimumHeight + boundingRect.height()); @@ -327,13 +462,30 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setRight(updatePointInLocalSpace.x()); + if (resizeFromCenter) { + double grow = boundingRect.width() - m_beginBoundingRect.width(); + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineTop)) + boundingRect.setTop(boundingRect.top() - grow); + if (!anchors.instanceHasAnchor(AnchorLineLeft)) + boundingRect.setLeft(boundingRect.left() - grow); + if (!anchors.instanceHasAnchor(AnchorLineBottom)) + boundingRect.setBottom(boundingRect.bottom() + grow); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); if (boundingRect.width() < minimumWidth) boundingRect.setWidth(minimumWidth); + if (boundingRect.height() < minimumHeight) + boundingRect.setHeight(minimumHeight); formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); if (anchors.instanceHasAnchor(AnchorLineRight)) { @@ -351,11 +503,27 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use boundingRect.setLeft(updatePointInLocalSpace.x()); + if (resizeFromCenter) { + double grow = boundingRect.width() - m_beginBoundingRect.width(); + + if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter) + && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) { + if (!anchors.instanceHasAnchor(AnchorLineTop)) + boundingRect.setTop(boundingRect.top() - grow); + if (!anchors.instanceHasAnchor(AnchorLineBottom)) + boundingRect.setBottom(boundingRect.bottom() + grow); + if (!anchors.instanceHasAnchor(AnchorLineRight)) + boundingRect.setRight(boundingRect.right() + grow); + } + } + if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter)) boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); if (boundingRect.width() < minimumWidth) boundingRect.setLeft(boundingRect.left() - minimumWidth + boundingRect.width()); + if (boundingRect.height() < minimumHeight) + boundingRect.setHeight(minimumHeight); formEditorItem->qmlItemNode().setSize(boundingRect.size()); formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h index ab4f97f9cb..551006d161 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h @@ -46,7 +46,8 @@ public: void removeHandle(); void begin(const QPointF& beginPoint); - void update(const QPointF& updatePoint, Snapper::Snapping useSnapping); + void update(const QPointF& updatePoint, Snapper::Snapping useSnapping, + Qt::KeyboardModifiers keyMods = Qt::NoModifier); void end(Snapper::Snapping useSnapping); void moveBy(double deltaX, double deltaY); diff --git a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp index 8b4e5c552a..e959a4b6b1 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp @@ -71,7 +71,8 @@ void ResizeTool::mouseMoveEvent(const QList<QGraphicsItem*> &, QGraphicsSceneMouseEvent *event) { if (m_resizeManipulator.isActive()) - m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers())); + m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers()), + event->modifiers()); } void ResizeTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp new file mode 100644 index 0000000000..e8222e8ac8 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "transitiontool.h" + +#include <formeditorscene.h> +#include <formeditorview.h> +#include <formeditorwidget.h> +#include <itemutilfunctions.h> +#include <formeditoritem.h> +#include <layeritem.h> + +#include <resizehandleitem.h> + +#include <bindingproperty.h> +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <nodemetainfo.h> +#include <qmlitemnode.h> +#include <qmldesignerplugin.h> +#include <abstractaction.h> +#include <designeractionmanager.h> +#include <variantproperty.h> +#include <rewritingexception.h> +#include <rewritertransaction.h> + +#include <coreplugin/icore.h> +#include <utils/qtcassert.h> + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QAction> +#include <QMessageBox> +#include <QPair> +#include <QGraphicsSceneMouseEvent> + +namespace QmlDesigner { + +static bool isTransitionSource(const ModelNode &node) +{ + return QmlFlowTargetNode::isFlowEditorTarget(node); +} + +static bool isTransitionTarget(const QmlItemNode &node) +{ + return QmlFlowTargetNode::isFlowEditorTarget(node) + && !node.isFlowActionArea() + && !node.isFlowWildcard(); +} + +class TransitionToolAction : public AbstractAction +{ +public: + TransitionToolAction(const QString &name) : AbstractAction(name) {} + + QByteArray category() const override + { + return QByteArray(); + } + + QByteArray menuId() const override + { + return "TransitionTool"; + } + + int priority() const override + { + return CustomActionsPriority; + } + + Type type() const override + { + return ContextMenuAction; + } + +protected: + bool isVisible(const SelectionContext &selectionContext) const override + { + if (selectionContext.scenePosition().isNull()) + return false; + + if (selectionContext.singleNodeIsSelected()) + return isTransitionSource(selectionContext.currentSingleSelectedNode()); + + return false; + } + + bool isEnabled(const SelectionContext &selectionContext) const override + { + return isVisible(selectionContext); + } +}; + +class TransitionCustomAction : public TransitionToolAction +{ +public: + TransitionCustomAction(const QString &name) : TransitionToolAction(name) {} + + QByteArray category() const override + { + return ComponentCoreConstants::flowCategory; + } + + SelectionContext selectionContext() const + { + return AbstractAction::selectionContext(); + } + +}; + +static QRectF paintedBoundingRect(FormEditorItem *item) +{ + QRectF boundingRect = item->qmlItemNode().instanceBoundingRect(); + if (boundingRect.width() < 4) + boundingRect = item->boundingRect(); + return boundingRect; +} + +static QPointF centerPoint(FormEditorItem *item) +{ + QRectF boundingRect = paintedBoundingRect(item); + return QPointF(item->scenePos().x() + boundingRect.width() / 2, + item->scenePos().y() + boundingRect.height() / 2); +} + +void static setToBoundingRect(QGraphicsRectItem *rect, FormEditorItem *item) +{ + QPolygonF boundingRectInSceneSpace(item->mapToScene(paintedBoundingRect(item))); + rect->setRect(boundingRectInSceneSpace.boundingRect()); +} + +TransitionTool::TransitionTool() + : QObject(), AbstractCustomTool() +{ + + TransitionToolAction *transitionToolAction = new TransitionToolAction(tr("Add Transition")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(transitionToolAction); + + connect(transitionToolAction->action(), &QAction::triggered, + this, &TransitionTool::activateTool); + + TransitionCustomAction *removeAction = new TransitionCustomAction(tr("Remove Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAction); + + connect(removeAction->action(), &QAction::triggered, + this, [removeAction](){ + + SelectionContext context = removeAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove Transitions", [&node](){ + if (node.isValid()) + node.removeTransitions(); + }); + }); + + TransitionCustomAction *removeAllTransitionsAction = new TransitionCustomAction(tr("Remove All Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAllTransitionsAction); + + connect(removeAllTransitionsAction->action(), &QAction::triggered, + this, [removeAllTransitionsAction](){ + + if (QMessageBox::question(Core::ICore::dialogParent(), + tr("Remove All Transitions"), + tr("Do you really want to remove all transitions?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return; + + SelectionContext context = removeAllTransitionsAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove All Transitions", [&node](){ + if (node.isValid() && node.flowView().isValid()) + node.flowView().removeAllTransitions(); + }); + }); + + TransitionCustomAction *removeDanglingTransitionAction = new TransitionCustomAction(tr("Remove Dangling Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeDanglingTransitionAction); + + connect(removeDanglingTransitionAction->action(), &QAction::triggered, + this, [removeDanglingTransitionAction](){ + + SelectionContext context = removeDanglingTransitionAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove Dangling Transitions", [&node](){ + if (node.isValid() && node.flowView().isValid()) + node.flowView().removeDanglingTransitions(); + }); + }); +} + +TransitionTool::~TransitionTool() +{ +} + +void TransitionTool::clear() +{ + m_lineItem.reset(nullptr); + m_rectangleItem1.reset(nullptr); + m_rectangleItem2.reset(nullptr); + + AbstractFormEditorTool::clear(); +} + +void TransitionTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_blockEvents) + return; + + if (event->button() != Qt::LeftButton) + return; + + AbstractFormEditorTool::mousePressEvent(itemList, event); + TransitionTool::mouseMoveEvent(itemList, event); +} + +void TransitionTool::mouseMoveEvent(const QList<QGraphicsItem*> & itemList, + QGraphicsSceneMouseEvent * event) +{ + if (!m_lineItem) + return; + + QTC_ASSERT(currentFormEditorItem(), return); + + const QPointF pos = centerPoint(m_formEditorItem); + lineItem()->setLine(pos.x(), + pos.y(), + event->scenePos().x(), + event->scenePos().y()); + + FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList); + + if (formEditorItem + && formEditorItem->qmlItemNode().isValid() + && isTransitionTarget(formEditorItem->qmlItemNode().modelNode())) { + rectangleItem2()->setVisible(true); + setToBoundingRect(rectangleItem2(), formEditorItem); + } else { + rectangleItem2()->setVisible(false); + } +} + +void TransitionTool::hoverMoveEvent(const QList<QGraphicsItem*> & itemList, + QGraphicsSceneMouseEvent *event) +{ + mouseMoveEvent(itemList, event); +} + +void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/) +{ +} + +void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ + view()->changeToSelectionTool(); +} + +void TransitionTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_blockEvents) + return; + + if (event->button() == Qt::LeftButton) { + FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList); + + if (formEditorItem + && QmlFlowTargetNode(formEditorItem->qmlItemNode().modelNode()).isValid()) + createTransition(m_formEditorItem, formEditorItem); + } + + view()->changeToSelectionTool(); +} + + +void TransitionTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event); +} + +void TransitionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &) +{ + view()->changeCurrentToolTo(this); +} + +void TransitionTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList) +{ + if (!itemList.isEmpty()) { + createItems(); + + m_formEditorItem = itemList.first(); + setToBoundingRect(rectangleItem1(), m_formEditorItem); + } +} + +void TransitionTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/) +{ +} + +void TransitionTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/) +{ +} + +void TransitionTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/) +{ +} + +void TransitionTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/) +{ +} + +int TransitionTool::wantHandleItem(const ModelNode &modelNode) const +{ + if (isTransitionSource(modelNode)) + return 10; + + return 0; +} + +QString TransitionTool::name() const +{ + return tr("Transition Tool"); +} + +void TransitionTool::activateTool() +{ + view()->changeToCustomTool(); +} + +void TransitionTool::unblock() +{ + m_blockEvents = false; +} + +QGraphicsLineItem *TransitionTool::lineItem() +{ + return m_lineItem.get(); +} + +QGraphicsRectItem *TransitionTool::rectangleItem1() +{ + return m_rectangleItem1.get(); +} + +QGraphicsRectItem *TransitionTool::rectangleItem2() +{ + return m_rectangleItem2.get(); +} + +FormEditorItem *TransitionTool::currentFormEditorItem() const +{ + if (scene()->items().contains(m_formEditorItem)) + return m_formEditorItem; + + return nullptr; +} + +void TransitionTool::createItems() { + m_blockEvents = true; + QTimer::singleShot(200, this, [this](){ unblock(); }); + + if (!lineItem()) + m_lineItem.reset(new QGraphicsLineItem(scene()->manipulatorLayerItem())); + + if (!rectangleItem1()) + m_rectangleItem1.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem())); + + if (!rectangleItem2()) + m_rectangleItem2.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem())); + + m_rectangleItem2->setVisible(false); + + QPen pen; + pen.setColor(QColor(Qt::lightGray)); + pen.setStyle(Qt::DashLine); + pen.setWidth(0); + m_lineItem->setPen(pen); + + pen.setColor(QColor(108, 141, 221)); + pen.setStyle(Qt::SolidLine); + pen.setWidth(4); + pen.setCosmetic(true); + m_rectangleItem1->setPen(pen); + + m_rectangleItem2->setPen(pen); +} + +void TransitionTool::createTransition(FormEditorItem *source, FormEditorItem *target) +{ + QmlFlowTargetNode sourceNode(source->qmlItemNode().modelNode()); + QmlFlowTargetNode targetNode(target->qmlItemNode().modelNode()); + + if (sourceNode.isValid() && targetNode.isValid() + && sourceNode != targetNode + && !targetNode.isFlowActionArea() + && !targetNode.isFlowWildcard()) { + view()->executeInTransaction("create transition", [&sourceNode, targetNode](){ + sourceNode.assignTargetItem(targetNode); + }); + } else { + qWarning() << Q_FUNC_INFO << "nodes invalid"; + } +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.h b/src/plugins/qmldesigner/components/formeditor/transitiontool.h new file mode 100644 index 0000000000..43c3894933 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "abstractcustomtool.h" +#include "selectionindicator.h" + +#include <QGraphicsLineItem> +#include <QHash> +#include <QPointer> + +#include <memory> + +namespace QmlDesigner { + +class TransitionTool : public QObject, public AbstractCustomTool +{ + Q_OBJECT +public: + TransitionTool(); + ~TransitionTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + + void dragLeaveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneDragDropEvent * event) override; + void dragMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneDragDropEvent * event) override; + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override; + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override; + + void instancesCompleted(const QList<FormEditorItem*> &itemList) override; + void instancesParentChanged(const QList<FormEditorItem *> &itemList) override; + void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override; + + void clear() override; + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override; + + int wantHandleItem(const ModelNode &modelNode) const override; + + QString name() const override; + + void activateTool(); + void unblock(); + + QGraphicsLineItem *lineItem(); + QGraphicsRectItem *rectangleItem1(); + QGraphicsRectItem *rectangleItem2(); + +private: + FormEditorItem *currentFormEditorItem() const; + void createItems(); + void createTransition(FormEditorItem *item1, FormEditorItem *item2); + + FormEditorItem* m_formEditorItem; + std::unique_ptr<QGraphicsLineItem> m_lineItem; + std::unique_ptr<QGraphicsRectItem> m_rectangleItem1; + std::unique_ptr<QGraphicsRectItem> m_rectangleItem2; + bool m_blockEvents = true; +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp index df9590af64..c2bce0b0ce 100644 --- a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp +++ b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp @@ -81,14 +81,16 @@ void ImportManagerView::modelAboutToBeDetached(Model *model) void ImportManagerView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) { - if (m_importsWidget) + if (m_importsWidget) { m_importsWidget->setImports(model()->imports()); + // setImports recreates labels, so we need to update used imports, as it is not guaranteed + // usedImportsChanged notification will come after this. + m_importsWidget->setUsedImports(model()->usedImports()); + } } void ImportManagerView::possibleImportsChanged(const QList<Import> &/*possibleImports*/) { - QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); - if (m_importsWidget) m_importsWidget->setPossibleImports(model()->possibleImports()); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 3f403d0313..0beb5bce72 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -32,6 +32,9 @@ #include "utils/outputformatter.h" #include "theme.h" +#include <projectexplorer/project.h> +#include <projectexplorer/session.h> + #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> #include <QtCore/qloggingcategory.h> @@ -97,6 +100,20 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); + QStringList importPaths; + auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); + if (doc) { + Model *model = doc->currentModel(); + if (model) + importPaths = model->importPaths(); + } + + QString targetDir = defaulTargetDirectory; + + ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(doc->fileName()); + if (currentProject) + targetDir = currentProject->projectDirectory().toString(); + // Import is always done under known folder. The order of preference for folder is: // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path @@ -105,19 +122,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER); const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); - QString candidatePath = defaulTargetDirectory + defaultAssetFolder + quick3DFolder; + QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder; int candidatePriority = 5; - QStringList importPaths; - - auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); - if (doc) { - Model *model = doc->currentModel(); - if (model) - importPaths = model->importPaths(); - } for (auto importPath : qAsConst(importPaths)) { - if (importPath.startsWith(defaulTargetDirectory)) { + if (importPath.startsWith(targetDir)) { const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder); const QString assetFolder = importPath + quick3DFolder; const bool exists = QFileInfo(assetFolder).exists(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 34fa71222d..6a5edb18a3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -280,7 +280,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar return; } + QString originalAssetName = assetName; if (targetDir.exists(assetName)) { + // If we have a file system with case insensitive filenames, assetName may be + // different from the existing name. Modify assetName to ensure exact match to + // the overwritten old asset capitalization + const QStringList assetDirs = targetDir.entryList({assetName}, QDir::Dirs); + if (assetDirs.size() == 1) { + assetName = assetDirs[0]; + targetDirPath = targetDir.filePath(assetName); + } if (!confirmAssetOverwrite(assetName)) { addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName)); return; @@ -306,6 +315,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar return; } + // The importer is reset after every import to avoid issues with it caching various things + m_quick3DAssetImporter.reset(new QSSGAssetImportManager); + + if (originalAssetName != assetName) { + // Fix the generated qml file name + const QString assetQml = originalAssetName + ".qml"; + if (outDir.exists(assetQml)) + outDir.rename(assetQml, assetName + ".qml"); + } + QHash<QString, QString> assetFiles; const int outDirPathSize = outDir.path().size(); auto insertAsset = [&](const QString &filePath) { @@ -512,18 +531,24 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() addInfo(progressTitle); notifyProgress(0, progressTitle); - // There is an inbuilt delay before rewriter change actually updates the data model, - // so we need to wait for a moment to allow the change to take effect. + // First we have to wait a while to ensure qmljs detects new files and updates its + // internal model. Then we make a non-change to the document to trigger qmljs snapshot + // update. There is an inbuilt delay before rewriter change actually updates the data + // model, so we need to wait for another moment to allow the change to take effect. // Otherwise subsequent subcomponent manager update won't detect new imports properly. QTimer *timer = new QTimer(parent()); static int counter; counter = 0; - timer->callOnTimeout([this, timer, progressTitle, model]() { + timer->callOnTimeout([this, timer, progressTitle, model, doc]() { if (!isCancelled()) { - notifyProgress(++counter * 10, progressTitle); - if (counter >= 10) { - // Trigger underlying qmljs snapshot update by making a non-change to the doc + notifyProgress(++counter * 5, progressTitle); + if (counter == 10) { model->rewriterView()->textModifier()->replace(0, 0, {}); + } else if (counter == 19) { + doc->updateSubcomponentManager(); + } else if (counter >= 20) { + if (!m_overwrittenImports.isEmpty()) + model->rewriterView()->emitCustomNotification("asset_import_update"); timer->stop(); notifyFinished(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index caf0663dce..3f7ca2c366 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -384,7 +384,19 @@ void ItemLibraryWidget::updateModel() { QTC_ASSERT(m_itemLibraryModel, return); + if (m_compressionTimer.isActive()) { + m_updateRetry = false; + m_compressionTimer.stop(); + } + m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data()); + + if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) { + m_updateRetry = true; // Only retry once to avoid endless loops + m_compressionTimer.start(); + } else { + m_updateRetry = false; + } updateImports(); updateSearch(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 6e8fc0340a..11dea7d0c1 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -121,6 +121,7 @@ private: QPointer<Model> m_model; FilterChangeFlag m_filterFlag; ItemLibraryEntry m_currentitemLibraryEntry; + bool m_updateRetry = false; }; } diff --git a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h index bedcc2f561..d560eb2824 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h +++ b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h @@ -43,6 +43,7 @@ public: virtual void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) = 0; virtual void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) = 0; virtual void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) = 0; + virtual void notifyIconsChanged() = 0; virtual void setFilter(bool showObjects) = 0; virtual void resetModel() = 0; }; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index c91ff78626..70d2e405f9 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -695,6 +695,11 @@ void NavigatorTreeModel::notifyModelNodesMoved(const QList<ModelNode> &modelNode emit layoutChanged(indexes); } +void NavigatorTreeModel::notifyIconsChanged() +{ + emit dataChanged(index(0, 0), index(rowCount(), 0), {Qt::DecorationRole}); +} + void NavigatorTreeModel::setFilter(bool showOnlyVisibleItems) { m_showOnlyVisibleItems = showOnlyVisibleItems; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index f10198adcc..15e89d3636 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -87,6 +87,7 @@ public: void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) override; void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) override; void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) override; + void notifyIconsChanged() override; void setFilter(bool showOnlyVisibleItems) override; void resetModel() override; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index ea64f0715a..cd6857d789 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -147,6 +147,17 @@ void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & prop } } +void NavigatorView::customNotification(const AbstractView *view, const QString &identifier, + const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + Q_UNUSED(view) + Q_UNUSED(nodeList) + Q_UNUSED(data) + + if (identifier == "asset_import_update") + m_currentModelInterface->notifyIconsChanged(); +} + void NavigatorView::handleChangedExport(const ModelNode &modelNode, bool exported) { const ModelNode rootNode = rootModelNode(); @@ -416,12 +427,24 @@ void NavigatorView::updateItemSelection() QItemSelection itemSelection; foreach (const ModelNode &node, selectedModelNodes()) { const QModelIndex index = indexForModelNode(node); + if (index.isValid()) { const QModelIndex beginIndex(currentModel()->index(index.row(), 0, index.parent())); const QModelIndex endIndex(currentModel()->index(index.row(), currentModel()->columnCount(index.parent()) - 1, index.parent())); if (beginIndex.isValid() && endIndex.isValid()) itemSelection.select(beginIndex, endIndex); - } + } else { + // if the node index is invalid expand ancestors manually if they are valid. + ModelNode parentNode = node; + while (parentNode.hasParentProperty()) { + parentNode = parentNode.parentProperty().parentQmlObjectNode(); + QModelIndex parentIndex = indexForModelNode(parentNode); + if (parentIndex.isValid()) + treeWidget()->expand(parentIndex); + else + break; + } + } } bool blocked = blockSelectionChangedSignal(true); @@ -431,10 +454,10 @@ void NavigatorView::updateItemSelection() if (!selectedModelNodes().isEmpty()) treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constFirst())); - // make sure selected nodes a visible + // make sure selected nodes are visible foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) { if (selectedIndex.column() == 0) - expandRecursively(selectedIndex); + expandAncestors(selectedIndex); } } @@ -458,9 +481,9 @@ bool NavigatorView::blockSelectionChangedSignal(bool block) return oldValue; } -void NavigatorView::expandRecursively(const QModelIndex &index) +void NavigatorView::expandAncestors(const QModelIndex &index) { - QModelIndex currentIndex = index; + QModelIndex currentIndex = index.parent(); while (currentIndex.isValid()) { if (!treeWidget()->isExpanded(currentIndex)) treeWidget()->expand(currentIndex); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index 852dddc70f..3bafe0fa80 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -84,6 +84,8 @@ public: void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; + void handleChangedExport(const ModelNode &modelNode, bool exported); bool isNodeInvisible(const ModelNode &modelNode) const; @@ -108,7 +110,7 @@ protected: //functions QTreeView *treeWidget() const; NavigatorTreeModel *treeModel(); bool blockSelectionChangedSignal(bool block); - void expandRecursively(const QModelIndex &index); + void expandAncestors(const QModelIndex &index); void reparentAndCatch(NodeAbstractProperty property, const ModelNode &modelNode); void setupWidget(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 8be49060e6..1a52a639a7 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -426,6 +426,17 @@ QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &fami return dataBase.styles(family); } +QStringList PropertyEditorContextObject::allStatesForId(const QString &id) +{ + if (m_model && m_model->rewriterView()) { + const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); + if (node.isValid()) + return node.allStateNames(); + } + + return {}; +} + void EasingCurveEditor::registerDeclarativeType() { qmlRegisterType<EasingCurveEditor>("HelperWidgets", 2, 0, "EasingCurveEditor"); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h index 9e3309ee35..03d82dbc34 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h @@ -94,6 +94,8 @@ public: Q_INVOKABLE QStringList styleNamesForFamily(const QString &family); + Q_INVOKABLE QStringList allStatesForId(const QString &id); + int majorVersion() const; int majorQtQuickVersion() const; int minorQtQuickVersion() const; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index ddded6400b..171046b4ae 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -47,6 +47,7 @@ #include <QApplication> #include <QDir> #include <QFileInfo> +#include <QVector3D> #include <QLoggingCategory> @@ -166,6 +167,18 @@ QVariant properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, return 0; else if (propertyName == "breakPoint") return 50; + else if (propertyName == "transitionType") + return 0; + else if (propertyName == "type") + return 0; + else if (propertyName == "transitionRadius") + return 8; + else if (propertyName == "radius") + return 8; + else if (propertyName == "transitionBezier") + return 50; + else if (propertyName == "bezier") + return 50; else if (propertyName == "customId") return QString(); else if (propertyName == "joinConnection") @@ -235,7 +248,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml propertyNames.append("customId"); if (itemNode.isFlowTransition()) { - propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint"}); + propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint", "type", "radius", "bezier"}); } else if (itemNode.isFlowItem()) { propertyNames.append({"color", "width", "inOffset", "outOffset", "joinConnection"}); } else if (itemNode.isFlowActionArea()) { @@ -245,7 +258,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml } else if (itemNode.isFlowWildcard()) { propertyNames.append({"color", "width", "fillColor", "dash"}); } else if (itemNode.isFlowView()) { - propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor" }); + propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor", "transitionType", "transitionRadius", "transitionBezier"}); } for (const PropertyName &propertyName : propertyNames) { @@ -294,11 +307,25 @@ void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qm void PropertyEditorQmlBackend::setValue(const QmlObjectNode & , const PropertyName &name, const QVariant &value) { - PropertyName propertyName = name; - propertyName.replace('.', '_'); - auto propertyValue = qobject_cast<PropertyEditorValue*>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(propertyName)))); - if (propertyValue) - propertyValue->setValue(value); + if (value.type() == QVariant::Vector3D) { + // Vector3D values need to be split into their subcomponents + const char *suffix[3] = {"_x", "_y", "_z"}; + auto vecValue = value.value<QVector3D>(); + for (int i = 0; i < 3; ++i) { + PropertyName subPropName(name.size() + 2, '\0'); + subPropName.replace(0, name.size(), name); + subPropName.replace(name.size(), 2, suffix[i]); + auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(subPropName)))); + if (propertyValue) + propertyValue->setValue(QVariant(vecValue[i])); + } + } else { + PropertyName propertyName = name; + propertyName.replace('.', '_'); + auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(propertyName)))); + if (propertyValue) + propertyValue->setValue(value); + } } QQmlContext *PropertyEditorQmlBackend::context() { diff --git a/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp index a35ff98ef4..cc82a5607e 100644 --- a/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp +++ b/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp @@ -85,10 +85,25 @@ bool ChangeImportsVisitor::remove(QmlJS::AST::UiProgram *ast, const Import &impo bool ChangeImportsVisitor::equals(QmlJS::AST::UiImport *ast, const Import &import) { + bool equal = false; if (import.isLibraryImport()) - return toString(ast->importUri) == import.url(); + equal = toString(ast->importUri) == import.url(); else if (import.isFileImport()) - return ast->fileName == import.file(); - else - return false; + equal = ast->fileName == import.file(); + + if (equal) { + equal = (!ast->version || (ast->version->minorVersion == 0 && ast->version->majorVersion == 0)) + && import.version().isEmpty(); + if (!equal && ast->version) { + const QStringList versions = import.version().split('.'); + if (versions.size() >= 1 && versions[0].toInt() == ast->version->majorVersion) { + if (versions.size() >= 2) + equal = versions[1].toInt() == ast->version->minorVersion; + else + equal = ast->version->minorVersion == 0; + } + } + } + + return equal; } diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index ef8f1b28ba..d779cc15bf 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -182,8 +182,13 @@ public: const QList<ModelNode> wildcards() const; const QList<ModelNode> decicions() const; QList<ModelNode> transitionsForTarget(const ModelNode &modelNode); + QList<ModelNode> transitionsForSource(const ModelNode &modelNode); void removeDanglingTransitions(); void removeAllTransitions(); + void setStartFlowItem(const QmlFlowItemNode &flowItem); + ModelNode createTransition(); +protected: + QList<ModelNode> transitionsForProperty(const PropertyName &propertyName, const ModelNode &modelNode); }; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstance.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstance.cpp index 453c1b31a4..535350055d 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstance.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstance.cpp @@ -32,6 +32,7 @@ #include <QDebug> #include <QPainter> +#include <QVector3D> QT_BEGIN_NAMESPACE void qt_blurImage(QPainter *painter, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); @@ -306,8 +307,38 @@ int NodeInstance::penWidth() const QVariant NodeInstance::property(const PropertyName &name) const { - if (isValid()) - return d->propertyValues.value(name); + if (isValid()) { + if (d->propertyValues.contains(name)) { + return d->propertyValues.value(name); + } else { + // Query may be for a subproperty, e.g. scale.x + const int index = name.indexOf('.'); + if (index != -1) { + PropertyName parentPropName = name.left(index); + QVariant varValue = d->propertyValues.value(parentPropName); + if (varValue.type() == QVariant::Vector3D) { + auto value = varValue.value<QVector3D>(); + char subProp = name.right(1)[0]; + float subValue = 0.f; + switch (subProp) { + case 'x': + subValue = value.x(); + break; + case 'y': + subValue = value.y(); + break; + case 'z': + subValue = value.z(); + break; + default: + subValue = 0.f; + break; + } + return QVariant(subValue); + } + } + } + } return QVariant(); } @@ -362,6 +393,30 @@ QPair<PropertyName, qint32> NodeInstance::anchor(const PropertyName &name) const void NodeInstance::setProperty(const PropertyName &name, const QVariant &value) { + const int index = name.indexOf('.'); + if (index != -1) { + PropertyName parentPropName = name.left(index); + QVariant oldValue = d->propertyValues.value(parentPropName); + QVector3D newValue; + if (oldValue.type() == QVariant::Vector3D) + newValue = oldValue.value<QVector3D>(); + bool update = false; + if (name.endsWith(".x")) { + newValue.setX(value.toFloat()); + update = true; + } else if (name.endsWith(".y")) { + newValue.setY(value.toFloat()); + update = true; + } else if (name.endsWith(".z")) { + newValue.setZ(value.toFloat()); + update = true; + } + if (update) { + d->propertyValues.insert(parentPropName, newValue); + return; + } + } + d->propertyValues.insert(name, value); } diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index f480e9bb97..6e6660a5cb 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -510,6 +510,8 @@ QProcessEnvironment PuppetCreator::processEnvironment() const customFileSelectors = m_target->additionalData("CustomFileSelectorsData").toStringList(); } + customFileSelectors.append("DesignMode"); + if (m_availablePuppetType == FallbackPuppet) importPaths.prepend(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index ff65187f46..b4b043847e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -420,8 +420,7 @@ void SubComponentManager::parseQuick3DAssetDir(const QString &assetPath) itemLibraryEntry.addHints(hints); } - if (!model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry)) - model()->metaInfo().itemLibraryInfo()->addEntries({itemLibraryEntry}); + model()->metaInfo().itemLibraryInfo()->addEntries({itemLibraryEntry}, true); } } } diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 098f8317a5..e2f0602cc2 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -664,9 +664,7 @@ QList<QmlFlowItemNode> QmlFlowViewNode::flowItems() const ModelNode QmlFlowViewNode::addTransition(const QmlFlowTargetNode &from, const QmlFlowTargetNode &to) { - ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0); - - nodeListProperty("flowTransitions").reparentHere(transition); + ModelNode transition = createTransition(); QmlFlowTargetNode f = from; QmlFlowTargetNode t = to; @@ -684,8 +682,6 @@ const QList<ModelNode> QmlFlowViewNode::transitions() const return modelNode().nodeListProperty("flowTransitions").toModelNodeList(); return {}; - - } const QList<ModelNode> QmlFlowViewNode::wildcards() const @@ -706,13 +702,12 @@ const QList<ModelNode> QmlFlowViewNode::decicions() const QList<ModelNode> QmlFlowViewNode::transitionsForTarget(const ModelNode &modelNode) { - QList<ModelNode> list; - for (const ModelNode &transition : transitions()) { - if (transition.hasBindingProperty("to") - && transition.bindingProperty("to").resolveToModelNode() == modelNode) - list.append(transition); - } - return list; + return transitionsForProperty("to", modelNode); +} + +QList<ModelNode> QmlFlowViewNode::transitionsForSource(const ModelNode &modelNode) +{ + return transitionsForProperty("from", modelNode); } void QmlFlowViewNode::removeDanglingTransitions() @@ -830,4 +825,41 @@ void QmlFlowViewNode::removeAllTransitions() removeProperty("flowTransitions"); } +void QmlFlowViewNode::setStartFlowItem(const QmlFlowItemNode &flowItem) +{ + QTC_ASSERT(flowItem.isValid(), return); + QmlFlowItemNode item = flowItem; + + ModelNode transition; + + for (const ModelNode &node : transitionsForSource(modelNode())) + transition = node; + if (!transition.isValid()) + transition = createTransition(); + + transition.bindingProperty("from").setExpression(modelNode().validId()); + transition.bindingProperty("to").setExpression(item.validId()); +} + +ModelNode QmlFlowViewNode::createTransition() +{ + ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0); + nodeListProperty("flowTransitions").reparentHere(transition); + + return transition; +} + +QList<ModelNode> QmlFlowViewNode::transitionsForProperty(const PropertyName &propertyName, + const ModelNode &modelNode) +{ + QList<ModelNode> list; + for (const ModelNode &transition : transitions()) { + if (transition.hasBindingProperty(propertyName) + && transition.bindingProperty(propertyName).resolveToModelNode() == modelNode) + list.append(transition); + } + return list; + +} + } //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp index a687853604..68738b06de 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp @@ -90,11 +90,12 @@ bool QmlTimeline::hasTimeline(const ModelNode &node, const PropertyName &propert for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(childNode)) { const QmlTimelineKeyframeGroup frames(childNode); - - if (frames.target().isValid() - && frames.target() == node - && frames.propertyName() == propertyName) + if (frames.target().isValid() && frames.target() == node + && (frames.propertyName() == propertyName + || (frames.propertyName().contains('.') + && frames.propertyName().startsWith(propertyName)))) { return true; + } } } } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 41bc1944ca..fd0d9a51ff 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -91,7 +91,12 @@ QStringList globalQtEnums() "Horizontal", "Vertical", "AlignVCenter", "AlignLeft", "LeftToRight", "RightToLeft", "AlignHCenter", "AlignRight", "AlignBottom", "AlignBaseline", "AlignTop", "BottomLeft", "LeftEdge", "RightEdge", "BottomEdge", "TopEdge", "TabFocus", "ClickFocus", "StrongFocus", - "WheelFocus", "NoFocus" + "WheelFocus", "NoFocus", "ArrowCursor", "UpArrowCursor", "CrossCursor", "WaitCursor", + "IBeamCursor", "SizeVerCursor", "SizeHorCursor", "SizeBDiagCursor", "SizeFDiagCursor", + "SizeAllCursor", "BlankCursor", "SplitVCursor", "SplitHCursor", "PointingHandCursor", + "ForbiddenCursor", "WhatsThisCursor", "BusyCursor", "OpenHandCursor", "ClosedHandCursor", + "DragCopyCursor", "DragMoveCursor", "DragLinkCursor", "TopToBottom", + "LeftButton", "RightButton", "MiddleButton", "BackButton", "ForwardButton", "AllButtons" }; return list; @@ -101,7 +106,7 @@ QStringList knownEnumScopes() { static const QStringList list = { "TextInput", "TextEdit", "Material", "Universal", "Font", "Shape", "ShapePath", - "AbstractButton", "Text", "ShaderEffectSource" + "AbstractButton", "Text", "ShaderEffectSource", "Grid" }; return list; } diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index d9db2ea006..e52eabc7bf 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -60,6 +60,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> +#include <utils/stylehelper.h> #include <QSettings> #include <QToolBar> @@ -70,6 +71,7 @@ #include <advanceddockingsystem/dockareawidget.h> #include <advanceddockingsystem/docksplitter.h> +#include <advanceddockingsystem/iconprovider.h> using Core::MiniSplitter; using Core::IEditor; @@ -210,6 +212,13 @@ void DesignModeWidget::disableWidgets() m_isDisabled = true; } +static void addSpacerToToolBar(QToolBar *toolBar) +{ + QWidget* empty = new QWidget(); + empty->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); + toolBar->addWidget(empty); +} + void DesignModeWidget::setup() { auto &actionManager = viewManager().designerActionManager(); @@ -227,6 +236,26 @@ void DesignModeWidget::setup() QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); + // Setup icons + QColor buttonColor(Theme::getColor(Theme::QmlDesigner_TabLight)); // TODO Use correct color roles + QColor tabColor(Theme::getColor(Theme::QmlDesigner_TabDark)); + + const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::adsClose); + const QString menuUnicode = Theme::getIconUnicode(Theme::Icon::adsDropDown); + const QString undockUnicode = Theme::getIconUnicode(Theme::Icon::adsDetach); + + const QString fontName = "qtds_propertyIconFont.ttf"; + const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, tabColor); + const QIcon menuIcon = Utils::StyleHelper::getIconFromIconFont(fontName, menuUnicode, 28, 28, buttonColor); + const QIcon undockIcon = Utils::StyleHelper::getIconFromIconFont(fontName, undockUnicode, 28, 28, buttonColor); + const QIcon closeIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, buttonColor); + + m_dockManager->iconProvider().registerCustomIcon(ADS::TabCloseIcon, tabsCloseIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaMenuIcon, menuIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaUndockIcon, undockIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaCloseIcon, closeIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::FloatingWidgetCloseIcon, closeIcon); + // Setup Actions and Menus Core::ActionContainer *mwindow = Core::ActionManager::actionContainer(Core::Constants::M_WINDOW); // Window > Views @@ -344,6 +373,7 @@ void DesignModeWidget::setup() // Create toolbars auto toolBar = new QToolBar(); + toolBar->addAction(viewManager().componentViewAction()); toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar); @@ -385,6 +415,8 @@ void DesignModeWidget::setup() } }); + addSpacerToToolBar(toolBar); + auto workspaceComboBox = new QComboBox(); workspaceComboBox->setMinimumWidth(120); workspaceComboBox->setToolTip(tr("Switch the active workspace.")); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a65ff3600b..21d1ffadf2 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -38,6 +38,7 @@ #include <sourcetool/sourcetool.h> #include <colortool/colortool.h> #include <annotationeditor/annotationtool.h> +#include <formeditor/transitiontool.h> #include <texttool/texttool.h> #include <timelineeditor/timelineview.h> #include <pathtool/pathtool.h> @@ -216,6 +217,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool()) GenerateResource::generateMenuEntry(); + QString fontPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf"); + if (QFontDatabase::addApplicationFont(fontPath) < 0) + qCWarning(qmldesignerLog) << "Could not add font " << fontPath << "to font database"; + return true; } @@ -242,6 +248,7 @@ bool QmlDesignerPlugin::delayedInitialize() d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TextTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::PathTool); + d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TransitionTool); return true; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 3c434788cd..c69f5e5c22 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -530,6 +530,8 @@ Project { "formeditor/toolbox.h", "formeditor/formeditortoolbutton.cpp", "formeditor/formeditortoolbutton.h", + "formeditor/transitiontool.cpp", + "formeditor/transitiontool.h", "importmanager/importlabel.cpp", "importmanager/importlabel.h", "importmanager/importmanagercombobox.cpp", |