diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml index 7481d242252..7ae7bd44d91 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 207dc941994..20dd112d614 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 db8d4fb4eab..6527d06e6e7 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 b16b27406d9..afaf8481ca6 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 f99d219c5c5..f62a7e51736 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 e9d8e525458..15795ac28bd 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/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 37f97126785..cd3ddef34db 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 4eb7420d740..5434e4705bf 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 00000000000..787e390cbde --- /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/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml index 3d39d44e8fb..362ba144c56 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/GridSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml index 37be5e57701..284849a4158 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 8f742f06e24..60b97a11796 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/MouseAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml index 71ff0260ba0..0ca7f3d888d 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/TextInputSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml index cfd972b0854..5c0e84d039b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml @@ -113,6 +113,19 @@ Section { minimumValue: -200 } + 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/FlickableSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml index 2b97df06467..11f46d29c52 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" } @@ -166,6 +166,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") } @@ -246,6 +263,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 a97923a032c..94a43424dac 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) + useString: true } 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 6ecc8d525ce..6c5e6fde867 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 { @@ -77,6 +77,21 @@ Section { model: ["ElideNone", "ElideLeft", "ElideMiddle", "ElideRight"] } + 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/StudioTheme/Constants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml index 3d692af671c..74edf9ccee8 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 00000000000..4d45032eae9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 alignBottom: "\u002C" + readonly property string alignCenterHorizontal: "\u002D" + readonly property string alignCenterVertical: "\u002E" + readonly property string alignLeft: "\u002F" + readonly property string alignRight: "\u0030" + readonly property string alignTo: "\u0031" + readonly property string alignTop: "\u0032" + readonly property string anchorBaseline: "\u0033" + readonly property string anchorBottom: "\u0034" + readonly property string anchorFill: "\u0035" + readonly property string anchorLeft: "\u0036" + readonly property string anchorRight: "\u0037" + readonly property string anchorTop: "\u0038" + readonly property string annotationBubble: "\u0039" + readonly property string annotationDecal: "\u003A" + readonly property string centerHorizontal: "\u003B" + readonly property string centerVertical: "\u003C" + readonly property string closeCross: "\u003D" + readonly property string decisionNode: "\u003E" + readonly property string deleteColumn: "\u003F" + readonly property string deleteRow: "\u0040" + readonly property string deleteTable: "\u0041" + readonly property string detach: "\u0042" + readonly property string distributeBottom: "\u0043" + readonly property string distributeCenterHorizontal: "\u0044" + readonly property string distributeCenterVertical: "\u0045" + readonly property string distributeLeft: "\u0046" + readonly property string distributeOriginBottomRight: "\u0047" + readonly property string distributeOriginCenter: "\u0048" + readonly property string distributeOriginNone: "\u0049" + readonly property string distributeOriginTopLeft: "\u004A" + readonly property string distributeRight: "\u004B" + readonly property string distributeSpacingHorizontal: "\u004C" + readonly property string distributeSpacingVertical: "\u004D" + readonly property string distributeTop: "\u004E" + readonly property string edit: "\u004F" + readonly property string fontStyleBold: "\u0050" + readonly property string fontStyleItalic: "\u0051" + readonly property string fontStyleStrikethrough: "\u0052" + readonly property string fontStyleUnderline: "\u0053" + readonly property string mergeCells: "\u0054" + readonly property string redo: "\u0055" + readonly property string splitColumns: "\u0056" + readonly property string splitRows: "\u0057" + readonly property string startNode: "\u0058" + readonly property string testIcon: "\u0059" + readonly property string textAlignBottom: "\u005A" + readonly property string textAlignCenter: "\u005B" + readonly property string textAlignLeft: "\u005C" + readonly property string textAlignMiddle: "\u005D" + readonly property string textAlignRight: "\u005E" + readonly property string textAlignTop: "\u005F" + readonly property string textBulletList: "\u0060" + readonly property string textFullJustification: "\u0061" + readonly property string textNumberedList: "\u0062" + readonly property string tickIcon: "\u0063" + readonly property string triState: "\u0064" + readonly property string undo: "\u0065" + readonly property string upDownIcon: "\u0066" + readonly property string upDownSquare2: "\u0067" + readonly property string wildcard: "\u0068" + + 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 index d65b0651952..9e3ed424062 100644 Binary files a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf and b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf differ diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index b768fe63a2b..4f689f9f630 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 4c0b8d6896a..e61bfa7ceb4 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 9ee647265d9..79ba7ae70d5 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 611580d3685..b46d1720509 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 6a6f46752bb..f220418220d 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 64e71a3b1c3..7d92952d700 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/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index 36737ca1a28..36a92a7099d 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -22,6 +22,7 @@ add_qtc_library(Sqlite sqlitetable.h sqlitetransaction.h sqlitewritestatement.cpp sqlitewritestatement.h + sqlitevalue.h sqlstatementbuilder.cpp sqlstatementbuilder.h sqlstatementbuilderexception.h utf8string.cpp utf8string.h diff --git a/src/libs/sqlite/sqlite-lib.pri b/src/libs/sqlite/sqlite-lib.pri index 4d7f906a199..1ce343b6247 100644 --- a/src/libs/sqlite/sqlite-lib.pri +++ b/src/libs/sqlite/sqlite-lib.pri @@ -32,6 +32,7 @@ HEADERS += \ $$PWD/sqlitereadstatement.h \ $$PWD/sqlitereadwritestatement.h \ $$PWD/sqlitetransaction.h \ + $$PWD/sqlitevalue.h \ $$PWD/sqlitewritestatement.h \ $$PWD/sqlstatementbuilder.h \ $$PWD/sqlstatementbuilderexception.h \ diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 87aa3f68b37..85a6db2ddcd 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -190,6 +190,21 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) checkForBindingError(resultCode); } +void BaseStatement::bind(int index, const Value &value) +{ + switch (value.type()) { + case ValueType::Integer: + bind(index, value.toInteger()); + break; + case ValueType::Float: + bind(index, value.toFloat()); + break; + case ValueType::String: + bind(index, value.toStringView()); + break; + } +} + template void BaseStatement::bind(Utils::SmallStringView name, Type value) { @@ -498,12 +513,34 @@ StringType BaseStatement::fetchValue(int column) const return convertToTextForColumn(m_compiledStatement.get(), column); } +template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue( + int column) const; +template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue( + int column) const; +template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue( + int column) const; + Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const { return fetchValue(column); } -template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue(int column) const; -template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue(int column) const; -template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue(int column) const; +ValueView BaseStatement::fetchValueView(int column) const +{ + int dataType = sqlite3_column_type(m_compiledStatement.get(), column); + switch (dataType) { + case SQLITE_INTEGER: + return ValueView::create(fetchLongLongValue(column)); + case SQLITE_FLOAT: + return ValueView::create(fetchDoubleValue(column)); + case SQLITE3_TEXT: + return ValueView::create(fetchValue(column)); + case SQLITE_BLOB: + case SQLITE_NULL: + break; + } + + return ValueView::create(0LL); +} + } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 86d03fd8553..aa40e0c6ea2 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -28,6 +28,7 @@ #include "sqliteglobal.h" #include "sqliteexception.h" +#include "sqlitevalue.h" #include @@ -67,6 +68,7 @@ public: long long fetchLongLongValue(int column) const; double fetchDoubleValue(int column) const; Utils::SmallStringView fetchSmallStringViewValue(int column) const; + ValueView fetchValueView(int column) const; template Type fetchValue(int column) const; int columnCount() const; @@ -76,11 +78,9 @@ public: void bind(int index, long long fetchValue); void bind(int index, double fetchValue); void bind(int index, Utils::SmallStringView fetchValue); + void bind(int index, const Value &fetchValue); - void bind(int index, uint value) - { - bind(index, static_cast(value)); - } + void bind(int index, uint value) { bind(index, static_cast(value)); } void bind(int index, long value) { @@ -345,34 +345,16 @@ private: struct ValueGetter { ValueGetter(StatementImplementation &statement, int column) - : statement(statement), - column(column) + : statement(statement) + , column(column) {} - operator int() - { - return statement.fetchIntValue(column); - } - - operator long() - { - return statement.fetchLongValue(column); - } - - operator long long() - { - return statement.fetchLongLongValue(column); - } - - operator double() - { - return statement.fetchDoubleValue(column); - } - - operator Utils::SmallStringView() - { - return statement.fetchSmallStringViewValue(column); - } + operator int() { return statement.fetchIntValue(column); } + operator long() { return statement.fetchLongValue(column); } + operator long long() { return statement.fetchLongLongValue(column); } + operator double() { return statement.fetchDoubleValue(column); } + operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); } + operator ValueView() { return statement.fetchValueView(column); } StatementImplementation &statement; int column; diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h index 8edfd984ef4..6f898504a4c 100644 --- a/src/libs/sqlite/sqliteexception.h +++ b/src/libs/sqlite/sqliteexception.h @@ -264,4 +264,12 @@ public: } }; +class CannotConvert : public Exception +{ +public: + CannotConvert(const char *whatErrorHasHappen) + : Exception(whatErrorHasHappen) + {} +}; + } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h new file mode 100644 index 00000000000..ca576421777 --- /dev/null +++ b/src/libs/sqlite/sqlitevalue.h @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** 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 "sqliteexception.h" + +#include +#include + +#include + +#pragma once + +namespace Sqlite { + +enum class ValueType : unsigned char { Integer, Float, String }; + +template +class ValueBase +{ +public: + using VariantType = Utils::variant; + + explicit ValueBase(VariantType &&value) + : value(value) + {} + + explicit ValueBase(const char *value) + : value(Utils::SmallStringView{value}) + {} + + explicit ValueBase(long long value) + : value(value) + {} + explicit ValueBase(int value) + : value(static_cast(value)) + {} + + explicit ValueBase(uint value) + : value(static_cast(value)) + {} + + explicit ValueBase(double value) + : value(value) + {} + + explicit ValueBase(Utils::SmallStringView value) + : value(value) + + {} + + long long toInteger() const { return Utils::get(value); } + + double toFloat() const { return Utils::get(value); } + + Utils::SmallStringView toStringView() const + { + return Utils::get(value); + } + + explicit operator QVariant() const + { + switch (type()) { + case ValueType::Integer: + return QVariant(toInteger()); + case ValueType::Float: + return QVariant(toFloat()); + case ValueType::String: + return QVariant(QString(toStringView())); + } + + return {}; + } + + friend bool operator==(const ValueBase &first, long long second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(long long first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, double second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(const ValueBase &first, int second) + { + return first == static_cast(second); + } + + friend bool operator==(int first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, uint second) + { + return first == static_cast(second); + } + + friend bool operator==(uint first, const ValueBase &second) { return second == first; } + + friend bool operator==(double first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, Utils::SmallStringView second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(Utils::SmallStringView first, const ValueBase &second) + { + return second == first; + } + + friend bool operator==(const ValueBase &first, const QString &second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger + && second == QLatin1String{maybeInteger->data(), int(maybeInteger->size())}; + } + + friend bool operator==(const QString &first, const ValueBase &second) + { + return second == first; + } + + friend bool operator==(const ValueBase &first, const char *second) + { + return first == Utils::SmallStringView{second}; + } + + friend bool operator==(const char *first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, const ValueBase &second) + { + return first.value == second.value; + } + + friend bool operator!=(const ValueBase &first, const ValueBase &second) + { + return !(first.value == second.value); + } + + ValueType type() const + { + switch (value.index()) { + case 0: + return ValueType::Integer; + case 1: + return ValueType::Float; + case 2: + return ValueType::String; + } + + return {}; + } + +public: + VariantType value; +}; + +class ValueView : public ValueBase +{ +public: + explicit ValueView(ValueBase &&base) + : ValueBase(std::move(base)) + {} + + template + static ValueView create(Type &&value) + { + return ValueView{ValueBase{value}}; + } +}; + +class Value : public ValueBase +{ + using Base = ValueBase; + +public: + using Base::Base; + + explicit Value(ValueView view) + : ValueBase(convert(view)) + {} + + explicit Value(const QVariant &value) + : ValueBase(convert(value)) + {} + + explicit Value(Utils::SmallString &&value) + : ValueBase(VariantType{std::move(value)}) + {} + + explicit Value(const Utils::SmallString &value) + : ValueBase(Utils::SmallStringView(value)) + {} + + explicit Value(const QString &value) + : ValueBase(VariantType{Utils::SmallString(value)}) + {} + + friend bool operator!=(const Value &first, const Value &second) + { + return !(first.value == second.value); + } + + template + friend bool operator!=(const Value &first, const Type &second) + { + return !(first == second); + } + + template + friend bool operator!=(const Type &first, const Value &second) + { + return !(first == second); + } + + friend bool operator==(const Value &first, const ValueView &second) + { + if (first.type() != second.type()) + return false; + + switch (first.type()) { + case ValueType::Integer: + return first.toInteger() == second.toInteger(); + case ValueType::Float: + return first.toFloat() == second.toFloat(); + case ValueType::String: + return first.toStringView() == second.toStringView(); + } + + return false; + } + + friend bool operator==(const ValueView &first, const Value &second) { return second == first; } + +private: + static Base::VariantType convert(const QVariant &value) + { + switch (value.type()) { + case QVariant::Int: + return VariantType{static_cast(value.toInt())}; + case QVariant::LongLong: + return VariantType{value.toLongLong()}; + case QVariant::UInt: + return VariantType{static_cast(value.toUInt())}; + case QVariant::Double: + return VariantType{value.toFloat()}; + case QVariant::String: + return VariantType{value.toString()}; + default: + throw CannotConvert("Cannot convert this QVariant to a ValueBase"); + } + } + + static Base::VariantType convert(ValueView view) + { + switch (view.type()) { + case ValueType::Integer: + return VariantType{view.toInteger()}; + case ValueType::Float: + return VariantType{view.toFloat()}; + case ValueType::String: + return VariantType{view.toStringView()}; + default: + throw CannotConvert("Cannot convert this QVariant to a ValueBase"); + } + } +}; + +using Values = std::vector; +} // namespace Sqlite diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index f91c24b52ac..68d40accbc6 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 + #include #include #include @@ -35,6 +37,7 @@ #include #include #include +#include #include // 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 4bcec960ad5..09f32534352 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 availableImageResolutions(const QString &fileName); diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 9c32da72c41..d68085cdfc3 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 @@ -558,6 +559,7 @@ extend_qtc_plugin(QmlDesigner annotationeditordialog.cpp annotationeditordialog.h annotationeditordialog.ui annotationeditor.cpp annotationeditor.h annotationtool.cpp annotationtool.h + globalannotationeditor.cpp globalannotationeditor.h ) extend_qtc_plugin(QmlDesigner @@ -591,6 +593,13 @@ extend_qtc_plugin(QmlDesigner texttool/texttool.cpp texttool/texttool.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/richtexteditor + SOURCES + hyperlinkdialog.cpp hyperlinkdialog.h hyperlinkdialog.ui + richtexteditor.cpp richtexteditor.h hyperlinkdialog.ui +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/timelineeditor SOURCES diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp index bd7aed67d02..bfe18e952b8 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp @@ -26,6 +26,8 @@ #include "annotationcommenttab.h" #include "ui_annotationcommenttab.h" +#include "richtexteditor/richtexteditor.h" + namespace QmlDesigner { AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) : @@ -34,6 +36,9 @@ AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) : { ui->setupUi(this); + m_editor = new RichTextEditor; + ui->formLayout->setWidget(3, QFormLayout::FieldRole, m_editor); + connect(ui->titleEdit, &QLineEdit::textEdited, this, &AnnotationCommentTab::commentTitleChanged); } @@ -49,7 +54,7 @@ Comment AnnotationCommentTab::currentComment() const result.setTitle(ui->titleEdit->text().trimmed()); result.setAuthor(ui->authorEdit->text().trimmed()); - result.setText(ui->textEdit->toPlainText().trimmed()); + result.setText(m_editor->richText().trimmed()); if (m_comment.sameContent(result)) result.setTimestamp(m_comment.timestamp()); @@ -74,7 +79,7 @@ void AnnotationCommentTab::resetUI() { ui->titleEdit->setText(m_comment.title()); ui->authorEdit->setText(m_comment.author()); - ui->textEdit->setText(m_comment.text()); + m_editor->setRichText(m_comment.text()); if (m_comment.timestamp() > 0) ui->timeLabel->setText(m_comment.timestampStr()); diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h index cc8d4c3d769..55fcf6ff1ed 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h @@ -35,6 +35,8 @@ namespace Ui { class AnnotationCommentTab; } +class RichTextEditor; + class AnnotationCommentTab : public QWidget { Q_OBJECT @@ -59,6 +61,7 @@ private slots: private: Ui::AnnotationCommentTab *ui; + RichTextEditor *m_editor; Comment m_comment; }; diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui index f6bf277eb7c..4a69703d57d 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui @@ -33,13 +33,6 @@ - - - - true - - - @@ -64,7 +57,6 @@ titleEdit authorEdit - textEdit diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri index e221b3bb615..9a3a00e2da3 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri @@ -2,11 +2,13 @@ HEADERS += $$PWD/annotationtool.h HEADERS += $$PWD/annotationcommenttab.h HEADERS += $$PWD/annotationeditordialog.h HEADERS += $$PWD/annotationeditor.h +HEADERS += $$PWD/globalannotationeditor.h SOURCES += $$PWD/annotationtool.cpp SOURCES += $$PWD/annotationcommenttab.cpp SOURCES += $$PWD/annotationeditordialog.cpp SOURCES += $$PWD/annotationeditor.cpp +SOURCES += $$PWD/globalannotationeditor.cpp FORMS += $$PWD/annotationcommenttab.ui FORMS += $$PWD/annotationeditordialog.ui diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp index dd234a074cb..a25d93b666a 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp @@ -40,16 +40,16 @@ namespace QmlDesigner { -AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation) +AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation, EditorMode mode) : QDialog(parent) , ui(new Ui::AnnotationEditorDialog) , m_customId(customId) , m_annotation(annotation) + , m_editorMode(mode) { ui->setupUi(this); setWindowFlag(Qt::Tool, true); - setWindowTitle(titleString); setModal(true); connect(this, &QDialog::accepted, this, &AnnotationEditorDialog::acceptedClicked); @@ -98,6 +98,7 @@ AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &t ui->tabWidget->setCornerWidget(commentCornerWidget, Qt::TopRightCorner); ui->targetIdEdit->setText(targetId); + changeEditorMode(m_editorMode); fillFields(); } @@ -128,6 +129,39 @@ QString AnnotationEditorDialog::customId() const return m_customId; } +void AnnotationEditorDialog::changeEditorMode(AnnotationEditorDialog::EditorMode mode) +{ + switch (mode) { + case ItemAnnotation: { + ui->customIdEdit->setVisible(true); + ui->customIdLabel->setVisible(true); + ui->targetIdEdit->setVisible(true); + ui->targetIdLabel->setVisible(true); + setWindowTitle(annotationEditorTitle); + + break; + } + case GlobalAnnotation: { + ui->customIdEdit->clear(); + ui->targetIdEdit->clear(); + ui->customIdEdit->setVisible(false); + ui->customIdLabel->setVisible(false); + ui->targetIdEdit->setVisible(false); + ui->targetIdLabel->setVisible(false); + setWindowTitle(globalEditorTitle); + + break; + } + } + + m_editorMode = mode; +} + +AnnotationEditorDialog::EditorMode AnnotationEditorDialog::editorMode() const +{ + return m_editorMode; +} + void AnnotationEditorDialog::acceptedClicked() { m_customId = ui->customIdEdit->text(); diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h index 1324115a724..0c3cb50c807 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h @@ -40,7 +40,10 @@ class AnnotationEditorDialog : public QDialog Q_OBJECT public: - explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation); + enum EditorMode { ItemAnnotation, GlobalAnnotation }; + + explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation, + EditorMode mode = EditorMode::ItemAnnotation); ~AnnotationEditorDialog(); void setAnnotation(const Annotation &annotation); @@ -49,6 +52,9 @@ public: void setCustomId(const QString &customId); QString customId() const; + void changeEditorMode(EditorMode mode); + EditorMode editorMode() const; + signals: void accepted(); @@ -68,12 +74,15 @@ private: void deleteAllTabs(); private: - const QString titleString = {tr("Annotation Editor")}; + const QString annotationEditorTitle = {tr("Annotation Editor")}; + const QString globalEditorTitle = {tr("Global Annotation Editor")}; const QString defaultTabName = {tr("Annotation")}; Ui::AnnotationEditorDialog *ui; QString m_customId; Annotation m_annotation; + + EditorMode m_editorMode; }; } //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui index 8ca9b75136c..93ce92b0450 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui @@ -6,8 +6,8 @@ 0 0 - 700 - 487 + 1100 + 600 diff --git a/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp new file mode 100644 index 00000000000..1beeacaf74c --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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 "globalannotationeditor.h" + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "qmlmodelnodeproxy.h" +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +GlobalAnnotationEditor::GlobalAnnotationEditor(QObject *) +{ +} + +GlobalAnnotationEditor::~GlobalAnnotationEditor() +{ + hideWidget(); +} + +void GlobalAnnotationEditor::showWidget() +{ + m_dialog = new AnnotationEditorDialog(Core::ICore::dialogParent(), + modelNode().validId(), + "", + modelNode().globalAnnotation(), + AnnotationEditorDialog::EditorMode::GlobalAnnotation); + + QObject::connect(m_dialog, &AnnotationEditorDialog::accepted, + this, &GlobalAnnotationEditor::acceptedClicked); + QObject::connect(m_dialog, &AnnotationEditorDialog::rejected, + this, &GlobalAnnotationEditor::cancelClicked); + + m_dialog->setAttribute(Qt::WA_DeleteOnClose); + + m_dialog->show(); + m_dialog->raise(); +} + +void GlobalAnnotationEditor::showWidget(int x, int y) +{ + showWidget(); + m_dialog->move(x, y); +} + +void GlobalAnnotationEditor::hideWidget() +{ + if (m_dialog) + m_dialog->close(); + m_dialog = nullptr; +} + +void GlobalAnnotationEditor::setModelNode(const ModelNode &modelNode) +{ + m_modelNode = modelNode; +} + +ModelNode GlobalAnnotationEditor::modelNode() const +{ + return m_modelNode; +} + +bool GlobalAnnotationEditor::hasAnnotation() const +{ + if (m_modelNode.isValid()) + return m_modelNode.hasGlobalAnnotation(); + return false; +} + +void GlobalAnnotationEditor::removeFullAnnotation() +{ + if (!m_modelNode.isValid()) + return; + + QString dialogTitle = tr("Global Annotation"); + QMessageBox *deleteDialog = new QMessageBox(Core::ICore::dialogParent()); + deleteDialog->setWindowTitle(dialogTitle); + deleteDialog->setText(tr("Delete this annotation?")); + deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + deleteDialog->setDefaultButton(QMessageBox::Yes); + + int result = deleteDialog->exec(); + if (deleteDialog) deleteDialog->deleteLater(); + + if (result == QMessageBox::Yes) { + m_modelNode.removeGlobalAnnotation(); + } + + emit annotationChanged(); +} + +void GlobalAnnotationEditor::acceptedClicked() +{ + if (m_dialog) { + Annotation annotation = m_dialog->annotation(); + + if (annotation.comments().isEmpty()) + m_modelNode.removeGlobalAnnotation(); + else + m_modelNode.setGlobalAnnotation(annotation); + } + + hideWidget(); + + emit accepted(); + + emit annotationChanged(); +} + +void GlobalAnnotationEditor::cancelClicked() +{ + hideWidget(); + + emit canceled(); + + emit annotationChanged(); +} + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h new file mode 100644 index 00000000000..e0aa66a1193 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "modelnode.h" + +namespace QmlDesigner { + +class GlobalAnnotationEditor : public QObject +{ + Q_OBJECT + +public: + explicit GlobalAnnotationEditor(QObject *parent = nullptr); + ~GlobalAnnotationEditor(); + + Q_INVOKABLE void showWidget(); + Q_INVOKABLE void showWidget(int x, int y); + Q_INVOKABLE void hideWidget(); + + void setModelNode(const ModelNode &modelNode); + ModelNode modelNode() const; + + Q_INVOKABLE bool hasAnnotation() const; + + Q_INVOKABLE void removeFullAnnotation(); + +signals: + void accepted(); + void canceled(); + void modelNodeBackendChanged(); + + void annotationChanged(); + +private slots: + void acceptedClicked(); + void cancelClicked(); + +private: + QPointer m_dialog; + + ModelNode m_modelNode; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 2965bcd90b3..c0f7af85c26 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -41,6 +41,7 @@ const char anchorsCategory[] = "Anchors"; const char positionCategory[] = "Position"; 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 +58,7 @@ const char anchorsFillCommandId[] = "AnchorsFill"; const char anchorsResetCommandId[] = "AnchorsReset"; const char removePositionerCommandId[] = "RemovePositioner"; const char createFlowActionAreaCommandId[] = "CreateFlowActionArea"; +const char setFlowStartCommandId[] = "SetFlowStart"; const char layoutRowPositionerCommandId[] = "LayoutRowPositioner"; const char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner"; const char layoutGridPositionerCommandId[] = "LayoutGridPositioner"; @@ -85,6 +87,7 @@ const char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextM const char positionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position"); 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,6 +127,7 @@ 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 addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Item"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 42f95952db1..cf3fb5e1524 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -346,6 +346,12 @@ 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() @@ -362,9 +368,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 @@ -853,15 +859,24 @@ void DesignerActionManager::createDefaultDesignerActions() priorityLayoutCategory, &layoutOptionVisible)); - //isFlowTransitionItem - 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,6 +889,17 @@ void DesignerActionManager::createDefaultDesignerActions() &isFlowItem, &flowOptionVisible)); + addDesignerAction(new ModelNodeContextMenuAction( + setFlowStartCommandId, + setFlowStartDisplayName, + {}, + flowCategory, + priorityFirst, + {}, + &setFlowStartItem, + &isFlowItem, + &flowOptionVisible)); + addDesignerAction(new FlowActionConnectAction( flowConnectionCategoryDisplayName, flowConnectionCategory, @@ -1175,7 +1201,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) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 67a53ef9972..59029400e5a 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 e00418fec8b..cf148776de2 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -295,8 +295,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"); + } } }); } @@ -309,8 +311,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"); + } } }); } @@ -332,7 +336,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"); } }); } @@ -1079,7 +1084,24 @@ 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); + }); } } // namespace Mode diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 994110297e0..57fd0ea12ac 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -77,6 +77,7 @@ 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); } // namespace ModelNodeOperationso } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index ec4128479fe..29b937a502a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -28,19 +28,46 @@ #include +#include + #include #include #include #include #include +#include +#include +#include #include +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 6940c0c1cf0..07ee4caca8a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -41,12 +41,91 @@ 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, + alignBottom, + alignCenterHorizontal, + alignCenterVertical, + alignLeft, + alignRight, + alignTo, + alignTop, + anchorBaseline, + anchorBottom, + anchorFill, + anchorLeft, + anchorRight, + anchorTop, + annotationBubble, + annotationDecal, + centerHorizontal, + centerVertical, + 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 +137,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 c8e1e222e54..9e882be2fde 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 &addedImports, checkImports(); } +void Edit3DView::customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &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 Edit3DView::rightActions() const void Edit3DView::addQuick3DImport() { - const QList imports = model()->possibleImports(); - for (const auto &import : imports) { - if (import.url() == "QtQuick3D") { - model()->changeImports({import}, {}); + if (model()) { + const QList 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; + // 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 d1646470b75..6c3ae892a91 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 &addedImports, const QList &removedImports) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &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 c9482272335..3d0d3f2f640 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!

" - "To create a 3D View you need to add the QtQuick3D import to your file.
" - "You can add the import via the QML Imports tab of the Library view, or alternatively click" - " here " - "to add it straight away.

" - "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.

" + "To create a 3D view, add the QtQuick3D import to your file in the QML Imports tab of the Library view. Or click" + " here " + "here to add it immediately.

" + "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 3464ba3afe4..4609b277f90 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/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp index d069eaeaf86..220c7634712 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp @@ -339,7 +339,7 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const authorItem->update(); QGraphicsTextItem *textItem = new QGraphicsTextItem(frameItem); - textItem->setPlainText(text); + textItem->setHtml(text); textItem->setDefaultTextColor(textColor); textItem->setTextWidth(rect.width()); textItem->setPos(authorItem->x(), authorItem->boundingRect().height() + authorItem->y() + 5); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 75f08df1bf1..048a60a3584 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -32,10 +32,13 @@ #include #include +#include + #include #include #include +#include #include #include #include @@ -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(QGraphicsItem::scene()); @@ -578,6 +611,7 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi return; painter->save(); + painter->setRenderHint(QPainter::Antialiasing); QPen pen; pen.setJoinStyle(Qt::MiterJoin); @@ -621,10 +655,9 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value(); if (fillColor.alpha() > 0) - painter->fillRect(boundingRect(), fillColor); - - painter->drawRect(boundingRect()); + painter->setBrush(fillColor); + painter->drawRoundedRect(boundingRect(), blockRadius, blockRadius); painter->restore(); } @@ -989,6 +1022,7 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi return; painter->save(); + painter->setRenderHint(QPainter::Antialiasing); ResolveConnection resolved(qmlItemNode()); @@ -1021,8 +1055,8 @@ 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()); @@ -1075,23 +1109,28 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint")) breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt(); + if (resolved.isStartLine) + fromRect.translate(0, inOffset); + paintConnection(painter, fromRect, toRect, width, adjustedWidth ,color, dash, outOffset, inOffset, breakOffset); if (resolved.isStartLine) { + + const QString icon = Theme::getIconUnicode(Theme::startNode); + QPen pen; pen.setCosmetic(true); - pen.setColor(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, color); } painter->restore(); @@ -1140,6 +1179,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); @@ -1177,20 +1219,37 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap if (qmlItemNode().modelNode().hasAuxiliaryData("fillColor")) fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value(); + 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->drawLine(boundingRect().right(), boundingRect().center().y(), - boundingRect().center().x(), boundingRect().bottom()); + painter->drawRoundedRect(adjustedRect, radius, radius); - painter->drawLine(boundingRect().center().x(), boundingRect().bottom(), - boundingRect().left(), boundingRect().center().y()); + const int iconDecrement = 32; + const int iconSize = adjustedRect.width() - iconDecrement; + const int offset = iconDecrement / 2 + blockAdjust; + + painter->restore(); + + const QString icon = (m_iconType == + WildcardIcon) ? Theme::getIconUnicode(Theme::wildcard) + : Theme::getIconUnicode(Theme::decisionNode); + + 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 d725afd0b05..91dcad44a42 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -207,9 +207,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 +229,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/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp new file mode 100644 index 00000000000..e8222e8ac8a --- /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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +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 &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 & 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 & itemList, + QGraphicsSceneMouseEvent *event) +{ + mouseMoveEvent(itemList, event); +} + +void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/) +{ +} + +void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ + view()->changeToSelectionTool(); +} + +void TransitionTool::dragLeaveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::dragMoveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::mouseReleaseEvent(const QList &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 &itemList, QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event); +} + +void TransitionTool::itemsAboutToRemoved(const QList &) +{ + view()->changeCurrentToolTo(this); +} + +void TransitionTool::selectedItemsChanged(const QList &itemList) +{ + if (!itemList.isEmpty()) { + createItems(); + + m_formEditorItem = itemList.first(); + setToBoundingRect(rectangleItem1(), m_formEditorItem); + } +} + +void TransitionTool::instancesCompleted(const QList & /*itemList*/) +{ +} + +void TransitionTool::instancesParentChanged(const QList & /*itemList*/) +{ +} + +void TransitionTool::instancePropertyChange(const QList > & /*propertyList*/) +{ +} + +void TransitionTool::formEditorItemsChanged(const QList & /*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 00000000000..43c3894933e --- /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 +#include +#include + +#include + +namespace QmlDesigner { + +class TransitionTool : public QObject, public AbstractCustomTool +{ + Q_OBJECT +public: + TransitionTool(); + ~TransitionTool(); + + void mousePressEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseDoubleClickEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + + void dragLeaveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + void dragMoveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + + void itemsAboutToRemoved(const QList &itemList) override; + + void selectedItemsChanged(const QList &itemList) override; + + void instancesCompleted(const QList &itemList) override; + void instancesParentChanged(const QList &itemList) override; + void instancePropertyChange(const QList > &propertyList) override; + + void clear() override; + + void formEditorItemsChanged(const QList &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 m_lineItem; + std::unique_ptr m_rectangleItem1; + std::unique_ptr 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 df9590af646..c2bce0b0ce5 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 &/*addedImports*/, const QList &/*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 &/*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 3f403d03133..0beb5bce729 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 +#include + #include #include #include @@ -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 34fa71222d6..6a5edb18a39 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 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/navigator/navigatormodelinterface.h b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h index bedcc2f561a..d560eb2824a 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 &modelNodes) = 0; virtual void notifyModelNodesInserted(const QList &modelNodes) = 0; virtual void notifyModelNodesMoved(const QList &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 2e7a466a407..6370d2bfd43 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 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 f10198adcc0..15e89d36367 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 &modelNodes) override; void notifyModelNodesInserted(const QList &modelNodes) override; void notifyModelNodesMoved(const QList &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 e345052172a..832e57d069c 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 & prop } } +void NavigatorView::customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &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(); @@ -434,7 +445,7 @@ void NavigatorView::updateItemSelection() // make sure selected nodes a visible foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) { if (selectedIndex.column() == 0) - expandRecursively(selectedIndex); + expandAncestors(selectedIndex); } } @@ -458,9 +469,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 852dddc70fc..3bafe0fa80b 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 &propertyList, PropertyChangeFlags) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &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/richtexteditor/hyperlinkdialog.cpp b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp new file mode 100644 index 00000000000..82d24f6839e --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** 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 "hyperlinkdialog.h" +#include "ui_hyperlinkdialog.h" + +#include + +namespace QmlDesigner { + + +HyperlinkDialog::HyperlinkDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::HyperlinkDialog) +{ + ui->setupUi(this); + connect (ui->linkEdit, &QLineEdit::textChanged, [this] () { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!(ui->linkEdit->text().isEmpty())); + }); +} + +HyperlinkDialog::~HyperlinkDialog() +{ + delete ui; +} + +QString HyperlinkDialog::getLink() const +{ + return ui->linkEdit->text().trimmed(); +} + +void HyperlinkDialog::setLink(const QString &link) +{ + ui->linkEdit->setText(link); +} + +QString HyperlinkDialog::getAnchor() const +{ + return ui->anchorEdit->text().trimmed(); +} + +void HyperlinkDialog::setAnchor(const QString &anchor) +{ + ui->anchorEdit->setText(anchor); +} + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h new file mode 100644 index 00000000000..dcf8759e492 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 + +namespace QmlDesigner { + +namespace Ui { +class HyperlinkDialog; +} + +class HyperlinkDialog : public QDialog +{ + Q_OBJECT + +public: + explicit HyperlinkDialog(QWidget *parent = nullptr); + ~HyperlinkDialog(); + + QString getLink() const; + void setLink(const QString &link); + + QString getAnchor() const; + void setAnchor(const QString &anchor); + +private: + Ui::HyperlinkDialog *ui; +}; + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui new file mode 100644 index 00000000000..7906a1fccb1 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui @@ -0,0 +1,88 @@ + + + QmlDesigner::HyperlinkDialog + + + + 0 + 0 + 403 + 156 + + + + Dialog + + + + + + + + Link + + + + + + + + + + Anchor + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QmlDesigner::HyperlinkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QmlDesigner::HyperlinkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp new file mode 100644 index 00000000000..af04bb676a4 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp @@ -0,0 +1,684 @@ +/**************************************************************************** +** +** 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 "richtexteditor.h" +#include "ui_richtexteditor.h" +#include "hyperlinkdialog.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +template +class FontWidgetActions : public QWidgetAction { +public: + FontWidgetActions(QObject *parent = nullptr) + : QWidgetAction(parent) {} + + ~FontWidgetActions () override {} + + void setInitializer(std::function func) + { + m_initializer = func; + } + + QList createdWidgets() + { + return QWidgetAction::createdWidgets(); + } + +protected: + QWidget *createWidget(QWidget *parent) override + { + T *w = new T(parent); + if (m_initializer) + m_initializer(w); + return w; + } + + void deleteWidget(QWidget *widget) override + { + widget->deleteLater(); + } + +private: + std::function m_initializer; +}; + +static void cursorEditBlock(QTextCursor& cursor, std::function f) { + cursor.beginEditBlock(); + f(); + cursor.endEditBlock(); +} + +RichTextEditor::RichTextEditor(QWidget *parent) + : QWidget(parent) + , ui(new Ui::RichTextEditor) + , m_linkDialog(new HyperlinkDialog(this)) +{ + ui->setupUi(this); + ui->textEdit->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse); + ui->tableBar->setVisible(false); + + setupEditActions(); + setupTextActions(); + setupHyperlinkActions(); + setupAlignActions(); + setupListActions(); + setupFontActions(); + setupTableActions(); + + connect(ui->textEdit, &QTextEdit::currentCharFormatChanged, + this, &RichTextEditor::currentCharFormatChanged); + connect(ui->textEdit, &QTextEdit::cursorPositionChanged, + this, &RichTextEditor::cursorPositionChanged); + connect(m_linkDialog, &QDialog::accepted, [this]() { + QTextCharFormat oldFormat = ui->textEdit->textCursor().charFormat(); + + QTextCursor tcursor = ui->textEdit->textCursor(); + QTextCharFormat charFormat = tcursor.charFormat(); + + charFormat.setForeground(QApplication::palette().color(QPalette::Link)); + charFormat.setFontUnderline(true); + + QString link = m_linkDialog->getLink(); + QString anchor = m_linkDialog->getAnchor(); + + if (anchor.isEmpty()) + anchor = link; + + charFormat.setAnchor(true); + charFormat.setAnchorHref(link); + charFormat.setAnchorNames(QStringList(anchor)); + + tcursor.insertText(anchor, charFormat); + + tcursor.insertText(" ", oldFormat); + + m_linkDialog->hide(); + }); + + ui->textEdit->setFocus(); + m_linkDialog->hide(); +} + +RichTextEditor::~RichTextEditor() +{ +} + +void RichTextEditor::setPlainText(const QString &text) +{ + ui->textEdit->setPlainText(text); +} + +QString RichTextEditor::plainText() const +{ + return ui->textEdit->toPlainText(); +} + +void RichTextEditor::setRichText(const QString &text) +{ + ui->textEdit->setHtml(text); +} + +void RichTextEditor::setTabChangesFocus(bool change) +{ + ui->textEdit->setTabChangesFocus(change); +} + +QIcon RichTextEditor::getIcon(Theme::Icon icon) +{ + const QString fontName = "qtds_propertyIconFont.ttf"; + + return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 20, 20); +} + +QString RichTextEditor::richText() const +{ + return ui->textEdit->toHtml(); +} + +void RichTextEditor::currentCharFormatChanged(const QTextCharFormat &format) +{ + fontChanged(format.font()); + colorChanged(format.foreground().color()); +} + +void RichTextEditor::cursorPositionChanged() +{ + alignmentChanged(ui->textEdit->alignment()); + styleChanged(ui->textEdit->textCursor()); + tableChanged(ui->textEdit->textCursor()); +} + +void RichTextEditor::mergeFormatOnWordOrSelection(const QTextCharFormat &format) +{ + QTextCursor cursor = ui->textEdit->textCursor(); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); + ui->textEdit->mergeCurrentCharFormat(format); +} + +void RichTextEditor::fontChanged(const QFont &f) +{ + for (QWidget* w: m_fontNameAction->createdWidgets() ) { + QFontComboBox* box = qobject_cast(w); + if (box) + box->setCurrentFont(f); + } + for (QWidget* w: m_fontSizeAction->createdWidgets() ) { + QComboBox* box = qobject_cast(w); + if (box) + box->setCurrentText(QString::number(f.pointSize())); + } + + m_actionTextBold->setChecked(f.bold()); + m_actionTextItalic->setChecked(f.italic()); + m_actionTextUnderline->setChecked(f.underline()); +} + +void RichTextEditor::colorChanged(const QColor &c) +{ + QPixmap colorBox(ui->tableBar->iconSize()); + colorBox.fill(c); + m_actionTextColor->setIcon(colorBox); +} + +void RichTextEditor::alignmentChanged(Qt::Alignment a) +{ + if (a & Qt::AlignLeft) + m_actionAlignLeft->setChecked(true); + else if (a & Qt::AlignHCenter) + m_actionAlignCenter->setChecked(true); + else if (a & Qt::AlignRight) + m_actionAlignRight->setChecked(true); + else if (a & Qt::AlignJustify) + m_actionAlignJustify->setChecked(true); +} + +void RichTextEditor::styleChanged(const QTextCursor &cursor) +{ + if (!m_actionBulletList || !m_actionNumberedList) return; + + QTextList *currentList = cursor.currentList(); + + if (currentList) { + if (currentList->format().style() == QTextListFormat::ListDisc) { + m_actionBulletList->setChecked(true); + m_actionNumberedList->setChecked(false); + } + else if (currentList->format().style() == QTextListFormat::ListDecimal) { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(true); + } + else { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(false); + } + } + else { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(false); + } +} + +void RichTextEditor::tableChanged(const QTextCursor &cursor) +{ + if (!m_actionTableSettings) return; + + QTextTable *currentTable = cursor.currentTable(); + + if (currentTable) { + m_actionTableSettings->setChecked(true); + ui->tableBar->setVisible(true); + + setTableActionsActive(true); + } + else { + setTableActionsActive(false); + } +} + +void RichTextEditor::setupEditActions() +{ + const QIcon undoIcon(getIcon(Theme::Icon::undo)); + QAction *actionUndo = ui->toolBar->addAction(undoIcon, tr("&Undo"), ui->textEdit, &QTextEdit::undo); + actionUndo->setShortcut(QKeySequence::Undo); + connect(ui->textEdit->document(), &QTextDocument::undoAvailable, + actionUndo, &QAction::setEnabled); + + const QIcon redoIcon(getIcon(Theme::Icon::redo)); + QAction *actionRedo = ui->toolBar->addAction(redoIcon, tr("&Redo"), ui->textEdit, &QTextEdit::redo); + actionRedo->setShortcut(QKeySequence::Redo); + connect(ui->textEdit->document(), &QTextDocument::redoAvailable, + actionRedo, &QAction::setEnabled); + + actionUndo->setEnabled(ui->textEdit->document()->isUndoAvailable()); + actionRedo->setEnabled(ui->textEdit->document()->isRedoAvailable()); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupTextActions() +{ + const QIcon boldIcon(getIcon(Theme::Icon::fontStyleBold)); + m_actionTextBold = ui->toolBar->addAction(boldIcon, tr("&Bold"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextBold->setShortcut(Qt::CTRL + Qt::Key_B); + QFont bold; + bold.setBold(true); + m_actionTextBold->setFont(bold); + m_actionTextBold->setCheckable(true); + + const QIcon italicIcon(getIcon(Theme::Icon::fontStyleItalic)); + m_actionTextItalic = ui->toolBar->addAction(italicIcon, tr("&Italic"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontItalic(checked); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextItalic->setShortcut(Qt::CTRL + Qt::Key_I); + QFont italic; + italic.setItalic(true); + m_actionTextItalic->setFont(italic); + m_actionTextItalic->setCheckable(true); + + const QIcon underlineIcon(getIcon(Theme::Icon::fontStyleUnderline)); + m_actionTextUnderline = ui->toolBar->addAction(underlineIcon, tr("&Underline"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontUnderline(checked); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextUnderline->setShortcut(Qt::CTRL + Qt::Key_U); + QFont underline; + underline.setUnderline(true); + m_actionTextUnderline->setFont(underline); + m_actionTextUnderline->setCheckable(true); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupHyperlinkActions() +{ + const QIcon bulletIcon(getIcon(Theme::Icon::actionIconBinding)); + m_actionHyperlink = ui->toolBar->addAction(bulletIcon, tr("Hyperlink Settings"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + QTextCharFormat linkFormat = cursor.charFormat(); + if (linkFormat.isAnchor()) { + m_linkDialog->setLink(linkFormat.anchorHref()); + m_linkDialog->setAnchor(linkFormat.anchorName()); + } + else { + m_linkDialog->setLink("http://"); + m_linkDialog->setAnchor(""); + } + + m_linkDialog->show(); + }); + m_actionHyperlink->setCheckable(false); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupAlignActions() +{ + const QIcon leftIcon(getIcon(Theme::Icon::textAlignLeft)); + m_actionAlignLeft = ui->toolBar->addAction(leftIcon, tr("&Left"), [this]() { ui->textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); }); + m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); + m_actionAlignLeft->setCheckable(true); + m_actionAlignLeft->setPriority(QAction::LowPriority); + + const QIcon centerIcon(getIcon(Theme::Icon::textAlignCenter)); + m_actionAlignCenter = ui->toolBar->addAction(centerIcon, tr("C&enter"), [this]() { ui->textEdit->setAlignment(Qt::AlignHCenter); }); + m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); + m_actionAlignCenter->setCheckable(true); + m_actionAlignCenter->setPriority(QAction::LowPriority); + + const QIcon rightIcon(getIcon(Theme::Icon::textAlignRight)); + m_actionAlignRight = ui->toolBar->addAction(rightIcon, tr("&Right"), [this]() { ui->textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); }); + m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); + m_actionAlignRight->setCheckable(true); + m_actionAlignRight->setPriority(QAction::LowPriority); + + const QIcon fillIcon(getIcon(Theme::Icon::textFullJustification)); + m_actionAlignJustify = ui->toolBar->addAction(fillIcon, tr("&Justify"), [this]() { ui->textEdit->setAlignment(Qt::AlignJustify); }); + m_actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); + m_actionAlignJustify->setCheckable(true); + m_actionAlignJustify->setPriority(QAction::LowPriority); + + // Make sure the alignLeft is always left of the alignRight + QActionGroup *alignGroup = new QActionGroup(ui->toolBar); + + if (QApplication::isLeftToRight()) { + alignGroup->addAction(m_actionAlignLeft); + alignGroup->addAction(m_actionAlignCenter); + alignGroup->addAction(m_actionAlignRight); + } else { + alignGroup->addAction(m_actionAlignRight); + alignGroup->addAction(m_actionAlignCenter); + alignGroup->addAction(m_actionAlignLeft); + } + alignGroup->addAction(m_actionAlignJustify); + + ui->toolBar->addActions(alignGroup->actions()); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupListActions() +{ + const QIcon bulletIcon(getIcon(Theme::Icon::textBulletList)); + m_actionBulletList = ui->toolBar->addAction(bulletIcon, tr("Bullet List"), [this](bool checked) { + if (checked) { + m_actionNumberedList->setChecked(false); + textStyle(QTextListFormat::ListDisc); + } + else if (!m_actionNumberedList->isChecked()) { + textStyle(QTextListFormat::ListStyleUndefined); + } + }); + m_actionBulletList->setCheckable(true); + + const QIcon numberedIcon(getIcon(Theme::Icon::textNumberedList)); + m_actionNumberedList = ui->toolBar->addAction(numberedIcon, tr("Numbered List"), [this](bool checked) { + if (checked) { + m_actionBulletList->setChecked(false); + textStyle(QTextListFormat::ListDecimal); + } + else if (!m_actionBulletList->isChecked()) { + textStyle(QTextListFormat::ListStyleUndefined); + } + }); + m_actionNumberedList->setCheckable(true); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupFontActions() +{ + QPixmap colorBox(ui->tableBar->iconSize()); + colorBox.fill(ui->textEdit->textColor()); + + m_actionTextColor = ui->toolBar->addAction(colorBox, tr("&Color..."), [this]() { + QColor col = QColorDialog::getColor(ui->textEdit->textColor(), this); + if (!col.isValid()) + return; + QTextCharFormat fmt; + fmt.setForeground(col); + mergeFormatOnWordOrSelection(fmt); + colorChanged(col); + }); + + m_fontNameAction = new FontWidgetActions(this); + m_fontNameAction->setInitializer([this](QFontComboBox *w) { + if (!w) return; + + w->setCurrentIndex(w->findText(ui->textEdit->currentCharFormat().font().family())); + connect(w, QOverload::of(&QComboBox::activated), [this](const QString &f) { + QTextCharFormat fmt; + fmt.setFontFamily(f); + mergeFormatOnWordOrSelection(fmt); + }); + }); + + m_fontNameAction->setDefaultWidget(new QFontComboBox); + ui->toolBar->addAction(m_fontNameAction); + + m_fontSizeAction = new FontWidgetActions(this); + m_fontSizeAction->setInitializer([this](QComboBox *w) { + if (!w) return; + + w->setEditable(true); + + const QList standardSizes = QFontDatabase::standardSizes(); + foreach (int size, standardSizes) + w->addItem(QString::number(size)); + w->setCurrentText(QString::number(ui->textEdit->currentCharFormat().font().pointSize())); + connect(w, QOverload::of(&QComboBox::activated), [this](const QString &p) { + qreal pointSize = p.toDouble(); + if (pointSize > 0.0) { + QTextCharFormat fmt; + fmt.setFontPointSize(pointSize); + mergeFormatOnWordOrSelection(fmt); + } + }); + }); + + m_fontSizeAction->setDefaultWidget(new QComboBox); + ui->toolBar->addAction(m_fontSizeAction); + + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupTableActions() +{ + const QIcon tableIcon(getIcon(Theme::Icon::addTable)); + m_actionTableSettings = ui->toolBar->addAction(tableIcon, tr("&Table Settings"), [this](bool checked) { + ui->tableBar->setVisible(checked); + }); + m_actionTableSettings->setShortcut(Qt::CTRL + Qt::Key_T); + m_actionTableSettings->setCheckable(true); + m_actionTableSettings->setPriority(QAction::LowPriority); + +//table bar: + + const QIcon createTableIcon(getIcon(Theme::Icon::addTable)); + m_actionCreateTable = ui->tableBar->addAction(createTableIcon, tr("Create Table"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + cursorEditBlock(cursor, [&] () { + cursor.insertTable(1,1); + }); + }); + m_actionCreateTable->setCheckable(false); + + const QIcon removeTableIcon(getIcon(Theme::Icon::deleteTable)); + m_actionRemoveTable = ui->tableBar->addAction(removeTableIcon, tr("Remove Table"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->removeRows(0, currentTable->rows()); + }); + } + }); + m_actionRemoveTable->setCheckable(false); + + ui->tableBar->addSeparator(); + + const QIcon addRowIcon(getIcon(Theme::Icon::addRowAfter)); //addRowAfter + m_actionAddRow = ui->tableBar->addAction(addRowIcon, tr("Add Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertRows(currentTable->cellAt(cursor).row()+1, 1); + }); + } + }); + m_actionAddRow->setCheckable(false); + + const QIcon addColumnIcon(getIcon(Theme::Icon::addColumnAfter)); //addColumnAfter + m_actionAddColumn = ui->tableBar->addAction(addColumnIcon, tr("Add Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertColumns(currentTable->cellAt(cursor).column()+1, 1); + }); + } + }); + m_actionAddColumn->setCheckable(false); + + const QIcon removeRowIcon(getIcon(Theme::Icon::deleteRow)); + m_actionRemoveRow = ui->tableBar->addAction(removeRowIcon, tr("Remove Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertColumns(currentTable->cellAt(cursor).column()+1, 1); + + int firstRow = 0; + int numRows = 0; + int firstColumn = 0; + int numColumns = 0; + + if (cursor.hasSelection()) + cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); + + if (numRows < 1) + currentTable->removeRows(currentTable->cellAt(cursor).row(), 1); + else + currentTable->removeRows(firstRow, numRows); + }); + } + }); + m_actionRemoveRow->setCheckable(false); + + const QIcon removeColumnIcon(getIcon(Theme::Icon::deleteColumn)); + m_actionRemoveColumn = ui->tableBar->addAction(removeColumnIcon, tr("Remove Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + int firstRow = 0; + int numRows = 0; + int firstColumn = 0; + int numColumns = 0; + + if (cursor.hasSelection()) + cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); + + if (numColumns < 1) + currentTable->removeColumns(currentTable->cellAt(cursor).column(), 1); + else + currentTable->removeColumns(firstColumn, numColumns); + }); + } + }); + m_actionRemoveColumn->setCheckable(false); + + ui->tableBar->addSeparator(); + + const QIcon mergeCellsIcon(getIcon(Theme::Icon::mergeCells)); + m_actionMergeCells = ui->tableBar->addAction(mergeCellsIcon, tr("Merge Cells"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + if (cursor.hasSelection()) { + cursorEditBlock(cursor, [&] () { + currentTable->mergeCells(cursor); + }); + } + } + }); + m_actionMergeCells->setCheckable(false); + + const QIcon splitRowIcon(getIcon(Theme::Icon::splitRows)); + m_actionSplitRow = ui->tableBar->addAction(splitRowIcon, tr("Split Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->splitCell(currentTable->cellAt(cursor).row(), + currentTable->cellAt(cursor).column(), + 2, 1); + }); + } + }); + m_actionSplitRow->setCheckable(false); + + const QIcon splitColumnIcon(getIcon(Theme::Icon::splitColumns)); + m_actionSplitColumn = ui->tableBar->addAction(splitRowIcon, tr("Split Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->splitCell(currentTable->cellAt(cursor).row(), + currentTable->cellAt(cursor).column(), + 1, 2); + }); + } + }); + m_actionSplitColumn->setCheckable(false); +} + +void RichTextEditor::textStyle(QTextListFormat::Style style) +{ + QTextCursor cursor = ui->textEdit->textCursor(); + cursorEditBlock(cursor, [&] () { + if (style != QTextListFormat::ListStyleUndefined) { + QTextBlockFormat blockFmt = cursor.blockFormat(); + QTextListFormat listFmt; + + if (cursor.currentList()) { + listFmt = cursor.currentList()->format(); + } else { + listFmt.setIndent(blockFmt.indent() + 1); + blockFmt.setIndent(0); + cursor.setBlockFormat(blockFmt); + } + + listFmt.setStyle(style); + + cursor.createList(listFmt); + } else { + QTextList* currentList = cursor.currentList(); + QTextBlock currentBlock = cursor.block(); + currentList->remove(currentBlock); + + QTextBlockFormat blockFormat = cursor.blockFormat(); + blockFormat.setIndent(0); + cursor.setBlockFormat(blockFormat); + } + }); +} + +void RichTextEditor::setTableActionsActive(bool active) +{ + m_actionCreateTable->setEnabled(!active); + m_actionRemoveTable->setEnabled(active); + + m_actionAddRow->setEnabled(active); + m_actionAddColumn->setEnabled(active); + m_actionRemoveRow->setEnabled(active); + m_actionRemoveColumn->setEnabled(active); + + m_actionMergeCells->setEnabled(active); + m_actionSplitRow->setEnabled(active); + m_actionSplitColumn->setEnabled(active); +} + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h new file mode 100644 index 00000000000..50053c1ab3c --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +namespace Ui { +class RichTextEditor; +} + +template +class FontWidgetActions; + +class HyperlinkDialog; + +class RichTextEditor : public QWidget +{ + Q_OBJECT + +public: + explicit RichTextEditor(QWidget *parent = nullptr); + ~RichTextEditor(); + + void setPlainText(const QString &text); + QString plainText() const; + + void setRichText(const QString &text); + QString richText() const; + + void setTabChangesFocus(bool change); + +private slots: + void currentCharFormatChanged(const QTextCharFormat &format); + void cursorPositionChanged(); + +private: + QIcon getIcon(Theme::Icon icon); + void mergeFormatOnWordOrSelection(const QTextCharFormat &format); + + void fontChanged(const QFont &f); + void colorChanged(const QColor &c); + void alignmentChanged(Qt::Alignment a); + void styleChanged(const QTextCursor &cursor); + void tableChanged(const QTextCursor &cursor); + + void setupEditActions(); + void setupTextActions(); + void setupHyperlinkActions(); + void setupAlignActions(); + void setupListActions(); + void setupFontActions(); + void setupTableActions(); + + void textStyle(QTextListFormat::Style style); + + void setTableActionsActive(bool active); //switches between "has table/has no table" ui setup + +private: + QScopedPointer ui; + QPointer m_linkDialog; + + QAction *m_actionTextBold; + QAction *m_actionTextItalic; + QAction *m_actionTextUnderline; + + QAction *m_actionHyperlink; + + QAction *m_actionAlignLeft; + QAction *m_actionAlignCenter; + QAction *m_actionAlignRight; + QAction *m_actionAlignJustify; + + QAction *m_actionTextColor; + + QAction *m_actionBulletList; + QAction *m_actionNumberedList; + + QAction *m_actionTableSettings; + + QAction *m_actionCreateTable; + QAction *m_actionRemoveTable; + + QAction *m_actionAddRow; + QAction *m_actionAddColumn; + QAction *m_actionRemoveRow; + QAction *m_actionRemoveColumn; + + QAction *m_actionMergeCells; + QAction *m_actionSplitRow; + QAction *m_actionSplitColumn; + + QPointer> m_fontNameAction; + QPointer> m_fontSizeAction; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri new file mode 100644 index 00000000000..68b6dbe0268 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri @@ -0,0 +1,8 @@ +HEADERS += $$PWD/richtexteditor.h +HEADERS += $$PWD/hyperlinkdialog.h + +SOURCES += $$PWD/richtexteditor.cpp +SOURCES += $$PWD/hyperlinkdialog.cpp + +FORMS += $$PWD/richtexteditor.ui +FORMS += $$PWD/hyperlinkdialog.ui diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui new file mode 100644 index 00000000000..3c21f9147fb --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui @@ -0,0 +1,50 @@ + + + QmlDesigner::RichTextEditor + + + + 0 + 0 + 428 + 283 + + + + + 0 + 5 + + + + Form + + + + + + + 20 + 20 + + + + + + + + + 20 + 20 + + + + + + + + + + + + diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 098fe766a02..d399eb19527 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -58,6 +58,7 @@ #include #include #include +#include namespace QmlDesigner { @@ -118,6 +119,7 @@ TimelineWidget::TimelineWidget(TimelineView *view) , m_timelineView(view) , m_graphicsScene(new TimelineGraphicsScene(this)) , m_addButton(new QPushButton(this)) + , m_onboardingContainer(new QWidget(this)) { setWindowTitle(tr("Timeline", "Title of timeline view")); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -185,6 +187,50 @@ TimelineWidget::TimelineWidget(TimelineView *view) m_addButton->setFlat(true); m_addButton->setFixedSize(32, 32); + + widgetLayout->addWidget(m_onboardingContainer); + + auto *onboardingTopLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomIcon = new QLabel(m_onboardingContainer); + + auto *onboardingLayout = new QVBoxLayout; + auto *onboardingSublayout = new QHBoxLayout; + auto *leftSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *rightSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *topSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + auto *bottomSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + + QString labelText = + tr("This file does not contain a timeline.

\ + To create an animation, add a timeline by clicking the + button."); + onboardingTopLabel->setText(labelText); + onboardingTopLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + + m_onboardingContainer->setLayout(onboardingLayout); + onboardingLayout->setContentsMargins(0, 0, 0, 0); + onboardingLayout->setSpacing(0); + onboardingLayout->addSpacerItem(topSpacer); + onboardingLayout->addWidget(onboardingTopLabel); + onboardingLayout->addLayout(onboardingSublayout); + + onboardingSublayout->setContentsMargins(0, 0, 0, 0); + onboardingSublayout->setSpacing(0); + onboardingSublayout->addSpacerItem(leftSpacer); + + onboardingBottomLabel->setAlignment(Qt::AlignRight | Qt::AlignTop); + onboardingBottomLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomLabel); + onboardingBottomLabel->setText(tr("To edit the timeline settings, click ")); + + onboardingBottomIcon->setAlignment(Qt::AlignLeft | Qt::AlignTop); + onboardingBottomIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomIcon); + onboardingBottomIcon->setPixmap(TimelineIcons::ANIMATION.pixmap()); + + onboardingSublayout->addSpacerItem(rightSpacer); + onboardingLayout->addSpacerItem(bottomSpacer); + widgetLayout->addLayout(contentLayout); this->setLayout(widgetLayout); @@ -532,6 +578,7 @@ void TimelineWidget::setTimelineActive(bool b) m_rulerView->setVisible(true); m_scrollbar->setVisible(true); m_addButton->setVisible(false); + m_onboardingContainer->setVisible(false); m_graphicsView->update(); m_rulerView->update(); } else { @@ -540,6 +587,7 @@ void TimelineWidget::setTimelineActive(bool b) m_rulerView->setVisible(false); m_scrollbar->setVisible(false); m_addButton->setVisible(true); + m_onboardingContainer->setVisible(true); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h index 681181dbe91..4d0e4711cbe 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h @@ -104,6 +104,8 @@ private: TimelineGraphicsScene *m_graphicsScene; QPushButton *m_addButton = nullptr; + + QWidget *m_onboardingContainer = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp index a35ff98ef4b..cc82a5607eb 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/annotation.h b/src/plugins/qmldesigner/designercore/include/annotation.h index 95fcf81fa21..2b7c64bb4a2 100644 --- a/src/plugins/qmldesigner/designercore/include/annotation.h +++ b/src/plugins/qmldesigner/designercore/include/annotation.h @@ -35,6 +35,7 @@ namespace QmlDesigner { static const PropertyName customIdProperty = {("customId")}; static const PropertyName annotationProperty = {("annotation")}; +static const PropertyName globalAnnotationProperty = {("globalAnnotation")}; class Comment { diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index ac65de0240f..85302ad2c42 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -202,6 +202,11 @@ public: void setAnnotation(const Annotation &annotation); void removeAnnotation(); + Annotation globalAnnotation() const; + bool hasGlobalAnnotation() const; + void setGlobalAnnotation(const Annotation &annotation); + void removeGlobalAnnotation(); + qint32 internalId() const; void setNodeSource(const QString&); diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index ef8f1b28ba2..d779cc15bf3 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -182,8 +182,13 @@ public: const QList wildcards() const; const QList decicions() const; QList transitionsForTarget(const ModelNode &modelNode); + QList transitionsForSource(const ModelNode &modelNode); void removeDanglingTransitions(); void removeAllTransitions(); + void setStartFlowItem(const QmlFlowItemNode &flowItem); + ModelNode createTransition(); +protected: + QList transitionsForProperty(const PropertyName &propertyName, const ModelNode &modelNode); }; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index ff65187f46e..b4b043847e2 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/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 61d0f93cf56..1d1a5386f21 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1150,6 +1150,35 @@ void ModelNode::removeAnnotation() } } +Annotation ModelNode::globalAnnotation() const +{ + Annotation result; + ModelNode root = view()->rootModelNode(); + + if (hasGlobalAnnotation()) + result.fromQString(root.auxiliaryData(globalAnnotationProperty).value()); + + return result; +} + +bool ModelNode::hasGlobalAnnotation() const +{ + return view()->rootModelNode().hasAuxiliaryData(globalAnnotationProperty); +} + +void ModelNode::setGlobalAnnotation(const Annotation &annotation) +{ + view()->rootModelNode().setAuxiliaryData(globalAnnotationProperty, + QVariant::fromValue(annotation.toQString())); +} + +void ModelNode::removeGlobalAnnotation() +{ + if (hasGlobalAnnotation()) { + view()->rootModelNode().removeAuxiliaryData(globalAnnotationProperty); + } +} + void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { model()->d->setScriptFunctions(internalNode(), scriptFunctionList); diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 098f8317a5d..e2f0602cc24 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -664,9 +664,7 @@ QList 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 QmlFlowViewNode::transitions() const return modelNode().nodeListProperty("flowTransitions").toModelNodeList(); return {}; - - } const QList QmlFlowViewNode::wildcards() const @@ -706,13 +702,12 @@ const QList QmlFlowViewNode::decicions() const QList QmlFlowViewNode::transitionsForTarget(const ModelNode &modelNode) { - QList 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 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 QmlFlowViewNode::transitionsForProperty(const PropertyName &propertyName, + const ModelNode &modelNode) +{ + QList 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/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index b7426c7a43c..91e4c9c7b05 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 5a1aa97fa64..ddf7be0ec18 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include +#include using Core::MiniSplitter; using Core::IEditor; @@ -227,6 +229,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 *mview = Core::ActionManager::actionContainer(Core::Constants::M_VIEW); // Window > Views @@ -409,6 +431,19 @@ void DesignModeWidget::setup() m_dockManager->openWorkspace(workspaceComboBox->currentText()); }); + const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::annotationBubble), 36, 36); + toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){ + ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); + + if (node.isValid()) { + m_globalAnnotationEditor.setModelNode(node); + m_globalAnnotationEditor.showWidget(); + } + }); + + + viewManager().enableWidgets(); readSettings(); show(); diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 8e44ce8c49f..67f8f3d554d 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -36,6 +36,7 @@ #include #include +#include namespace Core { class SideBar; @@ -120,6 +121,7 @@ private: // variables ADS::DockManager *m_dockManager = nullptr; ADS::DockWidget *m_outputPaneDockWidget = nullptr; + GlobalAnnotationEditor m_globalAnnotationEditor; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a65ff3600be..21d1ffadf22 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index ea017e3ceff..0095aa8f10b 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -31,6 +31,7 @@ include(components/connectioneditor/connectioneditor.pri) include(components/curveeditor/curveeditor.pri) include(components/bindingeditor/bindingeditor.pri) include(components/annotationeditor/annotationeditor.pri) +include(components/richtexteditor/richtexteditor.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 3c434788cd9..dbbdb1079a5 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", @@ -654,6 +656,8 @@ Project { "annotationeditor/annotationcommenttab.ui", "annotationeditor/annotationeditor.cpp", "annotationeditor/annotationeditor.h", + "annotationeditor/globalannotationeditor.cpp", + "annotationeditor/globalannotationeditor.h", "annotationeditor/annotationeditordialog.cpp", "annotationeditor/annotationeditordialog.h", "annotationeditor/annotationeditordialog.ui", @@ -746,6 +750,12 @@ Project { "pathtool/pathtool.h", "pathtool/pathtoolview.cpp", "pathtool/pathtoolview.h", + "richtexteditor/hyperlinkdialog.cpp", + "richtexteditor/hyperlinkdialog.h", + "richtexteditor/hyperlinkdialog.ui", + "richtexteditor/richtexteditor.cpp", + "richtexteditor/richtexteditor.h", + "richtexteditor/richtexteditor.ui", "sourcetool/sourcetool.cpp", "sourcetool/sourcetool.h", "texttool/textedititem.cpp", diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 4e7b97d53b7..904af9b6d16 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -164,6 +164,14 @@ add_qtc_test(unittest GTEST unittest-utility-functions.h usedmacrofilter-test.cpp utf8-test.cpp + sqlitecolumn-test.cpp + sqlitedatabasebackend-test.cpp + sqlitedatabase-test.cpp + sqlitestatement-test.cpp + sqlitetable-test.cpp + sqlstatementbuilder-test.cpp + createtablesqlstatementbuilder-test.cpp + sqlitevalue-test.cpp ) # Do not work on the source directory data @@ -219,7 +227,6 @@ if (TARGET libclang) codecompleter-test.cpp codecompletionsextractor-test.cpp completionchunkstotextconverter-test.cpp - createtablesqlstatementbuilder-test.cpp cursor-test.cpp diagnosticset-test.cpp diagnostic-test.cpp @@ -228,12 +235,6 @@ if (TARGET libclang) skippedsourceranges-test.cpp sourcelocation-test.cpp sourcerange-test.cpp - sqlitecolumn-test.cpp - sqlitedatabasebackend-test.cpp - sqlitedatabase-test.cpp - sqlitestatement-test.cpp - sqlitetable-test.cpp - sqlstatementbuilder-test.cpp token-test.cpp translationunitupdater-test.cpp unsavedfiles-test.cpp diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index 2e84e1cdcab..e5b10f65ae8 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -14,11 +14,11 @@ include($$PWD/../../../src/tools/clangpchmanagerbackend/source/clangpchmanagerba include($$PWD/../../../src/plugins/clangrefactoring/clangrefactoring-source.pri) include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) -include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) include($$PWD/../../../src/plugins/debugger/debuggerunittestfiles.pri) include($$PWD/../../../src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri) include(cplusplus.pri) !isEmpty(LLVM_VERSION) { +include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) include($$PWD/../../../src/shared/clang/clang_defines.pri) include($$PWD/../../../src/tools/clangbackend/source/clangbackendclangipc-source.pri) include($$PWD/../../../src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri) diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 2bbcdbaa45e..6d5f7a2cf34 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -64,12 +66,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include namespace { ClangBackEnd::FilePathCaching *filePathCache = nullptr; @@ -306,6 +302,27 @@ void PrintTo(const Utils::PathString &text, ::std::ostream *os) } // namespace Utils +namespace Sqlite { +std::ostream &operator<<(std::ostream &out, const Value &value) +{ + out << "("; + + switch (value.type()) { + case Sqlite::ValueType::Integer: + out << value.toInteger(); + break; + case Sqlite::ValueType::Float: + out << value.toFloat(); + break; + case Sqlite::ValueType::String: + out << "\"" << value.toStringView() << "\""; + break; + } + + return out << ")"; +} +} // namespace Sqlite + namespace ClangBackEnd { std::ostream &operator<<(std::ostream &out, const FilePathId &id) diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index e26809ca519..b91c8d46beb 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -64,6 +64,12 @@ void PrintTo(const TextRange &range, ::std::ostream *os); } // namespace TextPosition } // namespace TextPosition +namespace Sqlite { +class Value; + +std::ostream &operator<<(std::ostream &out, const Value &value); +} // namespace Sqlite + namespace ProjectExplorer { enum class MacroType; diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp index cb8206d8757..c7da3971e4f 100644 --- a/tests/unit/unittest/sqlitestatement-test.cpp +++ b/tests/unit/unittest/sqlitestatement-test.cpp @@ -40,11 +40,12 @@ namespace { -using Sqlite::JournalMode; -using Sqlite::Exception; using Sqlite::Database; +using Sqlite::Exception; +using Sqlite::JournalMode; using Sqlite::ReadStatement; using Sqlite::ReadWriteStatement; +using Sqlite::Value; using Sqlite::WriteStatement; MATCHER_P3(HasValues, value1, value2, rowid, @@ -125,7 +126,7 @@ TEST_F(SqliteStatement, CountRows) TEST_F(SqliteStatement, Value) { - SqliteTestStatement statement("SELECT name, number FROM test ORDER BY name", database); + SqliteTestStatement statement("SELECT name, number, value FROM test ORDER BY name", database); statement.next(); statement.next(); @@ -142,6 +143,9 @@ TEST_F(SqliteStatement, Value) ASSERT_THAT(statement.fetchValue(1), "23.3"); ASSERT_THAT(statement.fetchValue(1), "23.3"); ASSERT_THAT(statement.fetchSmallStringViewValue(1), "23.3"); + ASSERT_THAT(statement.fetchValueView(0), Eq("foo")); + ASSERT_THAT(statement.fetchValueView(1), Eq(23.3)); + ASSERT_THAT(statement.fetchValueView(2), Eq(2)); } TEST_F(SqliteStatement, ThrowNoValuesToFetchForNotSteppedStatement) @@ -175,14 +179,14 @@ TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForNotExistingColumn) ASSERT_THROW(statement.fetchValue(2), Sqlite::InvalidColumnFetched); } -TEST_F(SqliteStatement, ToIntergerValue) +TEST_F(SqliteStatement, ToIntegerValue) { auto value = ReadStatement::toValue("SELECT number FROM test WHERE name='foo'", database); ASSERT_THAT(value, 23); } -TEST_F(SqliteStatement, ToLongIntergerValue) +TEST_F(SqliteStatement, ToLongIntegerValue) { ASSERT_THAT(ReadStatement::toValue("SELECT number FROM test WHERE name='foo'", database), Eq(23)); } @@ -319,6 +323,15 @@ TEST_F(SqliteStatement, WriteValues) ASSERT_THAT(statement, HasValues("see", "7.23", 1)); } +TEST_F(SqliteStatement, WriteSqliteValues) +{ + WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database); + + statement.write(Value{"see"}, Value{7.23}, Value{1}); + + ASSERT_THAT(statement, HasValues("see", "7.23", 1)); +} + TEST_F(SqliteStatement, BindNamedValues) { SqliteTestStatement statement("UPDATE test SET name=@name, number=@number WHERE rowid=@id", database); @@ -375,6 +388,31 @@ TEST_F(SqliteStatement, GetSingleValuesWithoutArguments) ASSERT_THAT(values, ElementsAre("bar", "foo", "poo")); } +class FooValue +{ +public: + FooValue(Sqlite::ValueView value) + : value(value) + {} + + Sqlite::Value value; + + template + friend bool operator==(const FooValue &value, const Type &other) + { + return value.value == other; + } +}; + +TEST_F(SqliteStatement, GetSingleSqliteValuesWithoutArguments) +{ + ReadStatement statement("SELECT number FROM test", database); + + std::vector values = statement.values(3); + + ASSERT_THAT(values, ElementsAre(Eq("blah"), Eq(23.3), Eq(40))); +} + TEST_F(SqliteStatement, GetStructValuesWithoutArguments) { ReadStatement statement("SELECT name, number, value FROM test", database); diff --git a/tests/unit/unittest/sqlitevalue-test.cpp b/tests/unit/unittest/sqlitevalue-test.cpp new file mode 100644 index 00000000000..e9cc5c9e408 --- /dev/null +++ b/tests/unit/unittest/sqlitevalue-test.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** 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 "googletest.h" + +#include + +namespace { + +TEST(SqliteValue, ConstructLongLong) +{ + Sqlite::Value value{1LL}; + + ASSERT_THAT(value.toInteger(), Eq(1LL)); +} + +TEST(SqliteValue, Construct) +{ + Sqlite::Value value{1}; + + ASSERT_THAT(value.toInteger(), Eq(1LL)); +} + +TEST(SqliteValue, ConstructFloatingPoint) +{ + Sqlite::Value value{1.1}; + + ASSERT_THAT(value.toFloat(), Eq(1.1)); +} + +TEST(SqliteValue, ConstructStringFromCString) +{ + Sqlite::Value value{"foo"}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromUtilsString) +{ + Sqlite::Value value{Utils::SmallString{"foo"}}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromQString) +{ + Sqlite::Value value{QString{"foo"}}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromIntQVariant) +{ + QVariant variant{1}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromLongLongQVariant) +{ + QVariant variant{1LL}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromUintQVariant) +{ + QVariant variant{1u}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromFloatQVariant) +{ + QVariant variant{1.}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toFloat(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromStringQVariant) +{ + QVariant variant{QString{"foo"}}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConvertToStringQVariant) +{ + Sqlite::Value value{"foo"}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq("foo")); +} + +TEST(SqliteValue, ConvertToIntegerQVariant) +{ + Sqlite::Value value{1}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq(1)); +} + +TEST(SqliteValue, ConvertToFloatQVariant) +{ + Sqlite::Value value{1.1}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq(1.1)); +} + +TEST(SqliteValue, IntegerEquals) +{ + bool isEqual = Sqlite::Value{1} == 1LL; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerEqualsInverse) +{ + bool isEqual = 1LL == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatEquals) +{ + bool isEqual = Sqlite::Value{1.0} == 1.; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatEqualsInverse) +{ + bool isEqual = 1. == Sqlite::Value{1.0}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringEquals) +{ + bool isEqual = Sqlite::Value{"foo"} == "foo"; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringEqualsInverse) +{ + bool isEqual = "foo" == Sqlite::Value{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatAreNotEquals) +{ + bool isEqual = Sqlite::Value{1} == 1.; + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, IntegerValuesAreEquals) +{ + bool isEqual = Sqlite::Value{1} == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatValuesAreNotEquals) +{ + bool isEqual = Sqlite::Value{1} == Sqlite::Value{1.}; + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, StringAndQStringAreEquals) +{ + bool isEqual = Sqlite::Value{"foo"} == QString{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatValuesAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != Sqlite::Value{1.0}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerAndFloatAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != 1.0; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerAndFloatAreUnequalInverse) +{ + bool isUnequal = 1.0 != Sqlite::Value{1}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegersAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != 2; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegersAreUnequalInverse) +{ + bool isUnequal = 2 != Sqlite::Value{1}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerType) +{ + auto type = Sqlite::Value{1}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::Integer); +} + +TEST(SqliteValue, FloatType) +{ + auto type = Sqlite::Value{1.}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::Float); +} + +TEST(SqliteValue, StringType) +{ + auto type = Sqlite::Value{"foo"}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::String); +} + +TEST(SqliteValue, StringValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create("foo") == Sqlite::Value{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{"foo"} == Sqlite::ValueView::create("foo"); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create(1) == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{2} == Sqlite::ValueView::create(2); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create(1.1) == Sqlite::Value{1.1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{1.1} == Sqlite::ValueView::create(1.1); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringValueAndIntergerValueViewAreNotEqual) +{ + bool isEqual = Sqlite::Value{"foo"} == Sqlite::ValueView::create(1); + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, ConvertStringValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create("foo"); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq("foo")); +} + +TEST(SqliteValue, ConvertIntegerValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create(1); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq(1)); +} + +TEST(SqliteValue, ConvertFloatValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create(1.4); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq(1.4)); +} + +} // namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 1db48813e85..6155cf32128 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -80,6 +80,7 @@ SOURCES += \ smallstring-test.cpp \ sourcerangefilter-test.cpp \ spydummy.cpp \ + sqlitevalue-test.cpp \ symbolindexer-test.cpp \ symbolsfindfilter-test.cpp \ stringcache-test.cpp \ @@ -122,7 +123,13 @@ SOURCES += \ headerpathfilter-test.cpp \ toolchainargumentscache-test.cpp \ modifiedtimechecker-test.cpp \ - readexporteddiagnostics-test.cpp + sqlitecolumn-test.cpp \ + sqlitedatabasebackend-test.cpp \ + sqlitedatabase-test.cpp \ + sqlitestatement-test.cpp \ + sqlitetable-test.cpp \ + sqlstatementbuilder-test.cpp \ + createtablesqlstatementbuilder-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \ @@ -158,7 +165,6 @@ SOURCES += \ codecompleter-test.cpp \ codecompletionsextractor-test.cpp \ completionchunkstotextconverter-test.cpp \ - createtablesqlstatementbuilder-test.cpp \ cursor-test.cpp \ diagnosticset-test.cpp \ diagnostic-test.cpp \ @@ -168,17 +174,12 @@ SOURCES += \ skippedsourceranges-test.cpp \ sourcelocation-test.cpp \ sourcerange-test.cpp \ - sqlitecolumn-test.cpp \ - sqlitedatabasebackend-test.cpp \ - sqlitedatabase-test.cpp \ - sqlitestatement-test.cpp \ - sqlitetable-test.cpp \ - sqlstatementbuilder-test.cpp \ token-test.cpp \ translationunitupdater-test.cpp \ unsavedfiles-test.cpp \ unsavedfile-test.cpp \ - utf8positionfromlinecolumn-test.cpp + utf8positionfromlinecolumn-test.cpp \ + readexporteddiagnostics-test.cpp } !isEmpty(LIBTOOLING_LIBS) {