forked from qt-creator/qt-creator
QmlDesigner: Add dynamic properties to property editor
Added dynamic properties section to property editor and material editor for all nodes. It shows all dynamic properties defined for the node with proper editors for the supported types. Dynamic properties can be added and removed via property editor as well. Material editor shows dynamic properties similarly. Fixes: QDS-7411 Change-Id: Id195f5ca23d55544cea29eb8996690a7eed1cc57 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
#include <qmldesignerconstants.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
@@ -90,6 +91,16 @@ QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::T
|
||||
} else {
|
||||
returnValue = QColor(Qt::black);
|
||||
}
|
||||
} else if (typeName == "vector2d") {
|
||||
returnValue = "Qt.vector2d(0, 0)";
|
||||
} else if (typeName == "vector3d") {
|
||||
returnValue = "Qt.vector3d(0, 0, 0)";
|
||||
} else if (typeName == "vector4d") {
|
||||
returnValue = "Qt.vector4d(0, 0, 0 ,0)";
|
||||
} else if (typeName == "TextureInput") {
|
||||
returnValue = "null";
|
||||
} else if (typeName == "alias") {
|
||||
returnValue = "null";
|
||||
} else if (typeName == "Item") {
|
||||
returnValue = 0;
|
||||
}
|
||||
@@ -119,9 +130,18 @@ QmlDesigner::PropertyName DynamicPropertiesModel::unusedProperty(const QmlDesign
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(ConnectionView *parent)
|
||||
bool DynamicPropertiesModel::isValueType(const TypeName &type)
|
||||
{
|
||||
// "variant" is considered value type as it is initialized as one.
|
||||
// This may need to change if we provide any kind of proper editor for it.
|
||||
static const QSet<TypeName> valueTypes {"int", "real", "color", "string", "bool", "url", "variant"};
|
||||
return valueTypes.contains(type);
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_connectionView(parent)
|
||||
, m_view(parent)
|
||||
, m_explicitSelection(explicitSelection)
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
|
||||
}
|
||||
@@ -133,8 +153,9 @@ void DynamicPropertiesModel::resetModel()
|
||||
setHorizontalHeaderLabels(
|
||||
QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}));
|
||||
|
||||
if (connectionView()->isAttached()) {
|
||||
for (const ModelNode &modelNode : connectionView()->selectedModelNodes())
|
||||
if (m_view->isAttached()) {
|
||||
const auto nodes = selectedNodes();
|
||||
for (const ModelNode &modelNode : nodes)
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
@@ -146,8 +167,8 @@ void DynamicPropertiesModel::resetModel()
|
||||
//Value copying is optional
|
||||
BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue)
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
if (modelNode.hasVariantProperty(name)) {
|
||||
try {
|
||||
@@ -181,8 +202,8 @@ BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const Property
|
||||
//If it's a BindingProperty, then replaces it with empty VariantProperty
|
||||
void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
if (modelNode.hasProperty(name)) {
|
||||
try {
|
||||
@@ -190,11 +211,10 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
|
||||
if (abProp.isVariantProperty()) {
|
||||
VariantProperty property = abProp.toVariantProperty();
|
||||
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), property.dynamicTypeName());
|
||||
QVariant newValue = convertVariantForTypeName({}, property.dynamicTypeName());
|
||||
property.setDynamicTypeNameAndValue(property.dynamicTypeName(),
|
||||
newValue);
|
||||
}
|
||||
else if (abProp.isBindingProperty()) {
|
||||
} else if (abProp.isBindingProperty()) {
|
||||
BindingProperty property = abProp.toBindingProperty();
|
||||
TypeName oldType = property.dynamicTypeName();
|
||||
|
||||
@@ -202,9 +222,8 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
modelNode.removeProperty(name);
|
||||
|
||||
VariantProperty newProperty = modelNode.variantProperty(name);
|
||||
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), oldType);
|
||||
newProperty.setDynamicTypeNameAndValue(oldType,
|
||||
newValue);
|
||||
QVariant newValue = convertVariantForTypeName({}, oldType);
|
||||
newProperty.setDynamicTypeNameAndValue(oldType, newValue);
|
||||
}
|
||||
|
||||
} catch (RewritingException &e) {
|
||||
@@ -226,8 +245,8 @@ void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindi
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
@@ -249,17 +268,16 @@ void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &varia
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(variantProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(variantProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForVariantProperty(variantProperty);
|
||||
|
||||
if (rowNumber == -1) {
|
||||
if (rowNumber == -1)
|
||||
addVariantProperty(variantProperty);
|
||||
} else {
|
||||
else
|
||||
updateVariantProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
@@ -269,8 +287,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
@@ -280,17 +298,36 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::selectionChanged(const QList<ModelNode> &selectedNodes)
|
||||
void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(variantProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForVariantProperty(variantProperty);
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::reset()
|
||||
{
|
||||
Q_UNUSED(selectedNodes)
|
||||
m_handleDataChanged = false;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
ConnectionView *DynamicPropertiesModel::connectionView() const
|
||||
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
|
||||
{
|
||||
return m_connectionView;
|
||||
QTC_ASSERT(m_explicitSelection, return);
|
||||
QTC_ASSERT(node.isValid(), return);
|
||||
|
||||
m_selectedNodes.clear();
|
||||
m_selectedNodes.append(node);
|
||||
reset();
|
||||
}
|
||||
|
||||
AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const
|
||||
@@ -298,10 +335,10 @@ AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) c
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
if (!connectionView()->isAttached())
|
||||
if (!m_view->isAttached())
|
||||
return AbstractProperty();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.property(targetPropertyName.toUtf8());
|
||||
@@ -314,7 +351,7 @@ BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) con
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.bindingProperty(targetPropertyName.toUtf8());
|
||||
@@ -327,7 +364,7 @@ VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) con
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.variantProperty(targetPropertyName.toUtf8());
|
||||
@@ -364,8 +401,8 @@ void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
|
||||
{
|
||||
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED);
|
||||
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
try {
|
||||
modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
|
||||
@@ -420,16 +457,14 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
|
||||
|
||||
void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
|
||||
{
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
if (bindingProperty.isValid()) {
|
||||
bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
} else {
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
if (variantProperty.isValid())
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -455,7 +490,6 @@ void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
|
||||
items.append(idItem);
|
||||
items.append(propertyNameItem);
|
||||
|
||||
|
||||
propertyTypeItem = new QStandardItem(propertyType);
|
||||
items.append(propertyTypeItem);
|
||||
|
||||
@@ -531,7 +565,7 @@ void DynamicPropertiesModel::updateValue(int row)
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = data(index(row, PropertyValueRow)).toString();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
|
||||
transaction.commit(); //committing in the try block
|
||||
@@ -547,7 +581,7 @@ void DynamicPropertiesModel::updateValue(int row)
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = data(index(row, PropertyValueRow));
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
|
||||
transaction.commit(); //committing in the try block
|
||||
@@ -571,7 +605,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
|
||||
@@ -590,7 +624,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
|
||||
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
|
||||
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
});
|
||||
@@ -616,7 +650,7 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
const PropertyName propertyName = bindingProperty.name();
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
|
||||
});
|
||||
@@ -632,12 +666,14 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
const PropertyName propertyName = variantProperty.name();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
if (newType == "alias") { //alias properties have to be bindings
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none"));
|
||||
if (!isValueType(newType)) {
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(
|
||||
newType, convertVariantForTypeName({}, newType).toString());
|
||||
} else {
|
||||
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType));
|
||||
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(
|
||||
newType, convertVariantForTypeName(value, newType));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -656,7 +692,7 @@ ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const M
|
||||
ModelNode modelNode;
|
||||
|
||||
if (id != QLatin1String("parent")) {
|
||||
modelNode = connectionView()->modelNodeForId(id);
|
||||
modelNode = m_view->modelNodeForId(id);
|
||||
} else {
|
||||
if (targetNode.hasParentProperty()) {
|
||||
modelNode = targetNode.parentProperty().parentModelNode();
|
||||
@@ -776,6 +812,24 @@ void DynamicPropertiesModel::handleException()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
const QList<ModelNode> DynamicPropertiesModel::selectedNodes() const
|
||||
{
|
||||
// If selected nodes are explicitly set, return those.
|
||||
// Otherwise return actual selected nodes of the model.
|
||||
if (m_explicitSelection)
|
||||
return m_selectedNodes;
|
||||
else
|
||||
return m_view->selectedModelNodes();
|
||||
}
|
||||
|
||||
const ModelNode DynamicPropertiesModel::singleSelectedNode() const
|
||||
{
|
||||
if (m_explicitSelection)
|
||||
return m_selectedNodes.first();
|
||||
else
|
||||
return m_view->singleSelectedModelNode();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
Reference in New Issue
Block a user