QmlDesigner: Fix crash

The version number of 'GradientStop' was hardcoded, but does
change with QtQuick 5.12. The issue is not consistently triggered.

This is a backport from master.

Task-number: QDS-472
Change-Id: I939659aa14ae1570fc5d833726f28894c043da02
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Thomas Hartmann
2019-02-19 17:58:58 +01:00
committed by Tim Jenssen
parent 99d20280ca
commit 00ee1ca7e0
2 changed files with 213 additions and 46 deletions

View File

@@ -28,6 +28,7 @@
#include "qmlanchorbindingproxy.h" #include "qmlanchorbindingproxy.h"
#include "propertyeditorview.h" #include "propertyeditorview.h"
#include <exception.h>
#include <nodeproperty.h> #include <nodeproperty.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <variantproperty.h> #include <variantproperty.h>
@@ -35,8 +36,11 @@
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <rewritertransaction.h> #include <rewritertransaction.h>
#include <utils/qtcassert.h>
GradientModel::GradientModel(QObject *parent) : GradientModel::GradientModel(QObject *parent) :
QAbstractListModel(parent), m_locked(false) QAbstractListModel(parent), m_locked(false)
,m_gradientTypeName("Gradient")
{ {
} }
@@ -101,21 +105,19 @@ int GradientModel::addStop(qreal position, const QColor &color)
return -1; return -1;
if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) {
//QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); int properPos = 0;
//### TODO does not work try {
QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode();
QmlDesigner::ModelNode gradientStopNode = QmlDesigner::ModelNode gradientStopNode = createGradientStopNode();
m_itemNode.modelNode().view()->createModelNode("QtQuick.GradientStop",
m_itemNode.modelNode().view()->majorQtQuickVersion(), 0);
gradientStopNode.variantProperty("position").setValue(position); gradientStopNode.variantProperty("position").setValue(position);
gradientStopNode.variantProperty("color").setValue(color); gradientStopNode.variantProperty("color").setValue(color);
gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode);
const QList<QmlDesigner::ModelNode> stopNodes = gradientNode.nodeListProperty("stops").toModelNodeList(); const QList<QmlDesigner::ModelNode> stopNodes = gradientNode.nodeListProperty("stops").toModelNodeList();
int properPos = 0;
for (int i = 0; i < stopNodes.count(); i++) { for (int i = 0; i < stopNodes.count(); i++) {
if (QmlDesigner::QmlObjectNode(stopNodes.at(i)).modelValue("position").toReal() < position) if (QmlDesigner::QmlObjectNode(stopNodes.at(i)).modelValue("position").toReal() < position)
properPos = i + 1; properPos = i + 1;
@@ -123,6 +125,9 @@ int GradientModel::addStop(qreal position, const QColor &color)
gradientNode.nodeListProperty("stops").slide(stopNodes.count() - 1, properPos); gradientNode.nodeListProperty("stops").slide(stopNodes.count() - 1, properPos);
setupModel(); setupModel();
} catch (const QmlDesigner::Exception &e) {
e.showException();
}
return properPos; return properPos;
} }
@@ -139,38 +144,38 @@ void GradientModel::addGradient()
return; return;
if (!m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { if (!m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) {
try {
QColor color = m_itemNode.instanceValue("color").value<QColor>(); QColor color = m_itemNode.instanceValue("color").value<QColor>();
if (!color.isValid()) if (!color.isValid())
color = QColor(Qt::white); 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 = createGradientNode();
QmlDesigner::ModelNode gradientNode =
m_itemNode.modelNode().view()->createModelNode("QtQuick.Gradient",
m_itemNode.modelNode().view()->majorQtQuickVersion(), 0);
m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).reparentHere(gradientNode); m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).reparentHere(gradientNode);
QmlDesigner::ModelNode gradientStopNode = createGradientStopNode();
QmlDesigner::ModelNode gradientStopNode =
m_itemNode.modelNode().view()->createModelNode("QtQuick.GradientStop",
m_itemNode.modelNode().view()->majorQtQuickVersion(), 0);
gradientStopNode.variantProperty("position").setValue(0.0); gradientStopNode.variantProperty("position").setValue(0.0);
gradientStopNode.variantProperty("color").setValue(color); gradientStopNode.variantProperty("color").setValue(color);
gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode);
gradientStopNode = m_itemNode.modelNode().view()->createModelNode( gradientStopNode = createGradientStopNode();
"QtQuick.GradientStop",
m_itemNode.modelNode().view()->majorQtQuickVersion(), 0);
gradientStopNode.variantProperty("position").setValue(1.0); gradientStopNode.variantProperty("position").setValue(1.0);
gradientStopNode.variantProperty("color").setValue(QColor(Qt::black)); gradientStopNode.variantProperty("color").setValue(QColor(Qt::black));
gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode); gradientNode.nodeListProperty("stops").reparentHere(gradientStopNode);
} catch (const QmlDesigner::Exception &e) {
e.showException();
}
} }
setupModel(); setupModel();
emit hasGradientChanged(); emit hasGradientChanged();
emit gradientTypeChanged();
} }
void GradientModel::setColor(int index, const QColor &color) void GradientModel::setColor(int index, const QColor &color)
@@ -231,7 +236,7 @@ qreal GradientModel::getPosition(int index) const
void GradientModel::removeStop(int index) void GradientModel::removeStop(int index)
{ {
if (index < rowCount() - 1 && index != 0) { 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::ModelNode gradientNode = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode();
QmlDesigner::QmlObjectNode stop = gradientNode.nodeListProperty("stops").at(index); QmlDesigner::QmlObjectNode stop = gradientNode.nodeListProperty("stops").at(index);
if (stop.isValid()) { if (stop.isValid()) {
@@ -255,7 +260,7 @@ void GradientModel::deleteGradient()
if (m_itemNode.isInBaseState()) { if (m_itemNode.isInBaseState()) {
if (modelNode.hasProperty(gradientPropertyName().toUtf8())) { 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(); QmlDesigner::ModelNode gradientNode = modelNode.nodeProperty(gradientPropertyName().toUtf8()).modelNode();
if (QmlDesigner::QmlObjectNode(gradientNode).isValid()) if (QmlDesigner::QmlObjectNode(gradientNode).isValid())
QmlDesigner::QmlObjectNode(gradientNode).destroy(); QmlDesigner::QmlObjectNode(gradientNode).destroy();
@@ -263,6 +268,7 @@ void GradientModel::deleteGradient()
} }
emit hasGradientChanged(); emit hasGradientChanged();
emit gradientTypeChanged();
} }
void GradientModel::lock() void GradientModel::lock()
@@ -280,6 +286,22 @@ void GradientModel::registerDeclarativeType()
qmlRegisterType<GradientModel>("HelperWidgets",2,0,"GradientModel"); qmlRegisterType<GradientModel>("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() void GradientModel::setupModel()
{ {
m_locked = true; m_locked = true;
@@ -299,12 +321,17 @@ void GradientModel::setAnchorBackend(const QVariant &anchorBackend)
if (backendCasted) if (backendCasted)
m_itemNode = backendCasted->getItemNode(); 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(); setupModel();
m_locked = true; m_locked = true;
emit anchorBackendChanged(); emit anchorBackendChanged();
emit hasGradientChanged(); emit hasGradientChanged();
emit gradientTypeChanged();
m_locked = false; m_locked = false;
} }
@@ -319,6 +346,16 @@ void GradientModel::setGradientPropertyName(const QString &name)
m_gradientPropertyName = name; m_gradientPropertyName = name;
} }
QString GradientModel::gradientTypeName() const
{
return m_gradientTypeName;
}
void GradientModel::setGradientTypeName(const QString &name)
{
m_gradientTypeName = name;
}
bool GradientModel::hasGradient() const bool GradientModel::hasGradient() const
{ {
return m_itemNode.isValid() return m_itemNode.isValid()
@@ -330,10 +367,125 @@ bool GradientModel::locked() const
if (m_locked) if (m_locked)
return true; return true;
auto view = qobject_cast<QmlDesigner::PropertyEditorView*>(m_itemNode.view()); auto editorView = qobject_cast<QmlDesigner::PropertyEditorView*>(view());
if (view && view->locked()) return editorView && editorView->locked();
return true; }
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; 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;
}
QmlDesigner::ModelNode GradientModel::createGradientStopNode()
{
QByteArray fullTypeName = "QtQuick.GradientStop";
auto metaInfo = model()->metaInfo(fullTypeName);
int minorVersion = metaInfo.minorVersion();
int majorVersion = metaInfo.majorVersion();
return view()->createModelNode(fullTypeName, majorVersion, minorVersion);
}
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();
}
}

View File

@@ -37,6 +37,7 @@ class GradientModel : public QAbstractListModel
Q_PROPERTY(QVariant anchorBackendProperty READ anchorBackend WRITE setAnchorBackend NOTIFY anchorBackendChanged) Q_PROPERTY(QVariant anchorBackendProperty READ anchorBackend WRITE setAnchorBackend NOTIFY anchorBackendChanged)
Q_PROPERTY(QString gradientPropertyName READ gradientPropertyName WRITE setGradientPropertyName) 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(int count READ rowCount)
Q_PROPERTY(bool hasGradient READ hasGradient NOTIFY hasGradientChanged) Q_PROPERTY(bool hasGradient READ hasGradient NOTIFY hasGradientChanged)
@@ -65,9 +66,14 @@ public:
static void registerDeclarativeType(); static void registerDeclarativeType();
Q_INVOKABLE qreal readGradientProperty(const QString &property) const;
Q_INVOKABLE void setGradientProperty(const QString &propertyName, qreal value);
signals: signals:
void anchorBackendChanged(); void anchorBackendChanged();
void hasGradientChanged(); void hasGradientChanged();
void gradientTypeChanged();
private: private:
void setupModel(); void setupModel();
@@ -75,14 +81,23 @@ private:
QVariant anchorBackend() const {return QVariant(); } QVariant anchorBackend() const {return QVariant(); }
QString gradientPropertyName() const; QString gradientPropertyName() const;
void setGradientPropertyName(const QString &name); void setGradientPropertyName(const QString &name);
QString gradientTypeName() const;
void setGradientTypeName(const QString &name);
bool hasGradient() const; bool hasGradient() const;
bool locked() const; bool locked() const;
QmlDesigner::ModelNode createGradientNode();
QmlDesigner::ModelNode createGradientStopNode();
private: private:
QmlDesigner::QmlItemNode m_itemNode; QmlDesigner::QmlItemNode m_itemNode;
QString m_gradientPropertyName; QString m_gradientPropertyName;
QString m_gradientTypeName;
bool m_locked; 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) QML_DECLARE_TYPE(GradientModel)