Cleanup ConnectionEditor models

Simplified interface for the BindingModel and DynamicPropertiesModel.

Change-Id: I772f31be704afe2a43c6368aefab1b026b85ec8b
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Knud Dollereder
2023-08-23 12:12:07 +02:00
committed by Thomas Hartmann
parent e0c129d4b1
commit 759b560bab
24 changed files with 1330 additions and 3389 deletions

View File

@@ -33,7 +33,6 @@ ListView {
onCurrentIndexChanged: { onCurrentIndexChanged: {
root.currentIndex = root.model.currentIndex root.currentIndex = root.model.currentIndex
dialog.backend.currentRow = root.currentIndex
} }
// Number of columns // Number of columns
@@ -79,7 +78,6 @@ ListView {
onClicked: { onClicked: {
root.model.currentIndex = itemDelegate.index root.model.currentIndex = itemDelegate.index
root.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index
dialog.backend.currentRow = itemDelegate.index
dialog.popup(mouseArea) dialog.popup(mouseArea)
} }
} }

View File

@@ -33,7 +33,6 @@ ListView {
onCurrentIndexChanged: { onCurrentIndexChanged: {
root.currentIndex = root.model.currentIndex root.currentIndex = root.model.currentIndex
dialog.backend.currentRow = root.currentIndex
} }
// Number of columns // Number of columns
@@ -81,7 +80,6 @@ ListView {
function onClicked() { function onClicked() {
root.model.currentIndex = itemDelegate.index root.model.currentIndex = itemDelegate.index
root.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index
dialog.backend.currentRow = itemDelegate.index
dialog.popup(mouseArea) dialog.popup(mouseArea)
} }
} }

View File

@@ -485,7 +485,6 @@ add_qtc_plugin(QmlDesigner
editorproxy.cpp editorproxy.h editorproxy.cpp editorproxy.h
EXPLICIT_MOC EXPLICIT_MOC
components/propertyeditor/propertyeditorvalue.h components/propertyeditor/propertyeditorvalue.h
components/connectioneditor/connectionviewwidget.h
qmldesignerplugin.h qmldesignerplugin.h
EXTRA_TRANSLATIONS EXTRA_TRANSLATIONS
"${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner" "${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner"
@@ -940,14 +939,15 @@ extend_qtc_plugin(QmlDesigner
addnewbackenddialog.cpp addnewbackenddialog.h addnewbackenddialog.ui addnewbackenddialog.cpp addnewbackenddialog.h addnewbackenddialog.ui
backendmodel.cpp backendmodel.h backendmodel.cpp backendmodel.h
bindingmodel.cpp bindingmodel.h bindingmodel.cpp bindingmodel.h
bindingmodelitem.cpp bindingmodelitem.h
connectioneditor.qrc connectioneditor.qrc
connectioneditorevaluator.cpp connectioneditorevaluator.h connectioneditorevaluator.cpp connectioneditorevaluator.h
connectioneditorstatements.cpp connectioneditorstatements.h connectioneditorstatements.cpp connectioneditorstatements.h
connectionmodel.cpp connectionmodel.h connectionmodel.cpp connectionmodel.h
connectionview.cpp connectionview.h connectionview.cpp connectionview.h
connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui
delegates.cpp delegates.h
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
dynamicpropertiesitem.cpp dynamicpropertiesitem.h
connectioneditorutils.cpp connectioneditorutils.h
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
propertytreemodel.cpp propertytreemodel.h propertytreemodel.cpp propertytreemodel.h
) )

View File

@@ -2,111 +2,27 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "bindingmodel.h" #include "bindingmodel.h"
#include "bindingmodelitem.h"
#include "connectionview.h" #include "connectionview.h"
#include "connectioneditorutils.h"
#include <bindingproperty.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <nodeproperty.h> #include <nodeproperty.h>
#include <bindingproperty.h>
#include <variantproperty.h>
#include <rewritingexception.h>
#include <rewritertransaction.h> #include <rewritertransaction.h>
#include <rewriterview.h> #include <rewritingexception.h>
#include <variantproperty.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QMessageBox>
#include <QTimer>
namespace QmlDesigner { namespace QmlDesigner {
BindingModel::BindingModel(ConnectionView *parent) BindingModel::BindingModel(ConnectionView *parent)
: QStandardItemModel(parent), m_connectionView(parent), : QStandardItemModel(parent)
m_delegate(new BindingModelBackendDelegate(this)) , m_connectionView(parent)
, m_delegate(new BindingModelBackendDelegate(this))
{ {
connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged); setHorizontalHeaderLabels(BindingModelItem::headerLabels());
}
void BindingModel::resetModel()
{
beginResetModel();
clear();
setHorizontalHeaderLabels(
QStringList({tr("Item"), tr("Property"), tr("Source Item"), tr("Source Property")}));
if (connectionView()->isAttached()) {
for (const ModelNode &modelNode : connectionView()->selectedModelNodes())
addModelNode(modelNode);
}
endResetModel();
}
void BindingModel::add()
{
addBindingForCurrentNode();
}
void BindingModel::remove(int row)
{
deleteBindindByRow(row);
}
int BindingModel::currentIndex() const
{
return m_currentIndex;
}
void BindingModel::setCurrentIndex(int i)
{
if (m_currentIndex == i)
return;
m_currentIndex = i;
emit currentIndexChanged();
}
void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
{
m_handleDataChanged = false;
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
return;
if (!m_lock) {
int rowNumber = findRowForBinding(bindingProperty);
if (rowNumber == -1) {
addBindingProperty(bindingProperty);
} else {
updateBindingProperty(rowNumber);
}
}
m_handleDataChanged = true;
}
void BindingModel::bindingRemoved(const BindingProperty &bindingProperty)
{
m_handleDataChanged = false;
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
return;
if (!m_lock) {
int rowNumber = findRowForBinding(bindingProperty);
removeRow(rowNumber);
}
m_handleDataChanged = true;
}
void BindingModel::selectionChanged([[maybe_unused]] const QList<ModelNode> &selectedNodes)
{
m_handleDataChanged = false;
resetModel();
m_handleDataChanged = true;
} }
ConnectionView *BindingModel::connectionView() const ConnectionView *BindingModel::connectionView() const
@@ -114,465 +30,235 @@ ConnectionView *BindingModel::connectionView() const
return m_connectionView; return m_connectionView;
} }
BindingProperty BindingModel::bindingPropertyForRow(int rowNumber) const
{
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);
if (modelNode.isValid())
return modelNode.bindingProperty(targetPropertyName.toLatin1());
return BindingProperty();
}
QStringList BindingModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
{
const ModelNode modelNode = bindingProperty.parentModelNode();
if (!modelNode.isValid()) {
qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
return QStringList();
}
NodeMetaInfo metaInfo = modelNode.metaInfo();
if (metaInfo.isValid()) {
const auto properties = metaInfo.properties();
QStringList writableProperties;
writableProperties.reserve(static_cast<int>(properties.size()));
for (const auto &property : properties) {
if (property.isWritable())
writableProperties.push_back(QString::fromUtf8(property.name()));
}
return writableProperties;
}
return QStringList();
}
QStringList BindingModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
{
const QString expression = bindingProperty.expression();
const QStringList stringlist = expression.split(QLatin1String("."));
QStringList possibleProperties;
NodeMetaInfo type;
if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid())
type = metaInfo.property(bindingProperty.name()).propertyType();
else
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
const QString &id = stringlist.constFirst();
ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
if (!modelNode.isValid()) {
//if it's not a valid model node, maybe it's a singleton
if (RewriterView* rv = connectionView()->rewriterView()) {
for (const QmlTypeData &data : rv->getQMLTypes()) {
if (!data.typeName.isEmpty() && data.typeName == id) {
NodeMetaInfo metaInfo = connectionView()->model()->metaInfo(data.typeName.toUtf8());
if (metaInfo.isValid()) {
for (const auto &property : metaInfo.properties()) {
//without check for now
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
return possibleProperties;
}
}
}
}
qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
return QStringList();
}
NodeMetaInfo metaInfo = modelNode.metaInfo();
for (const VariantProperty &variantProperty : modelNode.variantProperties()) {
if (variantProperty.isDynamic())
possibleProperties << QString::fromUtf8(variantProperty.name());
}
for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) {
if (bindingProperty.isDynamic())
possibleProperties << QString::fromUtf8((bindingProperty.name()));
}
if (metaInfo.isValid()) {
for (const auto &property : metaInfo.properties()) {
if (property.propertyType() == type) //### todo proper check
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
} else {
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
}
return possibleProperties;
}
void BindingModel::deleteBindindByRow(int rowNumber)
{
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
if (bindingProperty.isValid()) {
bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
}
resetModel();
}
static PropertyName unusedProperty(const ModelNode &modelNode)
{
PropertyName propertyName = "none";
if (modelNode.metaInfo().isValid()) {
for (const auto &property : modelNode.metaInfo().properties()) {
if (property.isWritable() && !modelNode.hasProperty(propertyName))
return property.name();
}
}
return propertyName;
}
void BindingModel::addBindingForCurrentNode()
{
if (connectionView()->selectedModelNodes().size() == 1) {
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
if (modelNode.isValid()) {
try {
modelNode.bindingProperty(unusedProperty(modelNode)).setExpression(QLatin1String("none.none"));
} catch (RewritingException &e) {
m_exceptionError = e.description();
QTimer::singleShot(200, this, &BindingModel::handleException);
}
}
} else {
qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
}
}
static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property)
{
item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole);
item->setData(property.name(), BindingModel::TargetPropertyNameRole);
const AbstractProperty source = property.resolveToProperty();
if (source.isValid()) {
item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole);
item->setData(source.name(), BindingModel::SourcePropertyNameRole);
}
}
void BindingModel::addBindingProperty(const BindingProperty &property)
{
QStandardItem *idItem;
QStandardItem *targetPropertyNameItem;
QStandardItem *sourceIdItem;
QStandardItem *sourcePropertyNameItem;
QString idLabel = property.parentModelNode().id();
if (idLabel.isEmpty())
idLabel = property.parentModelNode().simplifiedTypeName();
idItem = new QStandardItem(idLabel);
updateCustomData(idItem, property);
targetPropertyNameItem = new QStandardItem(QString::fromUtf8(property.name()));
QList<QStandardItem*> items;
items.append(idItem);
updateDisplayRoles(idItem, property);
items.append(targetPropertyNameItem);
QString sourceNodeName;
QString sourcePropertyName;
getExpressionStrings(property, &sourceNodeName, &sourcePropertyName);
sourceIdItem = new QStandardItem(sourceNodeName);
sourcePropertyNameItem = new QStandardItem(sourcePropertyName);
items.append(sourceIdItem);
items.append(sourcePropertyNameItem);
appendRow(items);
}
void BindingModel::updateBindingProperty(int rowNumber)
{
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
if (bindingProperty.isValid()) {
QStandardItem *idItem = item(rowNumber, 0);
if (idItem)
updateDisplayRoles(idItem, bindingProperty);
QString targetPropertyName = QString::fromUtf8(bindingProperty.name());
updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
QString sourceNodeName;
QString sourcePropertyName;
getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
updateDisplayRole(rowNumber, SourceModelNodeRow, sourceNodeName);
updateDisplayRole(rowNumber, SourcePropertyNameRow, sourcePropertyName);
}
}
void BindingModel::addModelNode(const ModelNode &modelNode)
{
const QList<BindingProperty> bindingProperties = modelNode.bindingProperties();
for (const BindingProperty &bindingProperty : bindingProperties) {
addBindingProperty(bindingProperty);
}
}
void BindingModel::updateExpression(int row)
{
const QString sourceNode = data(index(row, SourceModelNodeRow)).toString().trimmed();
const QString sourceProperty = data(index(row, SourcePropertyNameRow)).toString().trimmed();
QString expression;
if (sourceProperty.isEmpty()) {
expression = sourceNode;
} else {
expression = sourceNode + QLatin1String(".") + sourceProperty;
}
connectionView()->executeInTransaction("BindingModel::updateExpression", [this, row, expression](){
BindingProperty bindingProperty = bindingPropertyForRow(row);
bindingProperty.setExpression(expression.trimmed());
});
}
void BindingModel::updatePropertyName(int rowNumber)
{
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toUtf8();
const QString expression = bindingProperty.expression();
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
ModelNode targetNode = bindingProperty.parentModelNode();
if (!newName.isEmpty()) {
RewriterTransaction transaction =
connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updatePropertyName"));
try {
if (bindingProperty.isDynamic()) {
targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
} else {
targetNode.bindingProperty(newName).setExpression(expression);
}
targetNode.removeProperty(bindingProperty.name());
transaction.commit(); //committing in the try block
} catch (Exception &e) { //better save then sorry
m_exceptionError = e.description();
QTimer::singleShot(200, this, &BindingModel::handleException);
}
QStandardItem* idItem = item(rowNumber, 0);
BindingProperty newBindingProperty = targetNode.bindingProperty(newName);
updateCustomData(idItem, newBindingProperty);
} else {
qWarning() << "BindingModel::updatePropertyName invalid property name";
}
}
ModelNode BindingModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
{
ModelNode modelNode;
if (id != QLatin1String("parent")) {
modelNode = connectionView()->modelNodeForId(id);
} else {
if (targetNode.hasParentProperty()) {
modelNode = targetNode.parentProperty().parentModelNode();
}
}
return modelNode;
}
void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty)
{
item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
item->setData(bindingProperty.name(), Qt::UserRole + 2);
updateDisplayRoles(item, bindingProperty);
}
int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
{
for (int i=0; i < rowCount(); i++) {
if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
return i;
}
//not found
return -1;
}
bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
{
//TODO reimplement using existing helper functions
//### todo we assume no expressions yet
const QString expression = bindingProperty.expression();
if (true) {
const QStringList stringList = expression.split(QLatin1String("."));
*sourceNode = stringList.constFirst();
QString propertyName;
for (int i = 1; i < stringList.size(); i++) {
propertyName += stringList.at(i);
if (i != stringList.size() - 1)
propertyName += QLatin1String(".");
}
*sourceProperty = propertyName;
}
return true;
}
void BindingModel::updateDisplayRole(int row, int columns, const QString &string)
{
QModelIndex modelIndex = index(row, columns);
if (data(modelIndex).toString() != string)
setData(modelIndex, string);
}
void BindingModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
if (!m_handleDataChanged)
return;
if (topLeft != bottomRight) {
qWarning() << "BindingModel::handleDataChanged multi edit?";
return;
}
m_lock = true;
int currentColumn = topLeft.column();
int currentRow = topLeft.row();
switch (currentColumn) {
case TargetModelNodeRow: {
//updating user data
} break;
case TargetPropertyNameRow: {
updatePropertyName(currentRow);
} break;
case SourceModelNodeRow: {
updateExpression(currentRow);
} break;
case SourcePropertyNameRow: {
updateExpression(currentRow);
} break;
default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
}
m_lock = false;
}
void BindingModel::handleException()
{
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
resetModel();
}
QHash<int, QByteArray> BindingModel::roleNames() const
{
static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"},
{TargetPropertyNameRole, "targetProperty"},
{SourceNameRole, "source"},
{SourcePropertyNameRole, "sourceProperty"}};
return roleNames;
}
BindingModelBackendDelegate *BindingModel::delegate() const BindingModelBackendDelegate *BindingModel::delegate() const
{ {
return m_delegate; return m_delegate;
} }
BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent) int BindingModel::currentIndex() const
{
return m_currentIndex;
}
BindingProperty BindingModel::currentProperty() const
{
return propertyForRow(m_currentIndex);
}
BindingProperty BindingModel::propertyForRow(int row) const
{
if (!m_connectionView)
return {};
if (!m_connectionView->isAttached())
return {};
if (auto *item = itemForRow(row)) {
int internalId = item->internalId();
if (ModelNode node = m_connectionView->modelNodeForInternalId(internalId); node.isValid())
return node.bindingProperty(item->targetPropertyName());
}
return {};
}
static PropertyName unusedProperty(const ModelNode &modelNode)
{
if (modelNode.metaInfo().isValid()) {
for (const auto &property : modelNode.metaInfo().properties()) {
if (property.isWritable() && !modelNode.hasProperty(property.name()))
return property.name();
}
}
return "none";
}
void BindingModel::add()
{
if (const QList<ModelNode> nodes = connectionView()->selectedModelNodes(); nodes.size() == 1) {
const ModelNode modelNode = nodes.constFirst();
if (modelNode.isValid()) {
try {
PropertyName name = unusedProperty(modelNode);
modelNode.bindingProperty(name).setExpression(QLatin1String("none.none"));
} catch (RewritingException &e) {
showErrorMessage(e.description());
reset();
}
}
} else {
qWarning() << __FUNCTION__ << " Requires exactly one selected node";
}
}
void BindingModel::remove(int row)
{
if (BindingProperty property = propertyForRow(row); property.isValid()) {
ModelNode node = property.parentModelNode();
node.removeProperty(property.name());
}
reset();
}
void BindingModel::reset(const QList<ModelNode> &nodes)
{
if (!connectionView())
return;
if (!connectionView()->isAttached())
return;
AbstractProperty current = currentProperty();
clear();
if (!nodes.isEmpty()) {
for (const ModelNode &modelNode : nodes)
addModelNode(modelNode);
} else {
for (const ModelNode &modelNode : connectionView()->selectedModelNodes())
addModelNode(modelNode);
}
setCurrentProperty(current);
}
void BindingModel::setCurrentIndex(int i)
{
if (m_currentIndex != i) {
m_currentIndex = i;
emit currentIndexChanged();
}
m_delegate->update(currentProperty(), m_connectionView);
}
void BindingModel::setCurrentProperty(const AbstractProperty &property)
{
if (auto index = rowForProperty(property))
setCurrentIndex(*index);
}
void BindingModel::updateItem(const BindingProperty &property)
{
if (auto *item = itemForProperty(property))
item->updateProperty(property);
else
appendRow(new BindingModelItem(property));
}
void BindingModel::removeItem(const AbstractProperty &property)
{
AbstractProperty current = currentProperty();
if (auto index = rowForProperty(property))
static_cast<void>(removeRow(*index));
setCurrentProperty(current);
emit currentIndexChanged();
}
void BindingModel::commitExpression(int row, const QString &expression)
{
QTC_ASSERT(connectionView(), return);
BindingProperty bindingProperty = propertyForRow(row);
if (!bindingProperty.isValid())
return;
connectionView()->executeInTransaction(__FUNCTION__, [&bindingProperty, expression]() {
bindingProperty.setExpression(expression.trimmed());
});
}
QHash<int, QByteArray> BindingModel::roleNames() const
{
return BindingModelItem::roleNames();
}
std::optional<int> BindingModel::rowForProperty(const AbstractProperty &property) const
{
PropertyName name = property.name();
int internalId = property.parentModelNode().internalId();
for (int i = 0; i < rowCount(); ++i) {
if (auto *item = itemForRow(i)) {
if (item->targetPropertyName() == name && item->internalId() == internalId)
return i;
}
}
return std::nullopt;
}
BindingModelItem *BindingModel::itemForRow(int row) const
{
if (QModelIndex idx = index(row, 0); idx.isValid())
return dynamic_cast<BindingModelItem *>(itemFromIndex(idx));
return nullptr;
}
BindingModelItem *BindingModel::itemForProperty(const AbstractProperty &property) const
{
if (auto row = rowForProperty(property))
return itemForRow(*row);
return nullptr;
}
void BindingModel::addModelNode(const ModelNode &node)
{
if (!node.isValid())
return;
const QList<BindingProperty> bindingProperties = node.bindingProperties();
for (const BindingProperty &property : bindingProperties)
appendRow(new BindingModelItem(property));
}
BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent)
: QObject(parent)
, m_targetNode()
, m_property()
, m_sourceNode()
, m_sourceNodeProperty()
{ {
connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() {
handleSourceNodeChanged(); expressionChanged();
}); });
connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() {
handleSourcePropertyChanged(); expressionChanged();
}); });
} }
int BindingModelBackendDelegate::currentRow() const void BindingModelBackendDelegate::update(const BindingProperty &property, AbstractView *view)
{ {
return m_currentRow; if (!property.isValid())
}
void BindingModelBackendDelegate::setCurrentRow(int i)
{
// See BindingDelegate::createEditor
if (m_currentRow == i)
return; return;
m_currentRow = i; auto addName = [](QStringList&& list, const QString& name) {
if (!list.contains(name))
list.prepend(name);
return std::move(list);
};
//setup auto [sourceNodeName, sourcePropertyName] = splitExpression(property.expression());
BindingModel *model = qobject_cast<BindingModel *>(parent()); QString targetName = QString::fromUtf8(property.name());
m_targetNode = idOrTypeName(property.parentModelNode());
QTC_ASSERT(model, return );
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
QString idLabel = bindingProperty.parentModelNode().id();
if (idLabel.isEmpty())
idLabel = bindingProperty.parentModelNode().simplifiedTypeName();
m_targetNode = idLabel;
emit targetNodeChanged();
m_property.setModel(model->possibleTargetProperties(bindingProperty));
m_property.setCurrentText(QString::fromUtf8(bindingProperty.name()));
QStringList sourceNodes;
for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) {
if (!modelNode.id().isEmpty())
sourceNodes.append(modelNode.id());
}
std::sort(sourceNodes.begin(), sourceNodes.end());
QString sourceNodeName;
QString sourcePropertyName;
model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
if (!sourceNodes.contains(sourceNodeName))
sourceNodes.append(sourceNodeName);
m_sourceNode.setModel(sourceNodes);
auto modelNodes = addName(availableModelNodes(view), sourceNodeName);
m_sourceNode.setModel(modelNodes);
m_sourceNode.setCurrentText(sourceNodeName); m_sourceNode.setCurrentText(sourceNodeName);
m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty)); auto sourceproperties = addName(availableSourceProperties(property, view), sourcePropertyName);
m_sourceNodeProperty.setModel(sourceproperties);
m_sourceNodeProperty.setCurrentText(sourcePropertyName); m_sourceNodeProperty.setCurrentText(sourcePropertyName);
}
void BindingModelBackendDelegate::handleException() auto targetProperties = addName(availableTargetProperties(property), targetName);
{ m_property.setModel(targetProperties);
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); m_property.setCurrentText(targetName);
//reset
emit targetNodeChanged();
} }
QString BindingModelBackendDelegate::targetNode() const QString BindingModelBackendDelegate::targetNode() const
@@ -595,54 +281,22 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
return &m_sourceNodeProperty; return &m_sourceNodeProperty;
} }
void BindingModelBackendDelegate::handleSourceNodeChanged() void BindingModelBackendDelegate::expressionChanged() const
{ {
BindingModel *model = qobject_cast<BindingModel *>(parent()); BindingModel *model = qobject_cast<BindingModel *>(parent());
QTC_ASSERT(model, return);
QTC_ASSERT(model, return );
QTC_ASSERT(model->connectionView(), return );
const QString sourceNode = m_sourceNode.currentText(); const QString sourceNode = m_sourceNode.currentText();
const QString sourceProperty = m_sourceNodeProperty.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText();
QString expression; QString expression;
if (sourceProperty.isEmpty()) { if (sourceProperty.isEmpty())
expression = sourceNode; expression = sourceNode;
} else { else
expression = sourceNode + QLatin1String(".") + sourceProperty; expression = sourceNode + QLatin1String(".") + sourceProperty;
}
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); int row = model->currentIndex();
model->connectionView()->executeInTransaction("BindingModel::updateExpression", model->commitExpression(row, expression);
[&bindingProperty, expression]() {
bindingProperty.setExpression(
expression.trimmed());
});
}
void BindingModelBackendDelegate::handleSourcePropertyChanged()
{
BindingModel *model = qobject_cast<BindingModel *>(parent());
QTC_ASSERT(model, return );
QTC_ASSERT(model->connectionView(), return );
const QString sourceNode = m_sourceNode.currentText();
const QString sourceProperty = m_sourceNodeProperty.currentText();
QString expression;
if (sourceProperty.isEmpty()) {
expression = sourceNode;
} else {
expression = sourceNode + QLatin1String(".") + sourceProperty;
}
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
[&bindingProperty, expression]() {
bindingProperty.setExpression(
expression.trimmed());
});
} }
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -3,132 +3,98 @@
#pragma once #pragma once
#include <modelnode.h> #include <abstractproperty.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <variantproperty.h> #include <modelnode.h>
#include <studioquickwidget.h> #include <studioquickwidget.h>
#include <variantproperty.h>
#include <QStandardItemModel> #include <QStandardItemModel>
namespace QmlDesigner { namespace QmlDesigner {
class ConnectionView;
class BindingModelBackendDelegate; class BindingModelBackendDelegate;
class BindingModelItem;
class ConnectionView;
class BindingModel : public QStandardItemModel class BindingModel : public QStandardItemModel
{ {
Q_OBJECT Q_OBJECT
signals:
void currentIndexChanged();
public:
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT) Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
public: public:
enum ColumnRoles {
TargetModelNodeRow = 0,
TargetPropertyNameRow = 1,
SourceModelNodeRow = 2,
SourcePropertyNameRow = 3
};
enum UserRoles {
InternalIdRole = Qt::UserRole + 2,
TargetNameRole,
TargetPropertyNameRole,
SourceNameRole,
SourcePropertyNameRole
};
BindingModel(ConnectionView *parent = nullptr); BindingModel(ConnectionView *parent = nullptr);
void bindingChanged(const BindingProperty &bindingProperty);
void bindingRemoved(const BindingProperty &bindingProperty);
void selectionChanged(const QList<ModelNode> &selectedNodes);
ConnectionView *connectionView() const; ConnectionView *connectionView() const;
BindingProperty bindingPropertyForRow(int rowNumber) const; BindingModelBackendDelegate *delegate() const;
QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const; int currentIndex() const;
void deleteBindindByRow(int rowNumber); BindingProperty currentProperty() const;
void addBindingForCurrentNode(); BindingProperty propertyForRow(int row) const;
void resetModel();
Q_INVOKABLE void add(); Q_INVOKABLE void add();
Q_INVOKABLE void remove(int row); Q_INVOKABLE void remove(int row);
int currentIndex() const; void reset(const QList<ModelNode> &selectedNodes = {});
void setCurrentIndex(int i); void setCurrentIndex(int i);
bool getExpressionStrings(const BindingProperty &bindingProperty, void setCurrentProperty(const AbstractProperty &property);
QString *sourceNode,
QString *sourceProperty);
signals: void updateItem(const BindingProperty &property);
void currentIndexChanged(); void removeItem(const AbstractProperty &property);
void commitExpression(int row, const QString &expression);
protected: protected:
void addBindingProperty(const BindingProperty &property);
void updateBindingProperty(int rowNumber);
void addModelNode(const ModelNode &modelNode);
void updateExpression(int row);
void updatePropertyName(int rowNumber);
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
int findRowForBinding(const BindingProperty &bindingProperty);
void updateDisplayRole(int row, int columns, const QString &string);
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
BindingModelBackendDelegate *delegate() const;
private: private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); std::optional<int> rowForProperty(const AbstractProperty &property) const;
void handleException(); BindingModelItem *itemForRow(int row) const;
BindingModelItem *itemForProperty(const AbstractProperty &property) const;
void addModelNode(const ModelNode &modelNode);
private: private:
ConnectionView *m_connectionView; ConnectionView *m_connectionView = nullptr;
bool m_lock = false;
bool m_handleDataChanged = false;
QString m_exceptionError;
int m_currentIndex = 0;
BindingModelBackendDelegate *m_delegate = nullptr; BindingModelBackendDelegate *m_delegate = nullptr;
int m_currentIndex = -1;
}; };
class BindingModelBackendDelegate : public QObject class BindingModelBackendDelegate : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
signals:
void targetNodeChanged();
public: public:
BindingModelBackendDelegate(BindingModel *parent = nullptr); BindingModelBackendDelegate(BindingModel *parent = nullptr);
signals: void update(const BindingProperty &property, AbstractView *view);
void currentRowChanged();
//void nameChanged();
void targetNodeChanged();
private: private:
int currentRow() const;
void setCurrentRow(int i);
void handleException();
QString targetNode() const; QString targetNode() const;
void expressionChanged() const;
StudioQmlComboBoxBackend *property(); StudioQmlComboBoxBackend *property();
StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceNode();
StudioQmlComboBoxBackend *sourceProperty(); StudioQmlComboBoxBackend *sourceProperty();
void handleSourceNodeChanged(); QString m_targetNode;
void handleSourcePropertyChanged();
StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_property;
StudioQmlComboBoxBackend m_sourceNode; StudioQmlComboBoxBackend m_sourceNode;
StudioQmlComboBoxBackend m_sourceNodeProperty; StudioQmlComboBoxBackend m_sourceNodeProperty;
QString m_exceptionError;
int m_currentRow = -1;
QString m_targetNode;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -0,0 +1,55 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "bindingmodelitem.h"
#include "connectioneditorutils.h"
#include <modelnode.h>
#include <qmldesignertr.h>
namespace QmlDesigner {
QHash<int, QByteArray> BindingModelItem::roleNames()
{
return {{TargetNameRole, "target"},
{TargetPropertyNameRole, "targetProperty"},
{SourceNameRole, "source"},
{SourcePropertyNameRole, "sourceProperty"}};
}
QStringList BindingModelItem::headerLabels()
{
return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Source Item"), Tr::tr("Source Property")};
}
BindingModelItem::BindingModelItem(const BindingProperty &property)
: QStandardItem(idOrTypeName(property.parentModelNode()))
{
updateProperty(property);
}
int BindingModelItem::internalId() const
{
return data(InternalIdRole).toInt();
}
PropertyName BindingModelItem::targetPropertyName() const
{
return data(TargetPropertyNameRole).toString().toUtf8();
}
void BindingModelItem::updateProperty(const BindingProperty &property)
{
setData(property.parentModelNode().internalId(), InternalIdRole);
setData(idOrTypeName(property.parentModelNode()), TargetNameRole);
setData(property.name(), TargetPropertyNameRole);
// TODO: Make this safe when the new codemodel allows it.
if (auto expression = property.expression(); !expression.isEmpty()) {
auto [nodeName, propertyName] = splitExpression(expression);
setData(nodeName, SourceNameRole);
setData(propertyName, SourcePropertyNameRole);
}
}
} // End namespace QmlDesigner.

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <bindingproperty.h>
#include <modelfwd.h>
#include <QStandardItem>
namespace QmlDesigner {
class BindingModelItem : public QStandardItem
{
public:
enum UserRoles {
InternalIdRole = Qt::UserRole + 2,
TargetNameRole,
TargetPropertyNameRole,
SourceNameRole,
SourcePropertyNameRole
};
static QHash<int, QByteArray> roleNames();
static QStringList headerLabels();
BindingModelItem(const BindingProperty &property);
int internalId() const;
PropertyName targetPropertyName() const;
void updateProperty(const BindingProperty &property);
};
} // End namespace QmlDesigner.

View File

@@ -0,0 +1,392 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorutils.h"
#include <QtCore/qvariant.h>
#include <abstractproperty.h>
#include <abstractview.h>
#include <bindingproperty.h>
#include <modelnode.h>
#include <nodeabstractproperty.h>
#include <nodemetainfo.h>
#include <rewriterview.h>
#include <rewritingexception.h>
#include <type_traits>
#include <variantproperty.h>
#include <qmldesignertr.h>
#include <utils/algorithm.h>
#include <QMessageBox>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QTimer>
namespace QmlDesigner {
void callLater(const std::function<void()> &fun)
{
QTimer::singleShot(0, fun);
}
void showErrorMessage(const QString &text)
{
callLater([text]() { QMessageBox::warning(nullptr, Tr::tr("Error"), text); });
}
QString idOrTypeName(const ModelNode &modelNode)
{
QString idLabel = modelNode.id();
if (idLabel.isEmpty())
idLabel = modelNode.simplifiedTypeName();
return idLabel;
}
PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode)
{
PropertyName name = suggestion;
if (!modelNode.isValid() || !modelNode.metaInfo().isValid())
return name;
int i = 0;
while (true) {
if (!modelNode.hasProperty(name) && !modelNode.metaInfo().hasProperty(name))
return name;
name = suggestion + QString::number(i++).toLatin1();
}
return {};
}
NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property)
{
// Note: Uses old mechanism to create the NodeMetaInfo and supports
// only types we care about in the connection editor.
// TODO: Support all possible AbstractProperty types and move to the
// AbstractProperty class.
if (property.dynamicTypeName() == "bool")
return property.model()->boolMetaInfo();
else if (property.dynamicTypeName() == "int")
return property.model()->metaInfo("QML.int");
else if (property.dynamicTypeName() == "real")
return property.model()->metaInfo("QML.real");
else if (property.dynamicTypeName() == "color")
return property.model()->metaInfo("QML.color");
else if (property.dynamicTypeName() == "string")
return property.model()->metaInfo("QML.string");
else if (property.dynamicTypeName() == "url")
return property.model()->metaInfo("QML.url");
else if (property.dynamicTypeName() == "variant")
return property.model()->metaInfo("QML.variant");
else
qWarning() << __FUNCTION__ << " type " << property.dynamicTypeName() << "not found";
return { };
}
QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName)
{
QVariant returnValue = variant;
if (typeName == "int") {
bool ok;
returnValue = variant.toInt(&ok);
if (!ok)
returnValue = 0;
} else if (typeName == "real") {
bool ok;
returnValue = variant.toReal(&ok);
if (!ok)
returnValue = 0.0;
} else if (typeName == "string") {
returnValue = variant.toString();
} else if (typeName == "bool") {
returnValue = variant.toBool();
} else if (typeName == "url") {
returnValue = variant.toUrl();
} else if (typeName == "color") {
if (QColor::isValidColor(variant.toString()))
returnValue = variant.toString();
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 = "null";
}
return returnValue;
}
template<typename T>
void convertPropertyType(const T &property, const QVariant &value)
{
if (!property.isValid())
return;
ModelNode node = property.parentModelNode();
if (!node.isValid())
return;
PropertyName name = property.name();
TypeName type = property.dynamicTypeName();
node.removeProperty(name);
if constexpr (std::is_same_v<T, VariantProperty>) {
BindingProperty newProperty = node.bindingProperty(name);
if (newProperty.isValid())
newProperty.setDynamicTypeNameAndExpression(type, value.toString());
} else if constexpr (std::is_same_v<T, BindingProperty>) {
VariantProperty newProperty = node.variantProperty(name);
if (newProperty.isValid())
newProperty.setDynamicTypeNameAndValue(type, value);
}
}
void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value)
{
convertPropertyType(property, value);
}
void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value)
{
convertPropertyType(property, value);
}
bool isBindingExpression(const QVariant& value)
{
if (value.metaType().id() != QMetaType::QString)
return false;
QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
QRegularExpressionMatch match = regexp.match(value.toString());
return match.hasMatch();
}
bool isDynamicVariantPropertyType(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);
}
QVariant defaultValueForType(const TypeName &type)
{
QVariant value;
if (type == "int")
value = 0;
else if (type == "real")
value = 0.0;
else if (type == "color")
value = QColor(255, 255, 255);
else if (type == "string")
value = "This is a string";
else if (type == "bool")
value = false;
else if (type == "url")
value = "";
else if (type == "variant")
value = "";
return value;
}
QString defaultExpressionForType(const TypeName &type)
{
QString expression;
if (type == "alias")
expression = "null";
else if (type == "TextureInput")
expression = "null";
else if (type == "vector2d")
expression = "Qt.vector2d(0, 0)";
else if (type == "vector3d")
expression = "Qt.vector3d(0, 0, 0)";
else if (type == "vector4d")
expression = "Qt.vector4d(0, 0, 0 ,0)";
return expression;
}
QStringList availableModelNodes(AbstractView *view)
{
QStringList sourceNodes;
for (const ModelNode &modelNode : view->allModelNodes()) {
if (!modelNode.id().isEmpty())
sourceNodes.append(modelNode.id());
}
std::sort(sourceNodes.begin(), sourceNodes.end());
return sourceNodes;
}
QStringList dynamicPropertyNamesFromNode(const ModelNode& node)
{
QStringList dynamicProperties;
for (const VariantProperty &variantProperty : node.variantProperties()) {
if (variantProperty.isDynamic())
dynamicProperties << QString::fromUtf8(variantProperty.name());
}
for (const BindingProperty &bindingProperty : node.bindingProperties()) {
if (bindingProperty.isDynamic())
dynamicProperties << QString::fromUtf8((bindingProperty.name()));
}
return dynamicProperties;
}
QStringList availableTargetProperties(const BindingProperty &bindingProperty)
{
const ModelNode modelNode = bindingProperty.parentModelNode();
if (!modelNode.isValid()) {
qWarning() << __FUNCTION__ << " invalid model node";
return {};
}
NodeMetaInfo metaInfo = modelNode.metaInfo();
if (metaInfo.isValid()) {
const auto properties = metaInfo.properties();
QStringList writableProperties;
writableProperties.reserve(static_cast<int>(properties.size()));
for (const auto &property : properties) {
if (property.isWritable())
writableProperties.push_back(QString::fromUtf8(property.name()));
}
return dynamicPropertyNamesFromNode(modelNode) + writableProperties;
}
return dynamicPropertyNamesFromNode(modelNode);
}
ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const ModelNode &targetNode)
{
if (id != QLatin1String("parent"))
return view->modelNodeForId(id);
if (targetNode.hasParentProperty())
return targetNode.parentProperty().parentModelNode();
return {};
}
bool metaInfoIsCompatible(const NodeMetaInfo& sourceType, const PropertyMetaInfo& metaInfo)
{
if (sourceType.isVariant())
return true;
NodeMetaInfo targetType = metaInfo.propertyType();
if (sourceType.isBool() && targetType.isBool())
return true;
if (sourceType == targetType)
return true;
if (sourceType.isNumber() && targetType.isNumber())
return true;
if (sourceType.isString() && targetType.isString())
return true;
if (sourceType.isUrl() && targetType.isUrl())
return true;
if (sourceType.isColor() && targetType.isColor())
return true;
return false;
}
QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view)
{
const QString expression = bindingProperty.expression();
const QStringList stringlist = expression.split(QLatin1String("."));
QStringList possibleProperties;
const QString &id = stringlist.constFirst();
ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode());
if (!modelNode.isValid()) {
//if it's not a valid model node, maybe it's a singleton
if (RewriterView *rv = view->rewriterView()) {
for (const QmlTypeData &data : rv->getQMLTypes()) {
if (!data.typeName.isEmpty() && data.typeName == id) {
NodeMetaInfo metaInfo = view->model()->metaInfo(data.typeName.toUtf8());
if (metaInfo.isValid()) {
for (const auto &property : metaInfo.properties()) {
//without check for now
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
return possibleProperties;
}
}
}
}
qWarning() << __FUNCTION__ << " invalid model node";
return QStringList();
}
possibleProperties = possibleProperties + dynamicPropertyNamesFromNode(modelNode);
NodeMetaInfo type;
if (bindingProperty.isDynamic()) {
type = dynamicTypeMetaInfo(bindingProperty);
} else if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) {
type = metaInfo.property(bindingProperty.name()).propertyType();
} else
qWarning() << __FUNCTION__ << " no meta info for target node";
NodeMetaInfo metaInfo = modelNode.metaInfo();
if (metaInfo.isValid()) {
for (const auto &property : metaInfo.properties()) {
if (metaInfoIsCompatible(type, property) )
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
} else {
qWarning() << __FUNCTION__ << " no meta info for source node";
}
return possibleProperties;
}
QList<AbstractProperty> dynamicPropertiesFromNode(const ModelNode &node)
{
auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); };
auto byName = [](const AbstractProperty &a, const AbstractProperty &b) {
return a.name() < b.name();
};
QList<AbstractProperty> dynamicProperties = Utils::filtered(node.properties(), isDynamic);
Utils::sort(dynamicProperties, byName);
return dynamicProperties;
}
std::pair<QString, QString> splitExpression(const QString &expression)
{
// ### Todo from original code (getExpressionStrings):
// We assume no expressions yet
const QStringList stringList = expression.split(QLatin1String("."));
QString sourceNode = stringList.constFirst();
QString propertyName;
for (int i = 1; i < stringList.size(); ++i) {
propertyName += stringList.at(i);
if (i != stringList.size() - 1)
propertyName += QLatin1String(".");
}
if (propertyName.isEmpty())
std::swap(sourceNode, propertyName);
return {sourceNode, propertyName};
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,44 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "modelfwd.h"
#include "propertymetainfo.h"
#include <QList>
#include <QString>
#include <QVariant>
namespace QmlDesigner {
class AbstractView;
class AbstractProperty;
class BindingProperty;
class ModelNode;
class VariantProperty;
void callLater(const std::function<void()> &fun);
void showErrorMessage(const QString &text);
QString idOrTypeName(const ModelNode &modelNode);
PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode);
NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property);
QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName);
void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value);
void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value);
bool isBindingExpression(const QVariant& value);
bool isDynamicVariantPropertyType(const TypeName &type);
QVariant defaultValueForType(const TypeName &type);
QString defaultExpressionForType(const TypeName &type);
QStringList availableModelNodes(AbstractView *view);
QStringList availableTargetProperties(const BindingProperty &bindingProperty);
QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view);
QList<AbstractProperty> dynamicPropertiesFromNode(const ModelNode &node);
std::pair<QString, QString> splitExpression(const QString &expression);
} // namespace QmlDesigner

View File

@@ -80,10 +80,6 @@ void ConnectionModel::resetModel()
for (const ModelNode &modelNode : connectionView()->allModelNodes()) for (const ModelNode &modelNode : connectionView()->allModelNodes())
addModelNode(modelNode); addModelNode(modelNode);
} }
const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0);
connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80);
endResetModel(); endResetModel();
} }

View File

@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectionview.h" #include "connectionview.h"
#include "connectionviewwidget.h"
#include "backendmodel.h" #include "backendmodel.h"
#include "bindingmodel.h" #include "bindingmodel.h"
@@ -70,9 +69,13 @@ public:
"ConnectionsEditorEditorBackend", 1); "ConnectionsEditorEditorBackend", 1);
map->setProperties( map->setProperties(
{{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}, {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}});
{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())},
{"dynamicPropertiesModel", map->setProperties(
{{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}});
map->setProperties(
{{"dynamicPropertiesModel",
QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}});
qmlRegisterType<ConnectionModelBackendDelegate>("ConnectionsEditorEditorBackend", qmlRegisterType<ConnectionModelBackendDelegate>("ConnectionsEditorEditorBackend",
@@ -98,7 +101,6 @@ public:
// init the first load of the QML UI elements // init the first load of the QML UI elements
reloadQmlSource(); reloadQmlSource();
} }
~ConnectionViewQuickWidget() = default; ~ConnectionViewQuickWidget() = default;
static QString qmlSourcesPath() static QString qmlSourcesPath()
@@ -136,17 +138,13 @@ private:
}; };
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies) ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()), : AbstractView{externalDependencies}
m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)), , m_connectionModel(new ConnectionModel(this))
m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)), , m_bindingModel(new BindingModel(this))
m_backendModel(new BackendModel(this)), , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this))
m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) , m_backendModel(new BackendModel(this))
{ , m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
connectionViewWidget()->setBindingModel(m_bindingModel); {}
connectionViewWidget()->setConnectionModel(m_connectionModel);
connectionViewWidget()->setDynamicPropertiesModel(m_dynamicPropertiesModel);
connectionViewWidget()->setBackendModel(m_backendModel);
}
ConnectionView::~ConnectionView() ConnectionView::~ConnectionView()
{ {
@@ -156,25 +154,22 @@ ConnectionView::~ConnectionView()
void ConnectionView::modelAttached(Model *model) void ConnectionView::modelAttached(Model *model)
{ {
AbstractView::modelAttached(model); AbstractView::modelAttached(model);
bindingModel()->selectionChanged(QList<ModelNode>()); bindingModel()->reset();
dynamicPropertiesModel()->reset(); dynamicPropertiesModel()->reset();
connectionModel()->resetModel(); connectionModel()->resetModel();
connectionViewWidget()->resetItemViews();
backendModel()->resetModel(); backendModel()->resetModel();
} }
void ConnectionView::modelAboutToBeDetached(Model *model) void ConnectionView::modelAboutToBeDetached(Model *model)
{ {
AbstractView::modelAboutToBeDetached(model); AbstractView::modelAboutToBeDetached(model);
bindingModel()->selectionChanged(QList<ModelNode>()); bindingModel()->reset();
dynamicPropertiesModel()->reset(); dynamicPropertiesModel()->reset();
connectionModel()->resetModel(); connectionModel()->resetModel();
connectionViewWidget()->resetItemViews();
} }
void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/)
{ {
//bindings
connectionModel()->resetModel(); connectionModel()->resetModel();
} }
@@ -194,8 +189,8 @@ void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstra
void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/) void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/)
{ {
connectionModel()->resetModel(); connectionModel()->resetModel();
bindingModel()->resetModel(); bindingModel()->reset();
dynamicPropertiesModel()->resetModel(); dynamicPropertiesModel()->reset();
} }
void ConnectionView::propertiesRemoved(const QList<AbstractProperty> &propertyList) void ConnectionView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
@@ -212,10 +207,10 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &p
{ {
for (const AbstractProperty &property : propertyList) { for (const AbstractProperty &property : propertyList) {
if (property.isBindingProperty()) { if (property.isBindingProperty()) {
bindingModel()->bindingRemoved(property.toBindingProperty()); bindingModel()->removeItem(property);
dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty()); dynamicPropertiesModel()->removeItem(property);
} else if (property.isVariantProperty()) { } else if (property.isVariantProperty()) {
dynamicPropertiesModel()->variantRemoved(property.toVariantProperty()); dynamicPropertiesModel()->removeItem(property);
} else if (property.isSignalHandlerProperty()) { } else if (property.isSignalHandlerProperty()) {
connectionModel()->removeRowFromTable(property.toSignalHandlerProperty()); connectionModel()->removeRowFromTable(property.toSignalHandlerProperty());
} }
@@ -227,7 +222,7 @@ void ConnectionView::variantPropertiesChanged(const QList<VariantProperty> &prop
{ {
for (const VariantProperty &variantProperty : propertyList) { for (const VariantProperty &variantProperty : propertyList) {
if (variantProperty.isDynamic()) if (variantProperty.isDynamic())
dynamicPropertiesModel()->variantPropertyChanged(variantProperty); dynamicPropertiesModel()->updateItem(variantProperty);
if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode()) if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode())
backendModel()->resetModel(); backendModel()->resetModel();
@@ -241,9 +236,9 @@ void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &prop
AbstractView::PropertyChangeFlags /*propertyChange*/) AbstractView::PropertyChangeFlags /*propertyChange*/)
{ {
for (const BindingProperty &bindingProperty : propertyList) { for (const BindingProperty &bindingProperty : propertyList) {
bindingModel()->bindingChanged(bindingProperty); bindingModel()->updateItem(bindingProperty);
if (bindingProperty.isDynamic()) if (bindingProperty.isDynamic())
dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty); dynamicPropertiesModel()->updateItem(bindingProperty);
if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode()) if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode())
backendModel()->resetModel(); backendModel()->resetModel();
@@ -263,39 +258,8 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVector<SignalHandlerP
void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList, void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
const QList<ModelNode> & /*lastSelectedNodeList*/) const QList<ModelNode> & /*lastSelectedNodeList*/)
{ {
bindingModel()->selectionChanged(selectedNodeList); bindingModel()->reset(selectedNodeList);
dynamicPropertiesModel()->reset(); dynamicPropertiesModel()->reset();
connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex());
connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex());
if (connectionViewWidget()->currentTab() == ConnectionViewWidget::BindingTab
|| connectionViewWidget()->currentTab() == ConnectionViewWidget::DynamicPropertiesTab)
emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.size() == 1);
}
void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node,
AuxiliaryDataKeyView key,
const QVariant &data)
{
// Check if the auxiliary data is actually the locked property or if it is unlocked
if (key != lockedProperty || !data.toBool())
return;
QItemSelectionModel *selectionModel = connectionTableView()->selectionModel();
if (!selectionModel->hasSelection())
return;
QModelIndex modelIndex = selectionModel->currentIndex();
if (!modelIndex.isValid() || !model())
return;
const int internalId = connectionModel()->data(connectionModel()->index(modelIndex.row(),
ConnectionModel::TargetModelNodeRow),
ConnectionModel::UserRoles::InternalIdRole).toInt();
ModelNode modelNode = modelNodeForInternalId(internalId);
if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode))
selectionModel->clearSelection();
} }
void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/)
@@ -310,14 +274,7 @@ void ConnectionView::currentStateChanged(const ModelNode &)
WidgetInfo ConnectionView::widgetInfo() WidgetInfo ConnectionView::widgetInfo()
{ {
/* Enable new connection editor here */ return createWidgetInfo(m_connectionViewQuickWidget.data(),
const bool newEditor = true;
QWidget *widget = m_connectionViewWidget.data();
if (newEditor)
widget = m_connectionViewQuickWidget.data();
return createWidgetInfo(widget,
QLatin1String("ConnectionView"), QLatin1String("ConnectionView"),
WidgetInfo::LeftPane, WidgetInfo::LeftPane,
0, 0,
@@ -334,31 +291,6 @@ bool ConnectionView::isWidgetEnabled()
return widgetInfo().widget->isEnabled(); return widgetInfo().widget->isEnabled();
} }
QTableView *ConnectionView::connectionTableView() const
{
return connectionViewWidget()->connectionTableView();
}
QTableView *ConnectionView::bindingTableView() const
{
return connectionViewWidget()->bindingTableView();
}
QTableView *ConnectionView::dynamicPropertiesTableView() const
{
return connectionViewWidget()->dynamicPropertiesTableView();
}
QTableView *ConnectionView::backendView() const
{
return connectionViewWidget()->backendView();
}
ConnectionViewWidget *ConnectionView::connectionViewWidget() const
{
return m_connectionViewWidget.data();
}
ConnectionModel *ConnectionView::connectionModel() const ConnectionModel *ConnectionView::connectionModel() const
{ {
return m_connectionModel; return m_connectionModel;

View File

@@ -51,9 +51,6 @@ public:
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override; const QList<ModelNode> &lastSelectedNodeList) override;
void auxiliaryDataChanged(const ModelNode &node,
AuxiliaryDataKeyView key,
const QVariant &data) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
@@ -63,14 +60,8 @@ public:
bool hasWidget() const override; bool hasWidget() const override;
bool isWidgetEnabled(); bool isWidgetEnabled();
QTableView *connectionTableView() const;
QTableView *bindingTableView() const;
QTableView *dynamicPropertiesTableView() const;
QTableView *backendView() const;
DynamicPropertiesModel *dynamicPropertiesModel() const; DynamicPropertiesModel *dynamicPropertiesModel() const;
ConnectionViewWidget *connectionViewWidget() const;
ConnectionModel *connectionModel() const; ConnectionModel *connectionModel() const;
BindingModel *bindingModel() const; BindingModel *bindingModel() const;
BackendModel *backendModel() const; BackendModel *backendModel() const;
@@ -83,9 +74,7 @@ public:
signals: signals:
void currentIndexChanged(); void currentIndexChanged();
private: //variables private:
QPointer<ConnectionViewWidget> m_connectionViewWidget;
ConnectionModel *m_connectionModel; ConnectionModel *m_connectionModel;
BindingModel *m_bindingModel; BindingModel *m_bindingModel;
DynamicPropertiesModel *m_dynamicPropertiesModel; DynamicPropertiesModel *m_dynamicPropertiesModel;

View File

@@ -1,614 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectionviewwidget.h"
#include "connectionview.h"
#include "ui_connectionviewwidget.h"
#include "delegates.h"
#include "backendmodel.h"
#include "bindingmodel.h"
#include "connectionmodel.h"
#include "dynamicpropertiesmodel.h"
#include "theme.h"
#include "signalhandlerproperty.h"
#include <designeractionmanager.h>
#include <designersettings.h>
#include <qmldesignerplugin.h>
#include <nodemetainfo.h>
#include <bindingeditor/actioneditor.h>
#include <bindingeditor/bindingeditor.h>
#include <coreplugin/coreconstants.h>
#include <qmlprojectmanager/qmlproject.h>
#include <utils/fileutils.h>
#include <utils/utilsicons.h>
#include <QToolButton>
#include <QStyleFactory>
#include <QMenu>
#include <QShortcut>
namespace QmlDesigner {
ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
QFrame(parent),
ui(new Ui::ConnectionViewWidget)
{
m_connectionEditor = new QmlDesigner::ActionEditor(this);
m_bindingEditor = new QmlDesigner::BindingEditor(this);
m_dynamicEditor = new QmlDesigner::BindingEditor(this);
editorForConnection();
editorForBinding();
editorForDynamic();
setWindowTitle(tr("Connections", "Title of connections window"));
ui->setupUi(this);
QStyle *style = QStyleFactory::create("fusion");
ui->stackedWidget->setStyle(style);
//ui->tabWidget->tabBar()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
ui->tabBar->setUsesScrollButtons(true);
ui->tabBar->setElideMode(Qt::ElideRight);
ui->tabBar->addTab(tr("Connections", "Title of connection tab"));
ui->tabBar->addTab(tr("Bindings", "Title of connection tab"));
ui->tabBar->addTab(tr("Properties", "Title of dynamic properties tab"));
const Qt::Alignment headerAlignment = Qt::AlignLeft | Qt::AlignVCenter;
ui->connectionView->horizontalHeader()->setDefaultAlignment(headerAlignment);
ui->bindingView->horizontalHeader()->setDefaultAlignment(headerAlignment);
ui->dynamicPropertiesView->horizontalHeader()->setDefaultAlignment(headerAlignment);
ui->backendView->horizontalHeader()->setDefaultAlignment(headerAlignment);
const QList<QToolButton*> buttons = createToolBarWidgets();
ui->toolBar->setFixedHeight(41);
for (auto toolButton : buttons)
ui->toolBar->addWidget(toolButton);
if (!QmlProjectManager::QmlProject::isQtDesignStudio())
ui->tabBar->addTab(tr("Backends", "Title of dynamic properties view"));
ui->tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
QByteArray sheet = Utils::FileReader::fetchQrc(":/connectionview/stylesheet.css");
setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet)));
connect(ui->tabBar, &QTabBar::currentChanged,
ui->stackedWidget, &QStackedWidget::setCurrentIndex);
connect(ui->tabBar, &QTabBar::currentChanged,
this, &ConnectionViewWidget::handleTabChanged);
ui->stackedWidget->setCurrentIndex(0);
ui->stackedWidget->parentWidget()->hide();
}
ConnectionViewWidget::~ConnectionViewWidget()
{
delete m_connectionEditor;
delete m_bindingEditor;
delete m_dynamicEditor;
delete ui;
}
void ConnectionViewWidget::setBindingModel(BindingModel *model)
{
ui->bindingView->setModel(model);
ui->bindingView->verticalHeader()->hide();
ui->bindingView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->bindingView->setItemDelegate(new BindingDelegate);
connect(ui->bindingView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ConnectionViewWidget::bindingTableViewSelectionChanged);
}
void ConnectionViewWidget::setConnectionModel(ConnectionModel *model)
{
ui->connectionView->setModel(model);
ui->connectionView->verticalHeader()->hide();
ui->connectionView->horizontalHeader()->setDefaultSectionSize(160);
ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->connectionView->setItemDelegate(new ConnectionDelegate);
connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ConnectionViewWidget::connectionTableViewSelectionChanged);
}
void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
{
auto tablePos = [&](QTableView *targetView) {
// adjusting qpoint to the qtableview entrances:
QPoint posInTable(targetView->mapFromGlobal(mapToGlobal(event->pos())));
posInTable.ry() -= targetView->horizontalHeader()->height();
return posInTable;
};
switch (currentTab()) {
case ConnectionTab:
if (ui->connectionView != nullptr) {
QTableView *targetView = ui->connectionView;
// making sure that we have source column in our hands:
const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(ConnectionModel::SourceRow);
if (!index.isValid())
return;
QMenu menu(this);
menu.addAction(tr("Open Connection Editor"), this, [&]() {
auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model());
const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row());
const ModelNode node = property.parentModelNode();
const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString()
+ "." + property.name();
m_connectionEditor->showWidget();
m_connectionEditor->setConnectionValue(index.data().toString());
m_connectionEditor->setModelIndex(index);
m_connectionEditor->setModelNode(node);
m_connectionEditor->prepareConnections();
m_connectionEditor->updateWindowName(targetName);
});
QMap<QString, QVariant> data;
data["ModelNode"] = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data();
data["Signal"] = index.siblingAtColumn(ConnectionModel::TargetPropertyNameRow).data();
DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->designerActionManager();
const auto actions = designerActionManager.actionsForTargetView(
ActionInterface::TargetView::ConnectionEditor);
for (const auto &actionInterface : actions) {
auto *action = actionInterface->action();
action->setData(data);
menu.addAction(action);
}
menu.exec(event->globalPos());
}
break;
case BindingTab:
if (ui->bindingView != nullptr) {
QTableView *targetView = bindingTableView();
const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(BindingModel::SourcePropertyNameRow);
if (!index.isValid())
return;
QMenu menu(this);
menu.addAction(tr("Open Binding Editor"), this, [&]() {
BindingModel *bindingModel = qobject_cast<BindingModel*>(targetView->model());
const BindingProperty property = bindingModel->bindingPropertyForRow(index.row());
if (!property.isValid() || !property.isBindingProperty())
return;
const ModelNode node = property.parentModelNode();
auto model = node.model();
const auto type = property.isDynamic()
? model->metaInfo(property.dynamicTypeName())
: node.metaInfo().property(property.name()).propertyType();
const QString targetName = node.displayName() + "." + property.name();
m_bindingEditor->showWidget();
m_bindingEditor->setBindingValue(property.expression());
m_bindingEditor->setModelNode(node);
m_bindingEditor->setBackendValueType(type);
m_bindingEditor->setTargetName(targetName);
m_bindingEditor->prepareBindings();
m_bindingEditor->updateWindowName();
m_bindingIndex = index;
});
menu.exec(event->globalPos());
}
break;
case DynamicPropertiesTab:
if (ui->dynamicPropertiesView != nullptr) {
QTableView *targetView = dynamicPropertiesTableView();
const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(DynamicPropertiesModel::PropertyValueRow);
if (!index.isValid())
return;
DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(targetView->model());
QMenu menu(this);
menu.addAction(tr("Open Binding Editor"), this, [&]() {
AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row());
if (!abstractProperty.isValid())
return;
const ModelNode node = abstractProperty.parentModelNode();
QString newExpression;
if (abstractProperty.isBindingProperty())
newExpression = abstractProperty.toBindingProperty().expression();
else if (abstractProperty.isVariantProperty())
newExpression = abstractProperty.toVariantProperty().value().toString();
else
return;
const QString targetName = node.displayName() + "." + abstractProperty.name();
auto model = node.model();
m_dynamicEditor->showWidget();
m_dynamicEditor->setBindingValue(newExpression);
m_dynamicEditor->setModelNode(node);
m_dynamicEditor->setBackendValueType(
model->metaInfo(abstractProperty.dynamicTypeName()));
m_dynamicEditor->setTargetName(targetName);
m_dynamicEditor->prepareBindings();
m_dynamicEditor->updateWindowName();
m_dynamicIndex = index;
});
menu.addAction(tr("Reset Property"), this, [&]() {
propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name());
});
menu.exec(event->globalPos());
}
break;
default:
break;
}
}
void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model)
{
ui->dynamicPropertiesView->setModel(model);
ui->dynamicPropertiesView->verticalHeader()->hide();
ui->dynamicPropertiesView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->dynamicPropertiesView->setItemDelegate(new DynamicPropertiesDelegate);
connect(ui->dynamicPropertiesView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged);
}
void ConnectionViewWidget::setBackendModel(BackendModel *model)
{
ui->backendView->setModel(model);
ui->backendView->verticalHeader()->hide();
ui->backendView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->backendView->setItemDelegate(new BackendDelegate);
model->resetModel();
connect(ui->backendView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ConnectionViewWidget::backendTableViewSelectionChanged);
}
QList<QToolButton *> ConnectionViewWidget::createToolBarWidgets()
{
QList<QToolButton *> buttons;
buttons << new QToolButton();
buttons.constLast()->setIcon(Utils::Icons::PLUS_TOOLBAR.icon());
buttons.constLast()->setToolTip(tr("Add binding or connection."));
connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::addButtonClicked);
connect(this, &ConnectionViewWidget::setEnabledAddButton, buttons.constLast(), &QWidget::setEnabled);
buttons << new QToolButton();
buttons.constLast()->setIcon(Utils::Icons::MINUS_TOOLBAR.icon());
buttons.constLast()->setToolTip(tr("Remove selected binding or connection."));
connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::removeButtonClicked);
connect(this, &ConnectionViewWidget::setEnabledRemoveButton, buttons.constLast(), &QWidget::setEnabled);
QAction *deleteShortcut = new QAction(this);
this->addAction(deleteShortcut);
deleteShortcut->setShortcuts({QKeySequence::Delete, QKeySequence::Backspace});
deleteShortcut->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(deleteShortcut, &QAction::triggered, this, &ConnectionViewWidget::removeButtonClicked);
return buttons;
}
ConnectionViewWidget::TabStatus ConnectionViewWidget::currentTab() const
{
switch (ui->stackedWidget->currentIndex()) {
case 0: return ConnectionTab;
case 1: return BindingTab;
case 2: return DynamicPropertiesTab;
case 3: return BackendTab;
default: return InvalidTab;
}
}
void ConnectionViewWidget::resetItemViews()
{
if (currentTab() == ConnectionTab) {
ui->connectionView->selectionModel()->clear();
} else if (currentTab() == BindingTab) {
ui->bindingView->selectionModel()->clear();
} else if (currentTab() == DynamicPropertiesTab) {
ui->dynamicPropertiesView->selectionModel()->clear();
} else if (currentTab() == BackendTab) {
ui->backendView->selectionModel()->clear();
}
invalidateButtonStatus();
}
void ConnectionViewWidget::invalidateButtonStatus()
{
if (currentTab() == ConnectionTab) {
emit setEnabledRemoveButton(ui->connectionView->selectionModel()->hasSelection());
emit setEnabledAddButton(true);
} else if (currentTab() == BindingTab) {
emit setEnabledRemoveButton(ui->bindingView->selectionModel()->hasSelection());
auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
emit setEnabledAddButton(bindingModel->connectionView()->model()
&& bindingModel->connectionView()->selectedModelNodes().size() == 1);
} else if (currentTab() == DynamicPropertiesTab) {
emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection());
auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
emit setEnabledAddButton(dynamicPropertiesModel->view()->model()
&& dynamicPropertiesModel->selectedNodes().size() == 1);
} else if (currentTab() == BackendTab) {
emit setEnabledAddButton(true);
emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection());
}
}
QTableView *ConnectionViewWidget::connectionTableView() const
{
return ui->connectionView;
}
QTableView *ConnectionViewWidget::bindingTableView() const
{
return ui->bindingView;
}
QTableView *ConnectionViewWidget::dynamicPropertiesTableView() const
{
return ui->dynamicPropertiesView;
}
QTableView *ConnectionViewWidget::backendView() const
{
return ui->backendView;
}
void ConnectionViewWidget::handleTabChanged(int)
{
invalidateButtonStatus();
}
void ConnectionViewWidget::removeButtonClicked()
{
if (currentTab() == ConnectionTab) {
if (ui->connectionView->selectionModel()->selectedRows().isEmpty())
return;
int currentRow = ui->connectionView->selectionModel()->selectedRows().constFirst().row();
auto connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
if (connectionModel) {
connectionModel->deleteConnectionByRow(currentRow);
}
} else if (currentTab() == BindingTab) {
if (ui->bindingView->selectionModel()->selectedRows().isEmpty())
return;
int currentRow = ui->bindingView->selectionModel()->selectedRows().constFirst().row();
auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
if (bindingModel) {
bindingModel->deleteBindindByRow(currentRow);
}
} else if (currentTab() == DynamicPropertiesTab) {
if (ui->dynamicPropertiesView->selectionModel()->selectedRows().isEmpty())
return;
int currentRow = ui->dynamicPropertiesView->selectionModel()->selectedRows().constFirst().row();
auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
if (dynamicPropertiesModel)
dynamicPropertiesModel->deleteDynamicPropertyByRow(currentRow);
} else if (currentTab() == BackendTab) {
int currentRow = ui->backendView->selectionModel()->selectedRows().constFirst().row();
auto backendModel = qobject_cast<BackendModel*>(ui->backendView->model());
if (backendModel)
backendModel->deletePropertyByRow(currentRow);
}
invalidateButtonStatus();
}
void ConnectionViewWidget::addButtonClicked()
{
if (currentTab() == ConnectionTab) {
auto connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
if (connectionModel) {
connectionModel->addConnection();
}
} else if (currentTab() == BindingTab) {
auto bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
if (bindingModel) {
bindingModel->addBindingForCurrentNode();
}
} else if (currentTab() == DynamicPropertiesTab) {
auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
if (dynamicPropertiesModel)
dynamicPropertiesModel->addDynamicPropertyForCurrentNode();
} else if (currentTab() == BackendTab) {
auto backendModel = qobject_cast<BackendModel*>(ui->backendView->model());
if (backendModel)
backendModel->addNewBackend();
}
invalidateButtonStatus();
}
void ConnectionViewWidget::editorForConnection()
{
QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::accepted,
[&]() {
if (m_connectionEditor->hasModelIndex()) {
ConnectionModel *connectionModel = qobject_cast<ConnectionModel *>(ui->connectionView->model());
if (connectionModel->connectionView()->isWidgetEnabled()
&& (connectionModel->rowCount() > m_connectionEditor->modelIndex().row())) {
connectionModel->connectionView()
->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() {
SignalHandlerProperty signalHandler
= connectionModel->signalHandlerPropertyForRow(
m_connectionEditor->modelIndex().row());
signalHandler.setSource(m_connectionEditor->connectionValue());
});
}
m_connectionEditor->resetModelIndex();
}
m_connectionEditor->hideWidget();
});
QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::rejected,
[&]() {
m_connectionEditor->resetModelIndex();
m_connectionEditor->hideWidget();
});
}
void ConnectionViewWidget::editorForBinding()
{
QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::accepted,
[&]() {
BindingModel *bindingModel = qobject_cast<BindingModel *>(bindingTableView()->model());
QString newValue = m_bindingEditor->bindingValue().trimmed();
if (m_bindingIndex.isValid()) {
if (bindingModel->connectionView()->isWidgetEnabled()
&& (bindingModel->rowCount() > m_bindingIndex.row())) {
bindingModel->connectionView()->executeInTransaction(
"ConnectionView::setBindingProperty", [this, bindingModel, newValue]() {
BindingProperty property = bindingModel->bindingPropertyForRow(
m_bindingIndex.row());
if (property.isValid()) {
if (property.isBindingProperty()) {
if (property.isDynamic()) {
property
.setDynamicTypeNameAndExpression(property.dynamicTypeName(),
newValue);
} else {
property.setExpression(newValue);
}
}
}
});
}
}
m_bindingIndex = QModelIndex();
m_bindingEditor->hideWidget();
});
QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::rejected,
[&]() {
m_bindingIndex = QModelIndex(); //invalidating index
m_bindingEditor->hideWidget();
});
}
void ConnectionViewWidget::editorForDynamic()
{
QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::accepted,
[&]() {
DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(dynamicPropertiesTableView()->model());
QString newValue = m_dynamicEditor->bindingValue().trimmed();
if (m_dynamicIndex.isValid()) {
if (qobject_cast<ConnectionView *>(propertiesModel->view())->isWidgetEnabled()
&& (propertiesModel->rowCount() > m_dynamicIndex.row())) {
propertiesModel->view()->executeInTransaction(
"ConnectionView::setBinding", [this, propertiesModel, newValue]() {
AbstractProperty abProp = propertiesModel->abstractPropertyForRow(
m_dynamicIndex.row());
if (abProp.isValid()) {
if (abProp.isBindingProperty()) {
BindingProperty property = abProp.toBindingProperty();
property.setDynamicTypeNameAndExpression(property.dynamicTypeName(),
newValue);
}
//if it's a variant property, then we remove it and replace with binding
else if (abProp.isVariantProperty()) {
VariantProperty property = abProp.toVariantProperty();
PropertyName name = property.name();
TypeName type = property.dynamicTypeName();
BindingProperty newProperty = propertiesModel
->replaceVariantWithBinding(name);
if (newProperty.isValid()) {
newProperty.setDynamicTypeNameAndExpression(type, newValue);
}
}
}
});
}
}
m_dynamicIndex = QModelIndex();
m_dynamicEditor->hideWidget();
});
QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::rejected,
[&]() {
m_dynamicIndex = QModelIndex(); //invalidating index
m_dynamicEditor->hideWidget();
});
}
void ConnectionViewWidget::bindingTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{
if (currentTab() == BindingTab) {
if (current.isValid()) {
emit setEnabledRemoveButton(true);
} else {
emit setEnabledRemoveButton(false);
}
}
}
void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{
if (currentTab() == ConnectionTab) {
if (current.isValid()) {
emit setEnabledRemoveButton(true);
} else {
emit setEnabledRemoveButton(false);
}
}
}
void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{
if (currentTab() == DynamicPropertiesTab) {
if (current.isValid()) {
emit setEnabledRemoveButton(true);
} else {
emit setEnabledRemoveButton(false);
}
}
}
void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex &current, const QModelIndex & /*revious*/)
{
if (currentTab() == BackendTab) {
if (current.isValid()) {
emit setEnabledRemoveButton(true);
} else {
emit setEnabledRemoveButton(false);
}
}
}
} // namespace QmlDesigner

View File

@@ -1,94 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QFrame>
#include <QAbstractItemView>
QT_BEGIN_NAMESPACE
class QShortcut;
class QToolButton;
class QTableView;
class QListView;
QT_END_NAMESPACE
namespace QmlDesigner {
namespace Ui { class ConnectionViewWidget; }
class ActionEditor;
class BindingEditor;
class BindingModel;
class ConnectionModel;
class DynamicPropertiesModel;
class BackendModel;
class ConnectionViewWidget : public QFrame
{
Q_OBJECT
public:
enum TabStatus {
ConnectionTab,
BindingTab,
DynamicPropertiesTab,
BackendTab,
InvalidTab
};
explicit ConnectionViewWidget(QWidget *parent = nullptr);
~ConnectionViewWidget() override;
void setBindingModel(BindingModel *model);
void setConnectionModel(ConnectionModel *model);
void setDynamicPropertiesModel(DynamicPropertiesModel *model);
void setBackendModel(BackendModel *model);
QList<QToolButton*> createToolBarWidgets();
TabStatus currentTab() const;
void resetItemViews();
void invalidateButtonStatus();
QTableView *connectionTableView() const;
QTableView *bindingTableView() const;
QTableView *dynamicPropertiesTableView() const;
QTableView *backendView() const;
void bindingTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void connectionTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void dynamicPropertiesTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void backendTableViewSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
signals:
void setEnabledAddButton(bool enabled);
void setEnabledRemoveButton(bool enabled);
protected:
void contextMenuEvent(QContextMenuEvent *event) override;
private:
void handleTabChanged(int i);
void removeButtonClicked();
void addButtonClicked();
//methods to prepare editors
void editorForConnection();
void editorForBinding();
void editorForDynamic();
private:
Ui::ConnectionViewWidget *ui;
QmlDesigner::ActionEditor *m_connectionEditor; //editor for connections in connection view
QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view
QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view
QModelIndex m_bindingIndex;
QModelIndex m_dynamicIndex;
};
} // namespace QmlDesigner

View File

@@ -1,265 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::ConnectionViewWidget</class>
<widget class="QWidget" name="QmlDesigner::ConnectionViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>994</width>
<height>611</height>
</rect>
</property>
<property name="windowTitle">
<string>Connections</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTabBar" name="tabBar" native="true"/>
</item>
<item row="1" column="0">
<widget class="QWidget" name="widgetSpacer" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>2</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>2</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>3</number>
</property>
<widget class="QWidget" name="connectionViewPage">
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="3" column="0" colspan="5">
<widget class="QTableView" name="connectionView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="bindingViewPage">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="0" colspan="3">
<widget class="QTableView" name="bindingView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="dynamicPropertiesPage">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTableView" name="dynamicPropertiesView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="backendViewPage">
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTableView" name="backendView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0">
<widget class="QToolBar" name="toolBar"/>
</item>
</layout>
<zorder>stackedWidget</zorder>
<zorder>tabBar</zorder>
<zorder>widgetSpacer</zorder>
<zorder>toolBar</zorder>
</widget>
<customwidgets>
<customwidget>
<class>QTabBar</class>
<extends>QWidget</extends>
<header location="global">qtabbar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -1,409 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "delegates.h"
#include "backendmodel.h"
#include "connectionmodel.h"
#include "bindingmodel.h"
#include "dynamicpropertiesmodel.h"
#include "connectionview.h"
#include "nodemetainfo.h"
#include <bindingproperty.h>
#include <utils/qtcassert.h>
#include <QStyleFactory>
#include <QItemEditorFactory>
#include <QDebug>
namespace QmlDesigner {
QStringList prependOnForSignalHandler(const QStringList &signalNames)
{
QStringList signalHandlerNames;
for (const QString &signalName : signalNames) {
QString signalHandlerName = signalName;
if (!signalHandlerName.isEmpty()) {
QChar firstChar = signalHandlerName.at(0).toUpper();
signalHandlerName[0] = firstChar;
signalHandlerName.prepend(QLatin1String("on"));
signalHandlerNames.append(signalHandlerName);
}
}
return signalHandlerNames;
}
PropertiesComboBox::PropertiesComboBox(QWidget *parent) : QComboBox(parent)
{
setEditable(true);
setValidator(new QRegularExpressionValidator(QRegularExpression(QLatin1String("[a-z|A-Z|0-9|._-]*")), this));
}
QString PropertiesComboBox::text() const
{
return currentText();
}
void PropertiesComboBox::setText(const QString &text)
{
setEditText(text);
}
void PropertiesComboBox::disableValidator()
{
setValidator(nullptr);
}
ConnectionComboBox::ConnectionComboBox(QWidget *parent) : PropertiesComboBox(parent)
{
}
QString ConnectionComboBox::text() const
{
int index = findText(currentText());
if (index > -1) {
QVariant variantData = itemData(index);
if (variantData.isValid())
return variantData.toString();
}
return currentText();
}
ConnectionEditorDelegate::ConnectionEditorDelegate(QWidget *parent)
: QStyledItemDelegate(parent)
{
}
void ConnectionEditorDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
opt.state &= ~QStyle::State_HasFocus;
QStyledItemDelegate::paint(painter, opt, index);
}
BindingDelegate::BindingDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
{
static QItemEditorFactory *factory = nullptr;
if (factory == nullptr) {
factory = new QItemEditorFactory;
QItemEditorCreatorBase *creator
= new QItemEditorCreator<PropertiesComboBox>("text");
factory->registerEditor(QVariant::String, creator);
}
setItemEditorFactory(factory);
}
QWidget *BindingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
const auto model = qobject_cast<const BindingModel*>(index.model());
if (!model) {
qWarning() << "BindingDelegate::createEditor no model";
return widget;
}
if (!model->connectionView()) {
qWarning() << "BindingDelegate::createEditor no connection view";
return widget;
}
model->connectionView()->allModelNodes();
auto bindingComboBox = qobject_cast<PropertiesComboBox*>(widget);
if (!bindingComboBox) {
qWarning() << "BindingDelegate::createEditor no bindingComboBox";
return widget;
}
BindingProperty bindingProperty = model->bindingPropertyForRow(index.row());
switch (index.column()) {
case BindingModel::TargetModelNodeRow:
return nullptr; //no editor
case BindingModel::TargetPropertyNameRow: {
bindingComboBox->addItems(model->possibleTargetProperties(bindingProperty));
} break;
case BindingModel::SourceModelNodeRow: {
//common items
for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) {
if (!modelNode.id().isEmpty()) {
bindingComboBox->addItem(modelNode.id());
}
}
//singletons:
if (RewriterView* rv = model->connectionView()->rewriterView()) {
for (const QmlTypeData &data : rv->getQMLTypes()) {
if (!data.typeName.isEmpty()) {
bindingComboBox->addItem(data.typeName);
}
}
}
//parent:
if (!bindingProperty.parentModelNode().isRootNode())
bindingComboBox->addItem(QLatin1String("parent"));
} break;
case BindingModel::SourcePropertyNameRow: {
bindingComboBox->addItems(model->possibleSourceProperties(bindingProperty));
bindingComboBox->disableValidator();
} break;
default: qWarning() << "BindingDelegate::createEditor column" << index.column();
}
connect(bindingComboBox, &QComboBox::activated, this, [=] {
auto delegate = const_cast<BindingDelegate*>(this);
emit delegate->commitData(bindingComboBox);
});
return widget;
}
DynamicPropertiesDelegate::DynamicPropertiesDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
{
// static QItemEditorFactory *factory = 0;
// if (factory == 0) {
// factory = new QItemEditorFactory;
// QItemEditorCreatorBase *creator
// = new QItemEditorCreator<DynamicPropertiesComboBox>("text");
// factory->registerEditor(QVariant::String, creator);
// }
// setItemEditorFactory(factory);
}
QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
const auto model = qobject_cast<const DynamicPropertiesModel*>(index.model());
if (!model) {
qWarning() << "BindingDelegate::createEditor no model";
return widget;
}
if (!model->view()) {
qWarning() << "BindingDelegate::createEditor no connection view";
return widget;
}
model->view()->allModelNodes();
switch (index.column()) {
case DynamicPropertiesModel::TargetModelNodeRow: {
return nullptr; //no editor
};
case DynamicPropertiesModel::PropertyNameRow: {
return QStyledItemDelegate::createEditor(parent, option, index);
};
case DynamicPropertiesModel::PropertyTypeRow: {
auto dynamicPropertiesComboBox = new PropertiesComboBox(parent);
connect(dynamicPropertiesComboBox, &QComboBox::activated, this, [=] {
auto delegate = const_cast<DynamicPropertiesDelegate*>(this);
emit delegate->commitData(dynamicPropertiesComboBox);
});
dynamicPropertiesComboBox->addItem(QLatin1String("alias"));
dynamicPropertiesComboBox->addItem(QLatin1String("Item"));
dynamicPropertiesComboBox->addItem(QLatin1String("real"));
dynamicPropertiesComboBox->addItem(QLatin1String("int"));
dynamicPropertiesComboBox->addItem(QLatin1String("string"));
dynamicPropertiesComboBox->addItem(QLatin1String("bool"));
dynamicPropertiesComboBox->addItem(QLatin1String("url"));
dynamicPropertiesComboBox->addItem(QLatin1String("color"));
dynamicPropertiesComboBox->addItem(QLatin1String("variant"));
dynamicPropertiesComboBox->addItem(QLatin1String("TextureInput"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector2d"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector3d"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector4d"));
return dynamicPropertiesComboBox;
};
case DynamicPropertiesModel::PropertyValueRow: {
return QStyledItemDelegate::createEditor(parent, option, index);
};
default: qWarning() << "BindingDelegate::createEditor column" << index.column();
}
return nullptr;
}
ConnectionDelegate::ConnectionDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
{
static QItemEditorFactory *factory = nullptr;
if (factory == nullptr) {
factory = new QItemEditorFactory;
QItemEditorCreatorBase *creator
= new QItemEditorCreator<ConnectionComboBox>("text");
factory->registerEditor(QVariant::String, creator);
}
setItemEditorFactory(factory);
}
static QString nameForAction(const QString &input)
{
QStringList list = input.split('.');
return list.first();
}
QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
const auto connectionModel = qobject_cast<const ConnectionModel*>(index.model());
auto connectionComboBox = qobject_cast<ConnectionComboBox*>(widget);
if (!connectionModel) {
qWarning() << "ConnectionDelegate::createEditor no model";
return widget;
}
if (!connectionModel->connectionView()) {
qWarning() << "ConnectionDelegate::createEditor no connection view";
return widget;
}
if (!connectionComboBox) {
qWarning() << "ConnectionDelegate::createEditor no bindingComboBox";
return widget;
}
switch (index.column()) {
case ConnectionModel::TargetModelNodeRow: {
auto addMetaInfoProperties = [&](const NodeMetaInfo& itemMetaInfo, QString itemName){
if (itemMetaInfo.isValid()) {
for (const auto &property : itemMetaInfo.properties()) {
NodeMetaInfo propertyType = property.propertyType();
if (propertyType.isValid() && propertyType.isFileComponent()) {
if (!property.isEnumType() && !property.isPrivate()
&& !property.isListProperty() && !property.isPointer()) {
if (propertyType.isQtObject()) {
connectionComboBox->addItem(itemName + "." + property.name());
}
}
}
}
}
};
for (const ModelNode &modelNode : connectionModel->connectionView()->allModelNodes()) {
if (!modelNode.id().isEmpty()) {
connectionComboBox->addItem(modelNode.id());
for (const BindingProperty &property : modelNode.bindingProperties()) {
if (property.isValid()) {
if (property.isAlias()) {
connectionComboBox->addItem(modelNode.id()
+ "."
+ QString::fromUtf8(property.name()));
}
}
}
//Components
if (modelNode.isComponent()) {
NodeMetaInfo componentMetaInfo = modelNode.metaInfo();
addMetaInfoProperties(componentMetaInfo, modelNode.id());
}
}
}
//singletons:
if (RewriterView* rv = connectionModel->connectionView()->rewriterView()) {
for (const QmlTypeData &data : rv->getQMLTypes()) {
if (!data.typeName.isEmpty()) {
//singleton itself
connectionComboBox->addItem(data.typeName);
//its properties, mostly looking for aliases:
NodeMetaInfo metaInfo = connectionModel->connectionView()->model()->metaInfo(data.typeName.toUtf8());
addMetaInfoProperties(metaInfo, data.typeName);
}
}
}
} break;
case ConnectionModel::TargetPropertyNameRow: {
connectionComboBox->addItems(prependOnForSignalHandler(connectionModel->getSignalsForRow(index.row())));
} break;
case ConnectionModel::SourceRow: {
ModelNode rootModelNode = connectionModel->connectionView()->rootModelNode();
if (QmlItemNode::isValidQmlItemNode(rootModelNode) && !rootModelNode.id().isEmpty()) {
QString itemText = tr("Change to default state");
QString source = QString::fromLatin1("%1.state = \"\"").arg(rootModelNode.id());
connectionComboBox->addItem(itemText, source);
connectionComboBox->disableValidator();
for (const QmlModelState &state : QmlItemNode(rootModelNode).states().allStates()) {
QString itemText = tr("Change state to %1").arg(state.name());
QString source = QString::fromLatin1("%1.state = \"%2\"")
.arg(rootModelNode.id())
.arg(state.name());
connectionComboBox->addItem(itemText, source);
}
QStringList trigger = connectionModel->getflowActionTriggerForRow(index.row());
for (const QString &action : trigger) {
connectionComboBox->addItem(tr("Activate FlowAction %1").arg(nameForAction(action)), action);
}
}
connectionComboBox->disableValidator();
} break;
default: qWarning() << "ConnectionDelegate::createEditor column" << index.column();
}
connect(connectionComboBox, &QComboBox::activated, this, [=] {
auto delegate = const_cast<ConnectionDelegate*>(this);
emit delegate->commitData(connectionComboBox);
});
return widget;
}
BackendDelegate::BackendDelegate(QWidget *parent) : ConnectionEditorDelegate(parent)
{
}
QWidget *BackendDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const auto model = qobject_cast<const BackendModel*>(index.model());
model->connectionView()->allModelNodes();
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
QTC_ASSERT(model, return widget);
QTC_ASSERT(model->connectionView(), return widget);
switch (index.column()) {
case BackendModel::TypeNameColumn: {
auto backendComboBox = new PropertiesComboBox(parent);
backendComboBox->addItems(model->possibleCppTypes());
connect(backendComboBox, &QComboBox::activated, this, [=] {
auto delegate = const_cast<BackendDelegate*>(this);
emit delegate->commitData(backendComboBox);
});
return backendComboBox;
};
case BackendModel::PropertyNameColumn: {
return widget;
};
case BackendModel::IsSingletonColumn: {
return nullptr; //no editor
};
case BackendModel::IsLocalColumn: {
return nullptr; //no editor
};
default: qWarning() << "BackendDelegate::createEditor column" << index.column();
}
return widget;
}
} // namespace QmlDesigner

View File

@@ -1,78 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QStandardItem>
#include <QStyledItemDelegate>
#include <QComboBox>
namespace QmlDesigner {
class PropertiesComboBox : public QComboBox
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText USER true)
public:
PropertiesComboBox(QWidget *parent = nullptr);
virtual QString text() const;
void setText(const QString &text);
void disableValidator();
};
class ConnectionComboBox : public PropertiesComboBox
{
Q_OBJECT
public:
ConnectionComboBox(QWidget *parent = nullptr);
QString text() const override;
};
class ConnectionEditorDelegate : public QStyledItemDelegate
{
public:
ConnectionEditorDelegate(QWidget *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
class BindingDelegate : public ConnectionEditorDelegate
{
public:
BindingDelegate(QWidget *parent = nullptr);
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
class DynamicPropertiesDelegate : public ConnectionEditorDelegate
{
public:
DynamicPropertiesDelegate(QWidget *parent = nullptr);
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
class ConnectionDelegate : public ConnectionEditorDelegate
{
Q_OBJECT
public:
ConnectionDelegate(QWidget *parent = nullptr);
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
class BackendDelegate : public ConnectionEditorDelegate
{
Q_OBJECT
public:
BackendDelegate(QWidget *parent = nullptr);
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,74 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dynamicpropertiesitem.h"
#include "connectioneditorutils.h"
#include <abstractproperty.h>
#include <abstractview.h>
#include <bindingproperty.h>
#include <modelnode.h>
#include <variantproperty.h>
#include <qmldesignertr.h>
#include <qmlobjectnode.h>
namespace QmlDesigner {
QHash<int, QByteArray> DynamicPropertiesItem::roleNames()
{
return {{TargetNameRole, "target"},
{PropertyNameRole, "name"},
{PropertyTypeRole, "type"},
{PropertyValueRole, "value"}};
}
QStringList DynamicPropertiesItem::headerLabels()
{
return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Property Type"), Tr::tr("Property Value")};
}
DynamicPropertiesItem::DynamicPropertiesItem(const AbstractProperty &property)
: QStandardItem(idOrTypeName(property.parentModelNode()))
{
updateProperty(property);
}
int DynamicPropertiesItem::internalId() const
{
return data(InternalIdRole).toInt();
}
PropertyName DynamicPropertiesItem::propertyName() const
{
return data(PropertyNameRole).toString().toUtf8();
}
std::optional<const QmlObjectNode> parentIfNotDefaultState(const AbstractProperty &property)
{
const QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode());
if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState())
return objectNode;
return std::nullopt;
}
void DynamicPropertiesItem::updateProperty(const AbstractProperty &property)
{
setData(property.parentModelNode().internalId(), InternalIdRole);
setData(idOrTypeName(property.parentModelNode()), TargetNameRole);
setData(property.name(), PropertyNameRole);
setData(property.dynamicTypeName(), PropertyTypeRole);
if (property.isVariantProperty()) {
if (std::optional<const QmlObjectNode> nodeInState = parentIfNotDefaultState(property))
setData(nodeInState->modelValue(property.name()), PropertyValueRole);
else
setData(property.toVariantProperty().value(), PropertyValueRole);
} else if (property.isBindingProperty()) {
if (std::optional<const QmlObjectNode> nodeInState = parentIfNotDefaultState(property))
setData(nodeInState->expression(property.name()), PropertyValueRole);
else
setData(property.toBindingProperty().expression(), PropertyValueRole);
}
}
} // End namespace QmlDesigner.

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <modelfwd.h>
#include <QStandardItem>
namespace QmlDesigner {
class AbstractProperty;
class DynamicPropertiesItem : public QStandardItem
{
public:
enum UserRoles {
InternalIdRole = Qt::UserRole + 2,
TargetNameRole,
PropertyNameRole,
PropertyTypeRole,
PropertyValueRole
};
static QHash<int, QByteArray> roleNames();
static QStringList headerLabels();
DynamicPropertiesItem(const AbstractProperty &property);
int internalId() const;
PropertyName propertyName() const;
void updateProperty(const AbstractProperty &property);
};
} // End namespace QmlDesigner.

View File

@@ -3,19 +3,17 @@
#pragma once #pragma once
#include <nodeinstanceglobal.h> #include "dynamicpropertiesitem.h"
#include <studioquickwidget.h>
#include "nodeinstanceglobal.h"
#include "studioquickwidget.h"
#include <QStandardItemModel> #include <QStandardItemModel>
namespace QmlDesigner { namespace QmlDesigner {
class AbstractProperty;
class AbstractView; class AbstractView;
class BindingProperty; class AbstractProperty;
class ModelNode; class ModelNode;
class VariantProperty;
class DynamicPropertiesModelBackendDelegate; class DynamicPropertiesModelBackendDelegate;
@@ -23,112 +21,67 @@ class DynamicPropertiesModel : public QStandardItemModel
{ {
Q_OBJECT Q_OBJECT
signals:
void currentIndexChanged();
public: public:
enum ColumnRoles {
TargetModelNodeRow = 0,
PropertyNameRow = 1,
PropertyTypeRow = 2,
PropertyValueRow = 3
};
enum UserRoles {
InternalIdRole = Qt::UserRole + 2,
TargetNameRole,
PropertyNameRole,
PropertyTypeRole,
PropertyValueRole
};
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT) Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT)
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
void bindingPropertyChanged(const BindingProperty &bindingProperty); AbstractView *view() const;
void abstractPropertyChanged(const AbstractProperty &bindingProperty); DynamicPropertiesModelBackendDelegate *delegate() const;
void variantPropertyChanged(const VariantProperty &variantProperty);
void bindingRemoved(const BindingProperty &bindingProperty);
void variantRemoved(const VariantProperty &variantProperty);
void reset();
void setSelectedNode(const ModelNode &node);
const QList<ModelNode> selectedNodes() const;
const ModelNode singleSelectedNode() const;
AbstractView *view() const { return m_view; } int currentIndex() const;
AbstractProperty abstractPropertyForRow(int rowNumber) const; AbstractProperty currentProperty() const;
BindingProperty bindingPropertyForRow(int rowNumber) const; AbstractProperty propertyForRow(int row) const;
VariantProperty variantPropertyForRow(int rowNumber) const;
QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
void deleteDynamicPropertyByRow(int rowNumber);
void updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant);
void addDynamicPropertyForCurrentNode();
void resetModel();
BindingProperty replaceVariantWithBinding(const PropertyName &name, bool copyValue = false);
void resetProperty(const PropertyName &name);
void dispatchPropertyChanges(const AbstractProperty &abstractProperty);
PropertyName unusedProperty(const ModelNode &modelNode);
static bool isValueType(const TypeName &type);
static QVariant defaultValueForType(const TypeName &type);
static QString defaultExpressionForType(const TypeName &type);
Q_INVOKABLE void add(); Q_INVOKABLE void add();
Q_INVOKABLE void remove(int row); Q_INVOKABLE void remove(int row);
int currentIndex() const; void reset(const QList<ModelNode> &modelNodes = {});
void setCurrentIndex(int i); void setCurrentIndex(int i);
void setCurrentProperty(const AbstractProperty &property);
void setCurrent(int internalId, const PropertyName &name);
int findRowForProperty(const AbstractProperty &abstractProperty) const; void updateItem(const AbstractProperty &property);
void removeItem(const AbstractProperty &property);
signals: void commitPropertyType(int row, const TypeName &type);
void currentIndexChanged(); void commitPropertyName(int row, const PropertyName &name);
void commitPropertyValue(int row, const QVariant &value);
void dispatchPropertyChanges(const AbstractProperty &abstractProperty);
protected: protected:
void addProperty(const QVariant &propertyValue,
const QString &propertyType,
const AbstractProperty &abstractProperty);
void addBindingProperty(const BindingProperty &property);
void addVariantProperty(const VariantProperty &property);
void updateBindingProperty(int rowNumber);
void updateVariantProperty(int rowNumber);
void addModelNode(const ModelNode &modelNode);
void updateValue(int row);
void updatePropertyName(int rowNumber);
void updatePropertyType(int rowNumber);
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
void updateCustomData(QStandardItem *item, const AbstractProperty &property);
void updateCustomData(int row, const AbstractProperty &property);
int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
int findRowForVariantProperty(const VariantProperty &variantProperty) const;
bool getExpressionStrings(const BindingProperty &bindingProperty,
QString *sourceNode,
QString *sourceProperty);
void updateDisplayRole(int row, int columns, const QString &string);
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
DynamicPropertiesModelBackendDelegate *delegate() const; private:
std::optional<int> findRow(int nodeId, const PropertyName &name) const;
DynamicPropertiesItem *itemForRow(int row) const;
DynamicPropertiesItem *itemForProperty(const AbstractProperty &property) const;
ModelNode modelNodeForItem(DynamicPropertiesItem *item);
void addModelNode(const ModelNode &node);
void addProperty(const AbstractProperty &property);
public:
// TODO: Remove. This is a model for properties. Not nodes.
// Use reset with a list of nodes instead if all properties
// from a set of given nodes should be added.
const QList<ModelNode> selectedNodes() const;
void setSelectedNode(const ModelNode &node);
const ModelNode singleSelectedNode() const;
private: private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void handleException();
AbstractView *m_view = nullptr; AbstractView *m_view = nullptr;
bool m_lock = false;
bool m_handleDataChanged = false;
QString m_exceptionError;
QList<ModelNode> m_selectedNodes;
bool m_explicitSelection = false;
int m_currentIndex = 0;
DynamicPropertiesModelBackendDelegate *m_delegate = nullptr; DynamicPropertiesModelBackendDelegate *m_delegate = nullptr;
int m_currentIndex = -1;
// TODO: Remove.
QList<ModelNode> m_selectedNodes = {};
bool m_explicitSelection = false;
}; };
class DynamicPropertiesModelBackendDelegate : public QObject class DynamicPropertiesModelBackendDelegate : public QObject
@@ -137,40 +90,33 @@ class DynamicPropertiesModelBackendDelegate : public QObject
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT)
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT) Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT)
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
//Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
public: public:
DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr); DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr);
void update(const AbstractProperty &property);
signals: signals:
void currentRowChanged();
void nameChanged(); void nameChanged();
void valueChanged(); void valueChanged();
void targetNodeChanged(); void targetNodeChanged();
private: private:
int currentRow() const;
void setCurrentRow(int i);
void handleTypeChanged(); void handleTypeChanged();
void handleNameChanged(); void handleNameChanged();
void handleValueChanged(); void handleValueChanged();
void handleException();
QVariant variantValue() const;
QString targetNode() const;
StudioQmlComboBoxBackend *type(); StudioQmlComboBoxBackend *type();
StudioQmlTextBackend *name(); StudioQmlTextBackend *name();
StudioQmlTextBackend *value(); StudioQmlTextBackend *value();
QString targetNode() const;
std::optional<int> m_internalNodeId;
StudioQmlComboBoxBackend m_type; StudioQmlComboBoxBackend m_type;
StudioQmlTextBackend m_name; StudioQmlTextBackend m_name;
StudioQmlTextBackend m_value; StudioQmlTextBackend m_value;
int m_currentRow = -1;
QString m_exceptionError;
QString m_targetNode; QString m_targetNode;
}; };

View File

@@ -834,7 +834,7 @@ void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &
ModelNode node(property.parentModelNode()); ModelNode node(property.parentModelNode());
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
if (property.isDynamic()) if (property.isDynamic())
m_dynamicPropertiesModel->variantPropertyChanged(property); m_dynamicPropertiesModel->updateItem(property);
if (m_selectedMaterial.property(property.name()).isBindingProperty()) if (m_selectedMaterial.property(property.name()).isBindingProperty())
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
else else
@@ -869,7 +869,7 @@ void MaterialEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
if (property.isDynamic()) if (property.isDynamic())
m_dynamicPropertiesModel->bindingPropertyChanged(property); m_dynamicPropertiesModel->updateItem(property);
if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty()) if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty())
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
else else
@@ -897,12 +897,8 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node,
void MaterialEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) void MaterialEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList)
if (property.isBindingProperty()) m_dynamicPropertiesModel->removeItem(property);
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
else if (property.isVariantProperty())
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
}
} }
// request render image for the selected material node // request render image for the selected material node

View File

@@ -27,6 +27,7 @@
#include "bindingproperty.h" #include "bindingproperty.h"
#include "propertyeditorvalue.h" #include "propertyeditorvalue.h"
#include "connectioneditorutils.h"
#include <dynamicpropertiesmodel.h> #include <dynamicpropertiesmodel.h>
@@ -96,7 +97,7 @@ QHash<int, QByteArray> DynamicPropertiesProxyModel::roleNames() const
QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const
{ {
if (index.isValid() && index.row() < rowCount()) { if (index.isValid() && index.row() < rowCount()) {
AbstractProperty property = m_model->abstractPropertyForRow(index.row()); AbstractProperty property = m_model->propertyForRow(index.row());
QTC_ASSERT(property.isValid(), return QVariant()); QTC_ASSERT(property.isValid(), return QVariant());
@@ -142,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const
{ {
DynamicPropertiesModel *propsModel = dynamicPropertiesModel(); DynamicPropertiesModel *propsModel = dynamicPropertiesModel();
return QString::fromUtf8(propsModel->unusedProperty(propsModel->singleSelectedNode())); return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode()));
} }
void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type) void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type)
@@ -162,12 +163,12 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr
return; return;
} }
try { try {
if (DynamicPropertiesModel::isValueType(typeName)) { if (isDynamicVariantPropertyType(typeName)) {
QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); QVariant value = defaultValueForType(typeName);
VariantProperty variantProp = modelNode.variantProperty(name.toUtf8()); VariantProperty variantProp = modelNode.variantProperty(name.toUtf8());
variantProp.setDynamicTypeNameAndValue(typeName, value); variantProp.setDynamicTypeNameAndValue(typeName, value);
} else { } else {
QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); QString expression = defaultExpressionForType(typeName);
BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8()); BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8());
bindingProp.setDynamicTypeNameAndExpression(typeName, expression); bindingProp.setDynamicTypeNameAndExpression(typeName, expression);
@@ -261,7 +262,7 @@ PropertyEditorValue *DynamicPropertyRow::backendValue() const
void DynamicPropertyRow::remove() void DynamicPropertyRow::remove()
{ {
m_model->dynamicPropertiesModel()->deleteDynamicPropertyByRow(m_row); m_model->dynamicPropertiesModel()->remove(m_row);
} }
PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue() PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue()
@@ -283,7 +284,7 @@ void DynamicPropertyRow::setupBackendValue()
if (!m_model) if (!m_model)
return; return;
AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row); AbstractProperty property = m_model->dynamicPropertiesModel()->propertyForRow(m_row);
if (!property.isValid()) if (!property.isValid())
return; return;
@@ -324,9 +325,9 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
return; return;
auto propertiesModel = m_model->dynamicPropertiesModel(); auto propertiesModel = m_model->dynamicPropertiesModel();
VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row); AbstractProperty property = propertiesModel->propertyForRow(m_row);
if (!DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName())) if (!isDynamicVariantPropertyType(property.dynamicTypeName()))
return; return;
m_lock = true; m_lock = true;
@@ -335,6 +336,10 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
auto view = propertiesModel->view(); auto view = propertiesModel->view();
RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__); RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__);
try { try {
if (property.isBindingProperty()) {
convertBindingToVariantProperty(property.toBindingProperty(), value);
} else if (property.isVariantProperty()) {
VariantProperty variantProperty = property.toVariantProperty();
QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); QmlObjectNode objectNode = variantProperty.parentQmlObjectNode();
if (view->currentState().isBaseState() if (view->currentState().isBaseState()
&& !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) { && !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) {
@@ -346,6 +351,7 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
if (objectNode.isValid() && objectNode.modelValue(name) != value) if (objectNode.isValid() && objectNode.modelValue(name) != value)
objectNode.setVariantProperty(name, value); objectNode.setVariantProperty(name, value);
} }
}
transaction.commit(); // committing in the try block transaction.commit(); // committing in the try block
} catch (Exception &e) { } catch (Exception &e) {
e.showException(); e.showException();
@@ -358,7 +364,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression)
return; return;
auto propertiesModel = m_model->dynamicPropertiesModel(); auto propertiesModel = m_model->dynamicPropertiesModel();
AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); AbstractProperty property = propertiesModel->propertyForRow(m_row);
BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name()); BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name());
@@ -413,15 +419,15 @@ void DynamicPropertyRow::resetValue()
auto propertiesModel = m_model->dynamicPropertiesModel(); auto propertiesModel = m_model->dynamicPropertiesModel();
auto view = propertiesModel->view(); auto view = propertiesModel->view();
AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); AbstractProperty property = propertiesModel->propertyForRow(m_row);
TypeName typeName = property.dynamicTypeName(); TypeName typeName = property.dynamicTypeName();
if (view->currentState().isBaseState()) { if (view->currentState().isBaseState()) {
if (DynamicPropertiesModel::isValueType(typeName)) { if (isDynamicVariantPropertyType(typeName)) {
QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); QVariant value = defaultValueForType(typeName);
commitValue(value); commitValue(value);
} else { } else {
QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); QString expression = defaultExpressionForType(typeName);
commitExpression(expression); commitExpression(expression);
} }
} else { } else {

View File

@@ -588,7 +588,7 @@ void TextureEditorView::variantPropertiesChanged(const QList<VariantProperty> &p
ModelNode node(property.parentModelNode()); ModelNode node(property.parentModelNode());
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
if (property.isDynamic()) if (property.isDynamic())
m_dynamicPropertiesModel->variantPropertyChanged(property); m_dynamicPropertiesModel->updateItem(property);
if (m_selectedTexture.property(property.name()).isBindingProperty()) if (m_selectedTexture.property(property.name()).isBindingProperty())
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
else else
@@ -612,7 +612,7 @@ void TextureEditorView::bindingPropertiesChanged(const QList<BindingProperty> &p
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
if (property.isDynamic()) if (property.isDynamic())
m_dynamicPropertiesModel->bindingPropertyChanged(property); m_dynamicPropertiesModel->updateItem(property);
if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty()) if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty())
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
else else
@@ -642,12 +642,8 @@ void TextureEditorView::auxiliaryDataChanged(const ModelNode &node,
void TextureEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) void TextureEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList)
if (property.isBindingProperty()) m_dynamicPropertiesModel->removeItem(property);
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
else if (property.isVariantProperty())
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
}
} }
void TextureEditorView::nodeReparented(const ModelNode &node, void TextureEditorView::nodeReparented(const ModelNode &node,