Merge "Merge remote-tracking branch 'origin/qds-1.59'"

This commit is contained in:
The Qt Project
2020-04-29 08:05:31 +00:00
97 changed files with 4303 additions and 315 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -67,7 +67,8 @@ public:
float zoomFactor, bool relative);
Q_INVOKABLE QVector4D focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true);
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);

View File

@@ -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 {

View File

@@ -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 {
}
}
}
}

View File

@@ -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 {}
}
}
}
}

View File

@@ -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 {}
}
}
}
}

View File

@@ -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 {

View File

@@ -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 {}
}
}
}
}

View File

@@ -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 {
}
}
}
}
}

View File

@@ -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

View File

@@ -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")
}

View File

@@ -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"
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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 {}

View File

@@ -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
}

View File

@@ -1,2 +1,4 @@
singleton Values 1.0 Values.qml
singleton Constants 1.0 Constants.qml
InternalConstants 1.0 InternalConstants.qml

View File

@@ -111,6 +111,7 @@ enum eIcon {
DockAreaMenuIcon, //!< DockAreaMenuIcon
DockAreaUndockIcon, //!< DockAreaUndockIcon
DockAreaCloseIcon, //!< DockAreaCloseIcon
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon
IconCount, //!< just a delimiter for range checks
};

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -51,7 +51,7 @@ namespace ADS {
*/
IconProviderPrivate(IconProvider *parent);
};
// struct LedArrayPanelPrivate
// struct IconProviderPrivate
IconProviderPrivate::IconProviderPrivate(IconProvider *parent)
: q(parent)

View File

@@ -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);

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 <typename Type>
void BaseStatement::bind(Utils::SmallStringView name, Type value)
{
@@ -498,12 +513,34 @@ StringType BaseStatement::fetchValue(int column) const
return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
int column) const;
template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(
int column) const;
template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(
int column) const;
Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const
{
return fetchValue<Utils::SmallStringView>(column);
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(int column) const;
template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(int column) const;
template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(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<Utils::SmallStringView>(column));
case SQLITE_BLOB:
case SQLITE_NULL:
break;
}
return ValueView::create(0LL);
}
} // namespace Sqlite

View File

@@ -28,6 +28,7 @@
#include "sqliteglobal.h"
#include "sqliteexception.h"
#include "sqlitevalue.h"
#include <utils/smallstringvector.h>
@@ -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<typename Type>
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<long long>(value));
}
void bind(int index, uint value) { bind(index, static_cast<long long>(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;

View File

@@ -264,4 +264,12 @@ public:
}
};
class CannotConvert : public Exception
{
public:
CannotConvert(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
} // namespace Sqlite

View File

@@ -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 <utils/smallstring.h>
#include <utils/variant.h>
#include <QVariant>
#pragma once
namespace Sqlite {
enum class ValueType : unsigned char { Integer, Float, String };
template<typename StringType>
class ValueBase
{
public:
using VariantType = Utils::variant<long long, double, StringType>;
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<long long>(value))
{}
explicit ValueBase(uint value)
: value(static_cast<long long>(value))
{}
explicit ValueBase(double value)
: value(value)
{}
explicit ValueBase(Utils::SmallStringView value)
: value(value)
{}
long long toInteger() const { return Utils::get<int(ValueType::Integer)>(value); }
double toFloat() const { return Utils::get<int(ValueType::Float)>(value); }
Utils::SmallStringView toStringView() const
{
return Utils::get<int(ValueType::String)>(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<int(ValueType::Integer)>(&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<int(ValueType::Float)>(&first.value);
return maybeInteger && *maybeInteger == second;
}
friend bool operator==(const ValueBase &first, int second)
{
return first == static_cast<long long>(second);
}
friend bool operator==(int first, const ValueBase &second) { return second == first; }
friend bool operator==(const ValueBase &first, uint second)
{
return first == static_cast<long long>(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<int(ValueType::String)>(&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<int(ValueType::String)>(&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<Utils::SmallStringView>
{
public:
explicit ValueView(ValueBase &&base)
: ValueBase(std::move(base))
{}
template<typename Type>
static ValueView create(Type &&value)
{
return ValueView{ValueBase{value}};
}
};
class Value : public ValueBase<Utils::SmallString>
{
using Base = ValueBase<Utils::SmallString>;
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<typename Type>
friend bool operator!=(const Value &first, const Type &second)
{
return !(first == second);
}
template<typename Type>
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<long long>(value.toInt())};
case QVariant::LongLong:
return VariantType{value.toLongLong()};
case QVariant::UInt:
return VariantType{static_cast<long long>(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<Value>;
} // namespace Sqlite

View File

@@ -28,6 +28,8 @@
#include "theme/theme.h"
#include "hostosinfo.h"
#include <utils/qtcassert.h>
#include <QPixmapCache>
#include <QPainter>
#include <QApplication>
@@ -35,6 +37,7 @@
#include <QCommonStyle>
#include <QStyleOption>
#include <QWindow>
#include <QFontDatabase>
#include <qmath.h>
// Clamps float color values within (0, 255)
@@ -542,6 +545,48 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect)
return grad;
}
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color)
{
QFontDatabase a;
QTC_ASSERT(a.hasFamily(fontName), {});
if (a.hasFamily(fontName)) {
QIcon icon;
QSize size(iconSize, iconSize);
const int maxDpr = qRound(qApp->devicePixelRatio());
for (int dpr = 1; dpr <= maxDpr; dpr++) {
QPixmap pixmap(size * dpr);
pixmap.setDevicePixelRatio(dpr);
pixmap.fill(Qt::transparent);
QFont font(fontName);
font.setPixelSize(fontSize * dpr);
QPainter painter(&pixmap);
painter.save();
painter.setPen(color);
painter.setFont(font);
painter.drawText(QRectF(QPoint(0, 0), size), iconSymbol);
painter.restore();
icon.addPixmap(pixmap);
}
return icon;
}
return {};
}
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize)
{
QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText);
return getIconFromIconFont(fontName, iconSymbol, fontSize, iconSize, penColor);
}
QString StyleHelper::dpiSpecificImageFile(const QString &fileName)
{
// See QIcon::addFile()

View File

@@ -93,6 +93,9 @@ public:
static void tintImage(QImage &img, const QColor &tintColor);
static QLinearGradient statusBarGradient(const QRect &statusBarRect);
static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color);
static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize);
static QString dpiSpecificImageFile(const QString &fileName);
static QString imageFileWithResolution(const QString &fileName, int dpr);
static QList<int> availableImageResolutions(const QString &fileName);

View File

@@ -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

View File

@@ -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());

View File

@@ -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;
};

View File

@@ -33,13 +33,6 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QTextEdit" name="textEdit">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="authorLabel">
<property name="text">
@@ -64,7 +57,6 @@
<tabstops>
<tabstop>titleEdit</tabstop>
<tabstop>authorEdit</tabstop>
<tabstop>textEdit</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>487</height>
<width>1100</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">

View File

@@ -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 <coreplugin/icore.h>
#include <QObject>
#include <QToolBar>
#include <QAction>
#include <QMessageBox>
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

View File

@@ -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 <QObject>
#include <QtQml>
#include <QPointer>
#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<AnnotationEditorDialog> m_dialog;
ModelNode m_modelNode;
};
} //namespace QmlDesigner

View File

@@ -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");

View File

@@ -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)

View File

@@ -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

View File

@@ -295,9 +295,11 @@ void resetSize(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetSize",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
if (itemNode.isValid()) {
itemNode.removeProperty("width");
itemNode.removeProperty("height");
}
}
});
}
@@ -309,9 +311,11 @@ void resetPosition(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetPosition",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
if (itemNode.isValid()) {
itemNode.removeProperty("x");
itemNode.removeProperty("y");
}
}
});
}
@@ -332,6 +336,7 @@ void resetZ(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetZ",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
if (itemNode.isValid())
itemNode.removeProperty("z");
}
});
@@ -1082,6 +1087,23 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ
});
}
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
} //QmlDesigner

View File

@@ -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

View File

@@ -28,19 +28,46 @@
#include <qmldesignerplugin.h>
#include <coreplugin/icore.h>
#include <utils/stylehelper.h>
#include <QApplication>
#include <QRegExp>
#include <QScreen>
#include <QPointer>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlProperty>
#include <qqml.h>
static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg)
namespace QmlDesigner {
Theme::Theme(Utils::Theme *originTheme, QObject *parent)
: Utils::Theme(originTheme, parent)
, m_constants(nullptr)
{
QString constantsPath = Core::ICore::resourcePath() +
QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml");
QQmlEngine* engine = new QQmlEngine(this);
QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath));
if (component.status() == QQmlComponent::Ready) {
m_constants = component.create();
}
else if (component.status() == QQmlComponent::Error ) {
qCWarning(themeLog) << "Couldn't load" << constantsPath
<< "due to the following error(s):";
for (QQmlError error : component.errors())
qCWarning(themeLog) << error.toString();
}
else {
qCWarning(themeLog) << "Couldn't load" << constantsPath
<< "the status of the QQmlComponent is" << component.status();
}
}
QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName)
@@ -129,6 +156,25 @@ QPixmap Theme::getPixmap(const QString &id)
return QmlDesignerIconProvider::getPixmap(id);
}
QString Theme::getIconUnicode(Theme::Icon i)
{
if (!instance()->m_constants)
return QString();
const QMetaObject *m = instance()->metaObject();
const char *enumName = "Icon";
int enumIndex = m->indexOfEnumerator(enumName);
if (enumIndex == -1) {
qCWarning(themeLog) << "Couldn't find enum" << enumName;
return QString();
}
QMetaEnum e = m->enumerator(enumIndex);
return instance()->m_constants->property(e.valueToKey(i)).toString();
}
QColor Theme::qmlDesignerBackgroundColorDarker() const
{
return getColor(QmlDesigner_BackgroundColorDarker);

View File

@@ -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

View File

@@ -179,6 +179,17 @@ void Edit3DView::importsChanged(const QList<Import> &addedImports,
checkImports();
}
void Edit3DView::customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
{
Q_UNUSED(view)
Q_UNUSED(nodeList)
Q_UNUSED(data)
if (identifier == "asset_import_update")
resetPuppet();
}
void Edit3DView::sendInputEvent(QInputEvent *e) const
{
if (nodeInstanceView())
@@ -301,6 +312,7 @@ QVector<Edit3DAction *> Edit3DView::rightActions() const
void Edit3DView::addQuick3DImport()
{
if (model()) {
const QList<Import> imports = model()->possibleImports();
for (const auto &import : imports) {
if (import.url() == "QtQuick3D") {
@@ -311,6 +323,7 @@ void Edit3DView::addQuick3DImport()
return;
}
}
}
Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"),
tr("Could not add QtQuick3D import to project."));
}

View File

@@ -58,6 +58,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void sendInputEvent(QInputEvent *e) const;
void edit3DViewResized(const QSize &size) const;

View File

@@ -106,12 +106,11 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
// Onboarding label contains instructions for new users how to get 3D content into the project
m_onboardingLabel = new QLabel(this);
QString labelText =
"No 3D import here yet!<br><br>"
"To create a 3D View you need to add the QtQuick3D import to your file.<br>"
"You can add the import via the QML Imports tab of the Library view, or alternatively click"
tr("Your file does not import Qt Quick 3D.<br><br>"
"To create a 3D view, add the QtQuick3D import to your file in the QML Imports tab of the Library view. Or click"
" <a href=\"#add_import\"><span style=\"text-decoration:none;color:%1\">here</span></a> "
"to add it straight away.<br><br>"
"If you want to import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view.";
"here to add it immediately.<br><br>"
"To import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view.");
m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name()));
m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
connect(m_onboardingLabel, &QLabel::linkActivated, this, &Edit3DWidget::linkActivated);

View File

@@ -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

View File

@@ -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);

View File

@@ -32,10 +32,13 @@
#include <nodehints.h>
#include <nodemetainfo.h>
#include <theme.h>
#include <utils/theme/theme.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QFontDatabase>
#include <QPainter>
#include <QPainterPath>
#include <QStyleOptionGraphicsItem>
@@ -47,6 +50,36 @@
namespace QmlDesigner {
const int flowBlockSize = 200;
const int blockRadius = 18;
const int blockAdjust = 40;
const char startNodeIcon[] = "\u0055";
void drawIcon(QPainter *painter,
int x,
int y,
const QString &iconSymbol,
int fontSize, int iconSize,
const QColor &penColor)
{
static QFontDatabase a;
const QString fontName = "qtds_propertyIconFont.ttf";
Q_ASSERT(a.hasFamily(fontName));
if (a.hasFamily(fontName)) {
QFont font(fontName);
font.setPixelSize(fontSize);
painter->save();
painter->setPen(penColor);
painter->setFont(font);
painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
painter->restore();
}
}
FormEditorScene *FormEditorItem::scene() const {
return qobject_cast<FormEditorScene*>(QGraphicsItem::scene());
@@ -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<QColor>();
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<QColor>();
painter->save();
if (m_iconType == DecisionIcon) {
painter->translate(boundingRect().center());
painter->rotate(45);
painter->translate(-boundingRect().center());
}
if (fillColor.alpha() > 0)
painter->fillRect(boundingRect(), fillColor);
painter->setBrush(fillColor);
painter->drawLine(boundingRect().left(), boundingRect().center().y(),
boundingRect().center().x(), boundingRect().top());
int radius = blockRadius;
painter->drawLine(boundingRect().center().x(), boundingRect().top(),
boundingRect().right(), boundingRect().center().y());
const QRectF adjustedRect = boundingRect().adjusted(blockAdjust,
blockAdjust,
-blockAdjust,
-blockAdjust);
painter->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();
}

View File

@@ -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

View File

@@ -0,0 +1,438 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitiontool.h"
#include <formeditorscene.h>
#include <formeditorview.h>
#include <formeditorwidget.h>
#include <itemutilfunctions.h>
#include <formeditoritem.h>
#include <layeritem.h>
#include <resizehandleitem.h>
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <qmlitemnode.h>
#include <qmldesignerplugin.h>
#include <abstractaction.h>
#include <designeractionmanager.h>
#include <variantproperty.h>
#include <rewritingexception.h>
#include <rewritertransaction.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QAction>
#include <QMessageBox>
#include <QPair>
#include <QGraphicsSceneMouseEvent>
namespace QmlDesigner {
static bool isTransitionSource(const ModelNode &node)
{
return QmlFlowTargetNode::isFlowEditorTarget(node);
}
static bool isTransitionTarget(const QmlItemNode &node)
{
return QmlFlowTargetNode::isFlowEditorTarget(node)
&& !node.isFlowActionArea()
&& !node.isFlowWildcard();
}
class TransitionToolAction : public AbstractAction
{
public:
TransitionToolAction(const QString &name) : AbstractAction(name) {}
QByteArray category() const override
{
return QByteArray();
}
QByteArray menuId() const override
{
return "TransitionTool";
}
int priority() const override
{
return CustomActionsPriority;
}
Type type() const override
{
return ContextMenuAction;
}
protected:
bool isVisible(const SelectionContext &selectionContext) const override
{
if (selectionContext.scenePosition().isNull())
return false;
if (selectionContext.singleNodeIsSelected())
return isTransitionSource(selectionContext.currentSingleSelectedNode());
return false;
}
bool isEnabled(const SelectionContext &selectionContext) const override
{
return isVisible(selectionContext);
}
};
class TransitionCustomAction : public TransitionToolAction
{
public:
TransitionCustomAction(const QString &name) : TransitionToolAction(name) {}
QByteArray category() const override
{
return ComponentCoreConstants::flowCategory;
}
SelectionContext selectionContext() const
{
return AbstractAction::selectionContext();
}
};
static QRectF paintedBoundingRect(FormEditorItem *item)
{
QRectF boundingRect = item->qmlItemNode().instanceBoundingRect();
if (boundingRect.width() < 4)
boundingRect = item->boundingRect();
return boundingRect;
}
static QPointF centerPoint(FormEditorItem *item)
{
QRectF boundingRect = paintedBoundingRect(item);
return QPointF(item->scenePos().x() + boundingRect.width() / 2,
item->scenePos().y() + boundingRect.height() / 2);
}
void static setToBoundingRect(QGraphicsRectItem *rect, FormEditorItem *item)
{
QPolygonF boundingRectInSceneSpace(item->mapToScene(paintedBoundingRect(item)));
rect->setRect(boundingRectInSceneSpace.boundingRect());
}
TransitionTool::TransitionTool()
: QObject(), AbstractCustomTool()
{
TransitionToolAction *transitionToolAction = new TransitionToolAction(tr("Add Transition"));
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(transitionToolAction);
connect(transitionToolAction->action(), &QAction::triggered,
this, &TransitionTool::activateTool);
TransitionCustomAction *removeAction = new TransitionCustomAction(tr("Remove Transitions"));
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAction);
connect(removeAction->action(), &QAction::triggered,
this, [removeAction](){
SelectionContext context = removeAction->selectionContext();
QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
context.view()->executeInTransaction("Remove Transitions", [&node](){
if (node.isValid())
node.removeTransitions();
});
});
TransitionCustomAction *removeAllTransitionsAction = new TransitionCustomAction(tr("Remove All Transitions"));
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAllTransitionsAction);
connect(removeAllTransitionsAction->action(), &QAction::triggered,
this, [removeAllTransitionsAction](){
if (QMessageBox::question(Core::ICore::dialogParent(),
tr("Remove All Transitions"),
tr("Do you really want to remove all transitions?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
SelectionContext context = removeAllTransitionsAction->selectionContext();
QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
context.view()->executeInTransaction("Remove All Transitions", [&node](){
if (node.isValid() && node.flowView().isValid())
node.flowView().removeAllTransitions();
});
});
TransitionCustomAction *removeDanglingTransitionAction = new TransitionCustomAction(tr("Remove Dangling Transitions"));
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeDanglingTransitionAction);
connect(removeDanglingTransitionAction->action(), &QAction::triggered,
this, [removeDanglingTransitionAction](){
SelectionContext context = removeDanglingTransitionAction->selectionContext();
QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
context.view()->executeInTransaction("Remove Dangling Transitions", [&node](){
if (node.isValid() && node.flowView().isValid())
node.flowView().removeDanglingTransitions();
});
});
}
TransitionTool::~TransitionTool()
{
}
void TransitionTool::clear()
{
m_lineItem.reset(nullptr);
m_rectangleItem1.reset(nullptr);
m_rectangleItem2.reset(nullptr);
AbstractFormEditorTool::clear();
}
void TransitionTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event)
{
if (m_blockEvents)
return;
if (event->button() != Qt::LeftButton)
return;
AbstractFormEditorTool::mousePressEvent(itemList, event);
TransitionTool::mouseMoveEvent(itemList, event);
}
void TransitionTool::mouseMoveEvent(const QList<QGraphicsItem*> & itemList,
QGraphicsSceneMouseEvent * event)
{
if (!m_lineItem)
return;
QTC_ASSERT(currentFormEditorItem(), return);
const QPointF pos = centerPoint(m_formEditorItem);
lineItem()->setLine(pos.x(),
pos.y(),
event->scenePos().x(),
event->scenePos().y());
FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
if (formEditorItem
&& formEditorItem->qmlItemNode().isValid()
&& isTransitionTarget(formEditorItem->qmlItemNode().modelNode())) {
rectangleItem2()->setVisible(true);
setToBoundingRect(rectangleItem2(), formEditorItem);
} else {
rectangleItem2()->setVisible(false);
}
}
void TransitionTool::hoverMoveEvent(const QList<QGraphicsItem*> & itemList,
QGraphicsSceneMouseEvent *event)
{
mouseMoveEvent(itemList, event);
}
void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/)
{
}
void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/)
{
view()->changeToSelectionTool();
}
void TransitionTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
{
}
void TransitionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
{
}
void TransitionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event)
{
if (m_blockEvents)
return;
if (event->button() == Qt::LeftButton) {
FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
if (formEditorItem
&& QmlFlowTargetNode(formEditorItem->qmlItemNode().modelNode()).isValid())
createTransition(m_formEditorItem, formEditorItem);
}
view()->changeToSelectionTool();
}
void TransitionTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
{
AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event);
}
void TransitionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &)
{
view()->changeCurrentToolTo(this);
}
void TransitionTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
{
if (!itemList.isEmpty()) {
createItems();
m_formEditorItem = itemList.first();
setToBoundingRect(rectangleItem1(), m_formEditorItem);
}
}
void TransitionTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
{
}
void TransitionTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
{
}
void TransitionTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/)
{
}
void TransitionTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
{
}
int TransitionTool::wantHandleItem(const ModelNode &modelNode) const
{
if (isTransitionSource(modelNode))
return 10;
return 0;
}
QString TransitionTool::name() const
{
return tr("Transition Tool");
}
void TransitionTool::activateTool()
{
view()->changeToCustomTool();
}
void TransitionTool::unblock()
{
m_blockEvents = false;
}
QGraphicsLineItem *TransitionTool::lineItem()
{
return m_lineItem.get();
}
QGraphicsRectItem *TransitionTool::rectangleItem1()
{
return m_rectangleItem1.get();
}
QGraphicsRectItem *TransitionTool::rectangleItem2()
{
return m_rectangleItem2.get();
}
FormEditorItem *TransitionTool::currentFormEditorItem() const
{
if (scene()->items().contains(m_formEditorItem))
return m_formEditorItem;
return nullptr;
}
void TransitionTool::createItems() {
m_blockEvents = true;
QTimer::singleShot(200, this, [this](){ unblock(); });
if (!lineItem())
m_lineItem.reset(new QGraphicsLineItem(scene()->manipulatorLayerItem()));
if (!rectangleItem1())
m_rectangleItem1.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
if (!rectangleItem2())
m_rectangleItem2.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
m_rectangleItem2->setVisible(false);
QPen pen;
pen.setColor(QColor(Qt::lightGray));
pen.setStyle(Qt::DashLine);
pen.setWidth(0);
m_lineItem->setPen(pen);
pen.setColor(QColor(108, 141, 221));
pen.setStyle(Qt::SolidLine);
pen.setWidth(4);
pen.setCosmetic(true);
m_rectangleItem1->setPen(pen);
m_rectangleItem2->setPen(pen);
}
void TransitionTool::createTransition(FormEditorItem *source, FormEditorItem *target)
{
QmlFlowTargetNode sourceNode(source->qmlItemNode().modelNode());
QmlFlowTargetNode targetNode(target->qmlItemNode().modelNode());
if (sourceNode.isValid() && targetNode.isValid()
&& sourceNode != targetNode
&& !targetNode.isFlowActionArea()
&& !targetNode.isFlowWildcard()) {
view()->executeInTransaction("create transition", [&sourceNode, targetNode](){
sourceNode.assignTargetItem(targetNode);
});
} else {
qWarning() << Q_FUNC_INFO << "nodes invalid";
}
}
}

View File

@@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "abstractcustomtool.h"
#include "selectionindicator.h"
#include <QGraphicsLineItem>
#include <QHash>
#include <QPointer>
#include <memory>
namespace QmlDesigner {
class TransitionTool : public QObject, public AbstractCustomTool
{
Q_OBJECT
public:
TransitionTool();
~TransitionTool();
void mousePressEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event) override;
void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *keyEvent) override;
void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneDragDropEvent * event) override;
void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
QGraphicsSceneDragDropEvent * event) override;
void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
void clear() override;
void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
int wantHandleItem(const ModelNode &modelNode) const override;
QString name() const override;
void activateTool();
void unblock();
QGraphicsLineItem *lineItem();
QGraphicsRectItem *rectangleItem1();
QGraphicsRectItem *rectangleItem2();
private:
FormEditorItem *currentFormEditorItem() const;
void createItems();
void createTransition(FormEditorItem *item1, FormEditorItem *item2);
FormEditorItem* m_formEditorItem;
std::unique_ptr<QGraphicsLineItem> m_lineItem;
std::unique_ptr<QGraphicsRectItem> m_rectangleItem1;
std::unique_ptr<QGraphicsRectItem> m_rectangleItem2;
bool m_blockEvents = true;
};
} //QmlDesigner

View File

@@ -81,14 +81,16 @@ void ImportManagerView::modelAboutToBeDetached(Model *model)
void ImportManagerView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
{
if (m_importsWidget)
if (m_importsWidget) {
m_importsWidget->setImports(model()->imports());
// setImports recreates labels, so we need to update used imports, as it is not guaranteed
// usedImportsChanged notification will come after this.
m_importsWidget->setUsedImports(model()->usedImports());
}
}
void ImportManagerView::possibleImportsChanged(const QList<Import> &/*possibleImports*/)
{
QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
if (m_importsWidget)
m_importsWidget->setPossibleImports(model()->possibleImports());
}

View File

@@ -32,6 +32,9 @@
#include "utils/outputformatter.h"
#include "theme.h"
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
@@ -97,6 +100,20 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc) {
Model *model = doc->currentModel();
if (model)
importPaths = model->importPaths();
}
QString targetDir = defaulTargetDirectory;
ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(doc->fileName());
if (currentProject)
targetDir = currentProject->projectDirectory().toString();
// Import is always done under known folder. The order of preference for folder is:
// 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
// 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path
@@ -105,19 +122,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
// 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project
const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER);
const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
QString candidatePath = defaulTargetDirectory + defaultAssetFolder + quick3DFolder;
QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder;
int candidatePriority = 5;
QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc) {
Model *model = doc->currentModel();
if (model)
importPaths = model->importPaths();
}
for (auto importPath : qAsConst(importPaths)) {
if (importPath.startsWith(defaulTargetDirectory)) {
if (importPath.startsWith(targetDir)) {
const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder);
const QString assetFolder = importPath + quick3DFolder;
const bool exists = QFileInfo(assetFolder).exists();

View File

@@ -280,7 +280,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
return;
}
QString originalAssetName = assetName;
if (targetDir.exists(assetName)) {
// If we have a file system with case insensitive filenames, assetName may be
// different from the existing name. Modify assetName to ensure exact match to
// the overwritten old asset capitalization
const QStringList assetDirs = targetDir.entryList({assetName}, QDir::Dirs);
if (assetDirs.size() == 1) {
assetName = assetDirs[0];
targetDirPath = targetDir.filePath(assetName);
}
if (!confirmAssetOverwrite(assetName)) {
addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName));
return;
@@ -306,6 +315,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
return;
}
// The importer is reset after every import to avoid issues with it caching various things
m_quick3DAssetImporter.reset(new QSSGAssetImportManager);
if (originalAssetName != assetName) {
// Fix the generated qml file name
const QString assetQml = originalAssetName + ".qml";
if (outDir.exists(assetQml))
outDir.rename(assetQml, assetName + ".qml");
}
QHash<QString, QString> assetFiles;
const int outDirPathSize = outDir.path().size();
auto insertAsset = [&](const QString &filePath) {
@@ -512,18 +531,24 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
addInfo(progressTitle);
notifyProgress(0, progressTitle);
// There is an inbuilt delay before rewriter change actually updates the data model,
// so we need to wait for a moment to allow the change to take effect.
// First we have to wait a while to ensure qmljs detects new files and updates its
// internal model. Then we make a non-change to the document to trigger qmljs snapshot
// update. There is an inbuilt delay before rewriter change actually updates the data
// model, so we need to wait for another moment to allow the change to take effect.
// Otherwise subsequent subcomponent manager update won't detect new imports properly.
QTimer *timer = new QTimer(parent());
static int counter;
counter = 0;
timer->callOnTimeout([this, timer, progressTitle, model]() {
timer->callOnTimeout([this, timer, progressTitle, model, doc]() {
if (!isCancelled()) {
notifyProgress(++counter * 10, progressTitle);
if (counter >= 10) {
// Trigger underlying qmljs snapshot update by making a non-change to the doc
notifyProgress(++counter * 5, progressTitle);
if (counter == 10) {
model->rewriterView()->textModifier()->replace(0, 0, {});
} else if (counter == 19) {
doc->updateSubcomponentManager();
} else if (counter >= 20) {
if (!m_overwrittenImports.isEmpty())
model->rewriterView()->emitCustomNotification("asset_import_update");
timer->stop();
notifyFinished();
}

View File

@@ -43,6 +43,7 @@ public:
virtual void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) = 0;
virtual void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) = 0;
virtual void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) = 0;
virtual void notifyIconsChanged() = 0;
virtual void setFilter(bool showObjects) = 0;
virtual void resetModel() = 0;
};

View File

@@ -695,6 +695,11 @@ void NavigatorTreeModel::notifyModelNodesMoved(const QList<ModelNode> &modelNode
emit layoutChanged(indexes);
}
void NavigatorTreeModel::notifyIconsChanged()
{
emit dataChanged(index(0, 0), index(rowCount(), 0), {Qt::DecorationRole});
}
void NavigatorTreeModel::setFilter(bool showOnlyVisibleItems)
{
m_showOnlyVisibleItems = showOnlyVisibleItems;

View File

@@ -87,6 +87,7 @@ public:
void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) override;
void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) override;
void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) override;
void notifyIconsChanged() override;
void setFilter(bool showOnlyVisibleItems) override;
void resetModel() override;

View File

@@ -147,6 +147,17 @@ void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & prop
}
}
void NavigatorView::customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
{
Q_UNUSED(view)
Q_UNUSED(nodeList)
Q_UNUSED(data)
if (identifier == "asset_import_update")
m_currentModelInterface->notifyIconsChanged();
}
void NavigatorView::handleChangedExport(const ModelNode &modelNode, bool exported)
{
const ModelNode rootNode = rootModelNode();
@@ -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);

View File

@@ -84,6 +84,8 @@ public:
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags) override;
void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void handleChangedExport(const ModelNode &modelNode, bool exported);
bool isNodeInvisible(const ModelNode &modelNode) const;
@@ -108,7 +110,7 @@ protected: //functions
QTreeView *treeWidget() const;
NavigatorTreeModel *treeModel();
bool blockSelectionChangedSignal(bool block);
void expandRecursively(const QModelIndex &index);
void expandAncestors(const QModelIndex &index);
void reparentAndCatch(NodeAbstractProperty property, const ModelNode &modelNode);
void setupWidget();

View File

@@ -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 <QPushButton>
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);
}
}

View File

@@ -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 <QDialog>
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;
};
}

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::HyperlinkDialog</class>
<widget class="QDialog" name="QmlDesigner::HyperlinkDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>403</width>
<height>156</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Link</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="linkEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Anchor</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="anchorEdit"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QmlDesigner::HyperlinkDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QmlDesigner::HyperlinkDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -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 <functional>
#include <QToolButton>
#include <QAction>
#include <QStyle>
#include <QStyleFactory>
#include <QColorDialog>
#include <QWidgetAction>
#include <QTextTable>
#include <QScopeGuard>
#include <QPointer>
#include <utils/stylehelper.h>
namespace QmlDesigner {
template <class T>
class FontWidgetActions : public QWidgetAction {
public:
FontWidgetActions(QObject *parent = nullptr)
: QWidgetAction(parent) {}
~FontWidgetActions () override {}
void setInitializer(std::function<void(T*)> func)
{
m_initializer = func;
}
QList<QWidget *> 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<void(T*)> m_initializer;
};
static void cursorEditBlock(QTextCursor& cursor, std::function<void()> 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<QFontComboBox*>(w);
if (box)
box->setCurrentFont(f);
}
for (QWidget* w: m_fontSizeAction->createdWidgets() ) {
QComboBox* box = qobject_cast<QComboBox*>(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<QFontComboBox>(this);
m_fontNameAction->setInitializer([this](QFontComboBox *w) {
if (!w) return;
w->setCurrentIndex(w->findText(ui->textEdit->currentCharFormat().font().family()));
connect(w, QOverload<const QString &>::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<QComboBox>(this);
m_fontSizeAction->setInitializer([this](QComboBox *w) {
if (!w) return;
w->setEditable(true);
const QList<int> standardSizes = QFontDatabase::standardSizes();
foreach (int size, standardSizes)
w->addItem(QString::number(size));
w->setCurrentText(QString::number(ui->textEdit->currentCharFormat().font().pointSize()));
connect(w, QOverload<const QString &>::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);
}
}

View File

@@ -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 <theme.h>
#include <QWidget>
#include <QToolBar>
#include <QList>
#include <QTextCharFormat>
#include <QTextList>
#include <QFontComboBox>
#include <QWidgetAction>
#include <QPointer>
namespace QmlDesigner {
namespace Ui {
class RichTextEditor;
}
template <class>
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::RichTextEditor> ui;
QPointer<HyperlinkDialog> 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<FontWidgetActions<QFontComboBox>> m_fontNameAction;
QPointer<FontWidgetActions<QComboBox>> m_fontSizeAction;
};
} //namespace QmlDesigner

View File

@@ -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

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::RichTextEditor</class>
<widget class="QWidget" name="QmlDesigner::RichTextEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>428</width>
<height>283</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolBar" name="toolBar">
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolBar" name="tableBar">
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -58,6 +58,7 @@
#include <QSlider>
#include <QVBoxLayout>
#include <QtGlobal>
#include <QSpacerItem>
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. <br><br> \
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);
}
}

View File

@@ -104,6 +104,8 @@ private:
TimelineGraphicsScene *m_graphicsScene;
QPushButton *m_addButton = nullptr;
QWidget *m_onboardingContainer = nullptr;
};
} // namespace QmlDesigner

View File

@@ -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();
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
return false;
equal = ast->version->minorVersion == 0;
}
}
}
return equal;
}

View File

@@ -35,6 +35,7 @@ namespace QmlDesigner {
static const PropertyName customIdProperty = {("customId")};
static const PropertyName annotationProperty = {("annotation")};
static const PropertyName globalAnnotationProperty = {("globalAnnotation")};
class Comment
{

View File

@@ -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&);

View File

@@ -182,8 +182,13 @@ public:
const QList<ModelNode> wildcards() const;
const QList<ModelNode> decicions() const;
QList<ModelNode> transitionsForTarget(const ModelNode &modelNode);
QList<ModelNode> transitionsForSource(const ModelNode &modelNode);
void removeDanglingTransitions();
void removeAllTransitions();
void setStartFlowItem(const QmlFlowItemNode &flowItem);
ModelNode createTransition();
protected:
QList<ModelNode> transitionsForProperty(const PropertyName &propertyName, const ModelNode &modelNode);
};

View File

@@ -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);
}
}
}

View File

@@ -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<QString>());
return result;
}
bool ModelNode::hasGlobalAnnotation() const
{
return view()->rootModelNode().hasAuxiliaryData(globalAnnotationProperty);
}
void ModelNode::setGlobalAnnotation(const Annotation &annotation)
{
view()->rootModelNode().setAuxiliaryData(globalAnnotationProperty,
QVariant::fromValue<QString>(annotation.toQString()));
}
void ModelNode::removeGlobalAnnotation()
{
if (hasGlobalAnnotation()) {
view()->rootModelNode().removeAuxiliaryData(globalAnnotationProperty);
}
}
void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
{
model()->d->setScriptFunctions(internalNode(), scriptFunctionList);

View File

@@ -664,9 +664,7 @@ QList<QmlFlowItemNode> QmlFlowViewNode::flowItems() const
ModelNode QmlFlowViewNode::addTransition(const QmlFlowTargetNode &from, const QmlFlowTargetNode &to)
{
ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0);
nodeListProperty("flowTransitions").reparentHere(transition);
ModelNode transition = createTransition();
QmlFlowTargetNode f = from;
QmlFlowTargetNode t = to;
@@ -684,8 +682,6 @@ const QList<ModelNode> QmlFlowViewNode::transitions() const
return modelNode().nodeListProperty("flowTransitions").toModelNodeList();
return {};
}
const QList<ModelNode> QmlFlowViewNode::wildcards() const
@@ -706,13 +702,12 @@ const QList<ModelNode> QmlFlowViewNode::decicions() const
QList<ModelNode> QmlFlowViewNode::transitionsForTarget(const ModelNode &modelNode)
{
QList<ModelNode> list;
for (const ModelNode &transition : transitions()) {
if (transition.hasBindingProperty("to")
&& transition.bindingProperty("to").resolveToModelNode() == modelNode)
list.append(transition);
}
return list;
return transitionsForProperty("to", modelNode);
}
QList<ModelNode> QmlFlowViewNode::transitionsForSource(const ModelNode &modelNode)
{
return transitionsForProperty("from", modelNode);
}
void QmlFlowViewNode::removeDanglingTransitions()
@@ -830,4 +825,41 @@ void QmlFlowViewNode::removeAllTransitions()
removeProperty("flowTransitions");
}
void QmlFlowViewNode::setStartFlowItem(const QmlFlowItemNode &flowItem)
{
QTC_ASSERT(flowItem.isValid(), return);
QmlFlowItemNode item = flowItem;
ModelNode transition;
for (const ModelNode &node : transitionsForSource(modelNode()))
transition = node;
if (!transition.isValid())
transition = createTransition();
transition.bindingProperty("from").setExpression(modelNode().validId());
transition.bindingProperty("to").setExpression(item.validId());
}
ModelNode QmlFlowViewNode::createTransition()
{
ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0);
nodeListProperty("flowTransitions").reparentHere(transition);
return transition;
}
QList<ModelNode> QmlFlowViewNode::transitionsForProperty(const PropertyName &propertyName,
const ModelNode &modelNode)
{
QList<ModelNode> list;
for (const ModelNode &transition : transitions()) {
if (transition.hasBindingProperty(propertyName)
&& transition.bindingProperty(propertyName).resolveToModelNode() == modelNode)
list.append(transition);
}
return list;
}
} //QmlDesigner

View File

@@ -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;
}

View File

@@ -60,6 +60,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <QSettings>
#include <QToolBar>
@@ -70,6 +71,7 @@
#include <advanceddockingsystem/dockareawidget.h>
#include <advanceddockingsystem/docksplitter.h>
#include <advanceddockingsystem/iconprovider.h>
using Core::MiniSplitter;
using Core::IEditor;
@@ -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();

View File

@@ -36,6 +36,7 @@
#include <QScopedPointer>
#include <advanceddockingsystem/dockmanager.h>
#include <annotationeditor/globalannotationeditor.h>
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

View File

@@ -38,6 +38,7 @@
#include <sourcetool/sourcetool.h>
#include <colortool/colortool.h>
#include <annotationeditor/annotationtool.h>
#include <formeditor/transitiontool.h>
#include <texttool/texttool.h>
#include <timelineeditor/timelineview.h>
#include <pathtool/pathtool.h>
@@ -216,6 +217,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool())
GenerateResource::generateMenuEntry();
QString fontPath = Core::ICore::resourcePath() +
QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf");
if (QFontDatabase::addApplicationFont(fontPath) < 0)
qCWarning(qmldesignerLog) << "Could not add font " << fontPath << "to font database";
return true;
}
@@ -242,6 +248,7 @@ bool QmlDesignerPlugin::delayedInitialize()
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TextTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::PathTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TransitionTool);
return true;
}

View File

@@ -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)

View File

@@ -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",

View File

@@ -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

View File

@@ -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)

View File

@@ -37,6 +37,7 @@
#include <clangcodemodelservermessages.h>
#include <clangpathwatcher.h>
#include <clangrefactoringmessages.h>
#include <clangtools/clangtoolsdiagnostic.h>
#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/locator/ilocatorfilter.h>
#include <cpptools/usages.h>
@@ -57,6 +58,7 @@
#include <sourcedependency.h>
#include <sourcelocationentry.h>
#include <sourcelocationscontainer.h>
#include <sqlitevalue.h>
#include <symbol.h>
#include <symbolentry.h>
#include <symbolindexertaskqueue.h>
@@ -64,12 +66,6 @@
#include <tooltipinfo.h>
#include <usedmacro.h>
#include <utils/link.h>
#include <cpptools/usages.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/headerpath.h>
#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/locator/ilocatorfilter.h>
#include <clangtools/clangtoolsdiagnostic.h>
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)

View File

@@ -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;

View File

@@ -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<Utils::SmallString>(1), "23.3");
ASSERT_THAT(statement.fetchValue<Utils::PathString>(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<int>(2), Sqlite::InvalidColumnFetched);
}
TEST_F(SqliteStatement, ToIntergerValue)
TEST_F(SqliteStatement, ToIntegerValue)
{
auto value = ReadStatement::toValue<int>("SELECT number FROM test WHERE name='foo'", database);
ASSERT_THAT(value, 23);
}
TEST_F(SqliteStatement, ToLongIntergerValue)
TEST_F(SqliteStatement, ToLongIntegerValue)
{
ASSERT_THAT(ReadStatement::toValue<qint64>("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<typename Type>
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<FooValue> values = statement.values<FooValue>(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);

View File

@@ -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 <sqlitevalue.h>
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

View File

@@ -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) {