From 46ddfb9badb5fdda97afe4e67667ad56e5d69f35 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 4 Feb 2019 17:32:23 +0100 Subject: [PATCH] QmlDesigner: Add support for ShapeGradient This adds support for ShapeGradient in the property editor. * LinearGradient * RadialGradient * ConicalGradient The custom properties can be edited using a small popup. Task-number: QDS-359 Change-Id: I6232a4685f2d919dcab3efbb14a70c3617aea36c Reviewed-by: Brook Cronin Reviewed-by: Leena Miettinen Reviewed-by: Tim Jenssen Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/ColorEditor.qml | 291 +++++++++++++++++- .../HelperWidgets/GradientDialogPopup.qml | 135 ++++++++ .../imports/HelperWidgets/GradientLine.qml | 1 + .../HelperWidgets/GradientPopupIndicator.qml | 51 +++ .../HelperWidgets/GradientPropertySpinBox.qml | 42 +++ .../images/icon_color_conical_gradient.png | Bin 0 -> 670 bytes .../images/icon_color_radial_gradient.png | Bin 0 -> 677 bytes .../propertyeditor/gradientmodel.cpp | 190 ++++++++++-- .../components/propertyeditor/gradientmodel.h | 16 +- 9 files changed, 683 insertions(+), 43 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientDialogPopup.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPopupIndicator.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_conical_gradient.png create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_radial_gradient.png diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 583c324ac23..d10f302f6ab 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -26,7 +26,8 @@ import QtQuick 2.1 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 as Controls - +import QtQuickDesignerTheme 1.0 +import QtQuick.Controls.Styles 1.1 Column { id: colorEditor @@ -45,19 +46,17 @@ Column { property alias gradientPropertyName: gradientLine.gradientPropertyName + property bool shapeGradients: false + property alias transparent: transparentButton.checked function isNotInGradientMode() { - return (buttonRow.checkedIndex !== 1) + return (buttonRow.checkedIndex !== 1) } - onValueChanged: { - colorEditor.color = colorEditor.value - } + onValueChanged: colorEditor.color = colorEditor.value - onBackendValueChanged: { - colorEditor.color = colorEditor.value - } + onBackendValueChanged: colorEditor.color = colorEditor.value Timer { id: colorEditorTimer @@ -87,7 +86,13 @@ Column { GradientLine { property bool isInValidState: false - visible: buttonRow.checkedIndex === 1 + visible: { + if (colorEditor.shapeGradients) { + return buttonRow.checkedIndex > 0 && buttonRow.checkedIndex < 4 + } else { + return buttonRow.checkedIndex === 1 + } + } id: gradientLine width: parent.width @@ -98,11 +103,26 @@ Column { } onHasGradientChanged: { - if (!supportGradient) - return + if (!supportGradient) + return if (gradientLine.hasGradient) { - buttonRow.initalChecked = 1 + if (colorEditor.shapeGradients) { + switch (gradientLine.gradientTypeName) { + case "LinearGradient": + buttonRow.initalChecked = 1 + break; + case "RadialGradient": + buttonRow.initalChecked = 2 + break; + case "ConicalGradient": + buttonRow.initalChecked = 3 + break; + default: + buttonRow.initalChecked = 1 + } + } + colorEditor.color = gradientLine.currentColor } else { buttonRow.initalChecked = 0 @@ -155,6 +175,11 @@ Column { SecondColumnLayout { + ColorCheckButton { + id: checkButton + color: colorEditor.color + } + LineEdit { enabled: !colorEditor.transparent id: textField @@ -181,10 +206,6 @@ Column { Layout.fillWidth: true } - ColorCheckButton { - id: checkButton - color: colorEditor.color - } ButtonRow { @@ -193,6 +214,7 @@ Column { ButtonRowButton { iconSource: "images/icon_color_solid.png" + onClicked: { gradientLine.deleteGradient() textField.text = colorEditor.color @@ -205,10 +227,245 @@ Column { iconSource: "images/icon_color_gradient.png" onClicked: { colorEditor.backendValue.resetValue() + if (colorEditor.shapeGradients) { + gradientLine.deleteGradient() + gradientLine.gradientTypeName = "LinearGradient" + } gradientLine.addGradient() } - tooltip: qsTr("Gradient") + tooltip: qsTr("Linear Gradient") + + GradientPopupIndicator { + + onClicked: gradientDialogPopupLinear.toggle() + + GradientDialogPopup { + id: gradientDialogPopupLinear + + dialogHeight: 80 + content: GridLayout { + rowSpacing: 4 + anchors.fill: parent + height: 40 + + columns: 4 + rows: 2 + + anchors.leftMargin: 12 + anchors.rightMargin: 6 + + anchors.topMargin: 24 + anchors.bottomMargin: 6 + + Label { + text: "X1" + width: 16 + tooltip: qsTr("Defines the start point for color interpolation.") + } + + GradientPropertySpinBox { + propertyName: "x1" + } + + Label { + text: "X2" + width: 16 + tooltip: qsTr("Defines the end point for color interpolation.") + } + + GradientPropertySpinBox { + propertyName: "x2" + } + + Label { + text: "y1" + width: 16 + tooltip: qsTr("Defines the start point for color interpolation.") + } + + GradientPropertySpinBox { + propertyName: "y1" + } + + Label { + text: "Y2" + width: 16 + tooltip: qsTr("Defines the end point for color interpolation.") + } + + GradientPropertySpinBox { + propertyName: "y2" + } + } + } + } + } + ButtonRowButton { + visible: supportGradient && colorEditor.shapeGradients + iconSource: "images/icon_color_radial_gradient.png" + onClicked: { + colorEditor.backendValue.resetValue() + if (colorEditor.shapeGradients) { + gradientLine.deleteGradient() + gradientLine.gradientTypeName = "RadialGradient" + } + gradientLine.addGradient() + } + + tooltip: qsTr("Radial Gradient") + + GradientPopupIndicator { + onClicked: gradientDialogPopupRadial.toggle() + + GradientDialogPopup { + id: gradientDialogPopupRadial + dialogHeight: 140 + dialogWidth: 340 + content: GridLayout { + rowSpacing: 4 + anchors.fill: parent + height: 40 + + columns: 4 + rows: 3 + + anchors.leftMargin: 12 + anchors.rightMargin: 6 + + anchors.topMargin: 24 + anchors.bottomMargin: 6 + + Label { + text: "CenterX" + width: 54 + tooltip: qsTr("Defines the center point.") + } + + GradientPropertySpinBox { + propertyName: "centerX" + } + + Label { + text: "CenterY" + width: 54 + tooltip: qsTr("Defines the center point.") + } + + GradientPropertySpinBox { + propertyName: "centerY" + } + + Label { + text: "FocalX" + width: 54 + tooltip: qsTr("Defines the focal point.") + } + + GradientPropertySpinBox { + propertyName: "focalX" + } + + Label { + text: "FocalY" + width: 54 + tooltip: qsTr("Defines the focal point.") + } + + GradientPropertySpinBox { + propertyName: "focalY" + } + + Label { + text: "Center Radius" + width: 54 + tooltip: qsTr("Defines the center point.") + } + + GradientPropertySpinBox { + propertyName: "centerRadius" + } + + Label { + text: "Focal Radius" + width: 54 + tooltip: qsTr("Defines the focal radius. Set to 0 for simple radial gradients.") + } + + GradientPropertySpinBox { + propertyName: "focalRadius" + } + } + } + } + } + ButtonRowButton { + visible: supportGradient && colorEditor.shapeGradients + iconSource: "images/icon_color_conical_gradient.png" + onClicked: { + colorEditor.backendValue.resetValue() + if (colorEditor.shapeGradients) { + gradientLine.deleteGradient() + gradientLine.gradientTypeName = "ConicalGradient" + } + gradientLine.addGradient() + } + + tooltip: qsTr("Concial Gradient") + + GradientPopupIndicator { + + onClicked: gradientDialogPopupConical.toggle() + + GradientDialogPopup { + id: gradientDialogPopupConical + dialogHeight: 80 + content: GridLayout { + rowSpacing: 4 + anchors.fill: parent + height: 40 + + columns: 4 + rows: 2 + + anchors.leftMargin: 12 + anchors.rightMargin: 6 + + anchors.topMargin: 24 + anchors.bottomMargin: 6 + + Label { + text: "CenterX" + width: 32 + tooltip: qsTr("Defines the center point.") + } + + GradientPropertySpinBox { + propertyName: "centerX" + } + + Label { + text: "CenterY" + width: 32 + tooltip: qsTr("Defines the center point.") + } + + GradientPropertySpinBox { + propertyName: "centerY" + } + + Label { + text: "Angle" + width: 32 + tooltip: qsTr("Defines the start angle for the conical gradient. The value is in degrees (0-360).") + } + + GradientPropertySpinBox { + propertyName: "angle" + } + } + } + } } ButtonRowButton { id: transparentButton diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientDialogPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientDialogPopup.qml new file mode 100644 index 00000000000..e0a12de911e --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientDialogPopup.qml @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 as Controls +import QtQuickDesignerTheme 1.0 +import QtQuick.Controls.Styles 1.1 + +Loader { + id: gradientDialogLoader + parent: itemPane + anchors.fill: parent + + visible: false + active: visible + + function toggle() { + gradientDialogLoader.visible = !gradientDialogLoader.visible + } + + property Component content + + property int dialogHeight: 240 + property int dialogWidth: 280 + + sourceComponent: Component { + FocusScope { + id: popup + + Keys.onEscapePressed: { + event.accepted = true + gradientDialogLoader.visible = false + } + + Component.onCompleted: { + popup.forceActiveFocus() + } + + Rectangle { + anchors.fill: parent + color: Theme.qmlDesignerBackgroundColorDarker() + opacity: 0.6 + } + + MouseArea { + anchors.fill: parent + onClicked: gradientDialogLoader.visible = false + } + Rectangle { + id: background + + property int xOffset: itemPane.width - gradientDialogLoader.dialogWidth + x: 4 + xOffset + Component.onCompleted: { + var pos = itemPane.mapFromItem(buttonRow.parent, 0, 0) + y = pos.y + 32 + } + + width: parent.width - 8 - xOffset + height: gradientDialogLoader.dialogHeight + + radius: 2 + color: Theme.qmlDesignerBackgroundColorDarkAlternate() + border.color: Theme.qmlDesignerBorderColor() + + Label { + x: 8 + y: 6 + font.bold: true + text: qsTr("Gradient Properties") + } + + Button { + width: 16 + height: 16 + style: ButtonStyle { + background: Item { + Image { + width: 16 + height: 16 + source: "image://icons/error" + opacity: { + if (control.pressed) + return 0.8 + return 1.0 + } + Rectangle { + z: -1 + anchors.fill: parent + color: control.pressed + || control.hovered ? Theme.qmlDesignerBackgroundColorDarker() : Theme.qmlDesignerButtonColor() + border.color: Theme.qmlDesignerBorderColor() + radius: 2 + } + } + } + } + onClicked: gradientDialogLoader.visible = false + + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 4 + } + + Loader { + anchors.fill: parent + sourceComponent: gradientDialogLoader.content + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientLine.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientLine.qml index dd00c9d0a1d..0f0c8b85864 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientLine.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientLine.qml @@ -38,6 +38,7 @@ Item { property alias gradientPropertyName: gradientModel.gradientPropertyName + property alias gradientTypeName: gradientModel.gradientTypeName onHasGradientChanged: { colorLine.invalidate() diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPopupIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPopupIndicator.qml new file mode 100644 index 00000000000..f39486c78fa --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPopupIndicator.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 as Controls + +Image { + id: root + signal clicked + visible: colorEditor.shapeGradients && parent.checked + width: 8 + height: 4 + source: "image://icons/down-arrow" + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.bottom + anchors.topMargin: 2 + opacity: popupRegion.containsMouse ? 1 : 0.8 + + ToolTipArea { + id: popupRegion + + onClicked: root.clicked() + hoverEnabled: true + anchors.fill: parent + anchors.margins: -2 + tooltip: qsTr("Edit Gradient Properties") + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml new file mode 100644 index 00000000000..fc4f2db38f1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 as Controls +import QtQuickDesignerTheme 1.0 +import QtQuick.Controls.Styles 1.1 + +DoubleSpinBox { + id: spinBox + + property string propertyName + + minimumValue: -9999 + maximumValue: 9999 + Component.onCompleted: spinBox.value = gradientLine.model.readGradientProperty(propertyName) + onValueChanged: gradientLine.model.setGradientProperty(propertyName, spinBox.value) + stepSize: 1 +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_conical_gradient.png b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_conical_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..160ca12db881d4c5ca424b61afd39d723b847a4d GIT binary patch literal 670 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rm@;DWu&Co?cG za29w(7Bet#3xhBt!>lLV0FMhJw4NZ$OG(Dmw!M6O*TlV~EA+rIW3TqzwgFr6v9_&P+@3TBN6- zJ#}5e?-Hwa|7Q(;9%3ht{(F!Rs#Ls~OY(y0)%>3A zU*{BIO3vFCS6 znV#D?$FKam!N#O5v1Ci;vWpkIl6w-?9IUM=`^F~F8YCaBgDovMJVHn)8M188y?NWhTkAsd7HQ@&3CEzx%VZK6*31R-LW-J9UcD zz3Mev!yWu)UQ4@WW9%pFEO}z-hTDIazSj1?t$X{e@Y0ky#T^_S_f9XGVA>HNiiYLEc P1tk+tS3j3^P6AFv literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_radial_gradient.png b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/icon_color_radial_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d95e5766a0023a02267feb58099195cbbf740b GIT binary patch literal 677 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rm@;DWu&Co?cG za29w(7Bet#3xhBt!>lYRezr<9ElOq~AYDW^nRWUSZ{<2YSfetpUA-GzHzzms=QWd%Qs>mS!U5NL67_nYL?PK+xUZzwa&n Zv&kNi+;hU1_X{Ysc)I$ztaD0e0sxHYBPIX< literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index aa7cfe1bb08..40e74e13be4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -28,6 +28,7 @@ #include "qmlanchorbindingproxy.h" #include "propertyeditorview.h" +#include #include #include #include @@ -35,8 +36,11 @@ #include #include +#include + GradientModel::GradientModel(QObject *parent) : QAbstractListModel(parent), m_locked(false) + ,m_gradientTypeName("Gradient") { } @@ -139,38 +143,38 @@ void GradientModel::addGradient() return; if (!m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { + try { - QColor color = m_itemNode.instanceValue("color").value(); + QColor color = m_itemNode.instanceValue("color").value(); - if (!color.isValid()) - color = QColor(Qt::white); + if (!color.isValid()) + color = QColor(Qt::white); - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::addGradient")); + QmlDesigner::RewriterTransaction transaction = view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::addGradient")); - QmlDesigner::ModelNode gradientNode = - m_itemNode.modelNode().view()->createModelNode("QtQuick.Gradient", - m_itemNode.modelNode().view()->majorQtQuickVersion(), 0); - m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).reparentHere(gradientNode); + QmlDesigner::ModelNode gradientNode = createGradientNode(); + m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).reparentHere(gradientNode); - QmlDesigner::ModelNode gradientStopNode = - m_itemNode.modelNode().view()->createModelNode("QtQuick.GradientStop", - m_itemNode.modelNode().view()->majorQtQuickVersion(), 0); - gradientStopNode.variantProperty("position").setValue(0.0); - gradientStopNode.variantProperty("color").setValue(color); - gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); + QmlDesigner::ModelNode gradientStopNode = view()->createModelNode("QtQuick.GradientStop", view()->majorQtQuickVersion(), 0); + gradientStopNode.variantProperty("position").setValue(0.0); + gradientStopNode.variantProperty("color").setValue(color); + gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); - gradientStopNode = m_itemNode.modelNode().view()->createModelNode( - "QtQuick.GradientStop", - m_itemNode.modelNode().view()->majorQtQuickVersion(), 0); - gradientStopNode.variantProperty("position").setValue(1.0); - gradientStopNode.variantProperty("color").setValue(QColor(Qt::black)); - gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); + gradientStopNode = view()->createModelNode("QtQuick.GradientStop", view()->majorQtQuickVersion(), 0); + gradientStopNode.variantProperty("position").setValue(1.0); + gradientStopNode.variantProperty("color").setValue(QColor(Qt::black)); + gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); + + } catch (const QmlDesigner::Exception &e) { + e.showException(); + } } setupModel(); emit hasGradientChanged(); + emit gradientTypeChanged(); } void GradientModel::setColor(int index, const QColor &color) @@ -231,7 +235,7 @@ qreal GradientModel::getPosition(int index) const void GradientModel::removeStop(int index) { if (index < rowCount() - 1 && index != 0) { - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::removeStop")); + QmlDesigner::RewriterTransaction transaction = view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::removeStop")); QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); QmlDesigner::QmlObjectNode stop = gradientNode.nodeListProperty("stops").at(index); if (stop.isValid()) { @@ -255,7 +259,7 @@ void GradientModel::deleteGradient() if (m_itemNode.isInBaseState()) { if (modelNode.hasProperty(gradientPropertyName().toUtf8())) { - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::deleteGradient")); + QmlDesigner::RewriterTransaction transaction = view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::deleteGradient")); QmlDesigner::ModelNode gradientNode = modelNode.nodeProperty(gradientPropertyName().toUtf8()).modelNode(); if (QmlDesigner::QmlObjectNode(gradientNode).isValid()) QmlDesigner::QmlObjectNode(gradientNode).destroy(); @@ -263,6 +267,7 @@ void GradientModel::deleteGradient() } emit hasGradientChanged(); + emit gradientTypeChanged(); } void GradientModel::lock() @@ -280,6 +285,22 @@ void GradientModel::registerDeclarativeType() qmlRegisterType("HelperWidgets",2,0,"GradientModel"); } +qreal GradientModel::readGradientProperty(const QString &propertyName) const +{ + if (!m_itemNode.isValid()) + return 0; + + QmlDesigner::QmlObjectNode gradient; + + if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) + gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + + if (!gradient.isValid()) + return 0; + + return gradient.modelValue(propertyName.toUtf8()).toReal(); +} + void GradientModel::setupModel() { m_locked = true; @@ -299,12 +320,17 @@ void GradientModel::setAnchorBackend(const QVariant &anchorBackend) if (backendCasted) m_itemNode = backendCasted->getItemNode(); + if (m_itemNode.isValid() + && m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) + m_gradientTypeName = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode().simplifiedTypeName(); + setupModel(); m_locked = true; emit anchorBackendChanged(); emit hasGradientChanged(); + emit gradientTypeChanged(); m_locked = false; } @@ -319,6 +345,16 @@ void GradientModel::setGradientPropertyName(const QString &name) m_gradientPropertyName = name; } +QString GradientModel::gradientTypeName() const +{ + return m_gradientTypeName; +} + +void GradientModel::setGradientTypeName(const QString &name) +{ + m_gradientTypeName = name; +} + bool GradientModel::hasGradient() const { return m_itemNode.isValid() @@ -330,10 +366,114 @@ bool GradientModel::locked() const if (m_locked) return true; - auto view = qobject_cast(m_itemNode.view()); + auto editorView = qobject_cast(view()); - if (view && view->locked()) - return true; + return editorView && editorView->locked(); +} + +bool GradientModel::hasShapesImport() const +{ + if (m_itemNode.isValid()) { + QmlDesigner::Import import = QmlDesigner::Import::createLibraryImport("QtQuick.Shapes", "1.0"); + return model()->hasImport(import, true, true); + } return false; } + +void GradientModel::ensureShapesImport() +{ + if (!hasShapesImport()) { + QmlDesigner::Import timelineImport = QmlDesigner::Import::createLibraryImport("QtQuick.Shapes", "1.0"); + model()->changeImports({timelineImport}, {}); + } +} + +void GradientModel::setupGradientProperties(const QmlDesigner::ModelNode &gradient) +{ + QTC_ASSERT(m_itemNode.isValid(), return); + + QTC_ASSERT(gradient.isValid(), return); + + if (m_gradientTypeName == "Gradient") { + } else if (m_gradientTypeName == "LinearGradient") { + gradient.variantProperty("x1").setValue(0); + gradient.variantProperty("x2").setValue(m_itemNode.instanceValue("width")); + gradient.variantProperty("y1").setValue(0); + gradient.variantProperty("y2").setValue(m_itemNode.instanceValue("height")); + } else if (m_gradientTypeName == "RadialGradient") { + qreal width = m_itemNode.instanceValue("width").toReal(); + qreal height = m_itemNode.instanceValue("height").toReal(); + gradient.variantProperty("centerX").setValue(width / 2.0); + gradient.variantProperty("centerY").setValue(height / 2.0); + + gradient.variantProperty("focalX").setValue(width / 2.0); + gradient.variantProperty("focalY").setValue(height / 2.0); + + qreal radius = qMin(width, height) / 2; + + gradient.variantProperty("centerRadius").setValue(radius); + gradient.variantProperty("focalRadius").setValue(0); + + } else if (m_gradientTypeName == "ConicalGradient") { + qreal width = m_itemNode.instanceValue("width").toReal(); + qreal height = m_itemNode.instanceValue("height").toReal(); + gradient.variantProperty("centerX").setValue(width / 2.0); + gradient.variantProperty("centerY").setValue(height / 2.0); + + gradient.variantProperty("angle").setValue(0); + } +} + +QmlDesigner::Model *GradientModel::model() const +{ + QTC_ASSERT(m_itemNode.isValid(), return nullptr); + return m_itemNode.view()->model(); +} + +QmlDesigner::AbstractView *GradientModel::view() const +{ + QTC_ASSERT(m_itemNode.isValid(), return nullptr); + return m_itemNode.view(); +} + +QmlDesigner::ModelNode GradientModel::createGradientNode() +{ + QByteArray fullTypeName = m_gradientTypeName.toUtf8(); + + if (m_gradientTypeName == "Gradient") { + fullTypeName.prepend("QtQuick."); + } else { + fullTypeName.prepend("QtQuick.Shapes."); + ensureShapesImport(); + } + + auto metaInfo = model()->metaInfo(fullTypeName); + + int minorVersion = metaInfo.minorVersion(); + int majorVersion = metaInfo.majorVersion(); + + auto gradientNode = view()->createModelNode(fullTypeName, majorVersion, minorVersion); + + setupGradientProperties(gradientNode); + + return gradientNode; +} + +void GradientModel::setGradientProperty(const QString &propertyName, qreal value) +{ + QTC_ASSERT(m_itemNode.isValid(), return); + + QmlDesigner::QmlObjectNode gradient; + + if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) + gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + + QTC_ASSERT(gradient.isValid(), return); + + try { + gradient.setVariantProperty(propertyName.toUtf8(), value); + } catch (const QmlDesigner::Exception &e) { + e.showException(); + } +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h index 2ca8b93c429..aa7cdaa32f4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h @@ -37,6 +37,7 @@ class GradientModel : public QAbstractListModel Q_PROPERTY(QVariant anchorBackendProperty READ anchorBackend WRITE setAnchorBackend NOTIFY anchorBackendChanged) Q_PROPERTY(QString gradientPropertyName READ gradientPropertyName WRITE setGradientPropertyName) + Q_PROPERTY(QString gradientTypeName READ gradientTypeName WRITE setGradientTypeName NOTIFY gradientTypeChanged) Q_PROPERTY(int count READ rowCount) Q_PROPERTY(bool hasGradient READ hasGradient NOTIFY hasGradientChanged) @@ -65,9 +66,14 @@ public: static void registerDeclarativeType(); + Q_INVOKABLE qreal readGradientProperty(const QString &property) const; + + Q_INVOKABLE void setGradientProperty(const QString &propertyName, qreal value); + signals: void anchorBackendChanged(); void hasGradientChanged(); + void gradientTypeChanged(); private: void setupModel(); @@ -75,14 +81,22 @@ private: QVariant anchorBackend() const {return QVariant(); } QString gradientPropertyName() const; void setGradientPropertyName(const QString &name); + QString gradientTypeName() const; + void setGradientTypeName(const QString &name); bool hasGradient() const; bool locked() const; + QmlDesigner::ModelNode createGradientNode(); private: QmlDesigner::QmlItemNode m_itemNode; QString m_gradientPropertyName; + QString m_gradientTypeName; bool m_locked; - + bool hasShapesImport() const; + void ensureShapesImport(); + void setupGradientProperties(const QmlDesigner::ModelNode &gradient); + QmlDesigner::Model *model() const; + QmlDesigner::AbstractView *view() const; }; QML_DECLARE_TYPE(GradientModel)