forked from qt-creator/qt-creator
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:
committed by
Thomas Hartmann
parent
e0c129d4b1
commit
759b560bab
@@ -33,7 +33,6 @@ ListView {
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
root.currentIndex = root.model.currentIndex
|
||||
dialog.backend.currentRow = root.currentIndex
|
||||
}
|
||||
|
||||
// Number of columns
|
||||
@@ -79,7 +78,6 @@ ListView {
|
||||
onClicked: {
|
||||
root.model.currentIndex = itemDelegate.index
|
||||
root.currentIndex = itemDelegate.index
|
||||
dialog.backend.currentRow = itemDelegate.index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,6 @@ ListView {
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
root.currentIndex = root.model.currentIndex
|
||||
dialog.backend.currentRow = root.currentIndex
|
||||
}
|
||||
|
||||
// Number of columns
|
||||
@@ -81,7 +80,6 @@ ListView {
|
||||
function onClicked() {
|
||||
root.model.currentIndex = itemDelegate.index
|
||||
root.currentIndex = itemDelegate.index
|
||||
dialog.backend.currentRow = itemDelegate.index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
}
|
||||
|
@@ -485,7 +485,6 @@ add_qtc_plugin(QmlDesigner
|
||||
editorproxy.cpp editorproxy.h
|
||||
EXPLICIT_MOC
|
||||
components/propertyeditor/propertyeditorvalue.h
|
||||
components/connectioneditor/connectionviewwidget.h
|
||||
qmldesignerplugin.h
|
||||
EXTRA_TRANSLATIONS
|
||||
"${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner"
|
||||
@@ -940,14 +939,15 @@ extend_qtc_plugin(QmlDesigner
|
||||
addnewbackenddialog.cpp addnewbackenddialog.h addnewbackenddialog.ui
|
||||
backendmodel.cpp backendmodel.h
|
||||
bindingmodel.cpp bindingmodel.h
|
||||
bindingmodelitem.cpp bindingmodelitem.h
|
||||
connectioneditor.qrc
|
||||
connectioneditorevaluator.cpp connectioneditorevaluator.h
|
||||
connectioneditorstatements.cpp connectioneditorstatements.h
|
||||
connectionmodel.cpp connectionmodel.h
|
||||
connectionview.cpp connectionview.h
|
||||
connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui
|
||||
delegates.cpp delegates.h
|
||||
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
|
||||
dynamicpropertiesitem.cpp dynamicpropertiesitem.h
|
||||
connectioneditorutils.cpp connectioneditorutils.h
|
||||
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
|
||||
propertytreemodel.cpp propertytreemodel.h
|
||||
)
|
||||
|
@@ -2,111 +2,27 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "bindingmodel.h"
|
||||
|
||||
#include "bindingmodelitem.h"
|
||||
#include "connectionview.h"
|
||||
#include "connectioneditorutils.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <nodeproperty.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <rewritingexception.h>
|
||||
#include <rewritertransaction.h>
|
||||
#include <rewriterview.h>
|
||||
#include <rewritingexception.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
BindingModel::BindingModel(ConnectionView *parent)
|
||||
: QStandardItemModel(parent), m_connectionView(parent),
|
||||
m_delegate(new BindingModelBackendDelegate(this))
|
||||
: QStandardItemModel(parent)
|
||||
, m_connectionView(parent)
|
||||
, m_delegate(new BindingModelBackendDelegate(this))
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged);
|
||||
}
|
||||
|
||||
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;
|
||||
setHorizontalHeaderLabels(BindingModelItem::headerLabels());
|
||||
}
|
||||
|
||||
ConnectionView *BindingModel::connectionView() const
|
||||
@@ -114,465 +30,235 @@ ConnectionView *BindingModel::connectionView() const
|
||||
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
|
||||
{
|
||||
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]() {
|
||||
handleSourceNodeChanged();
|
||||
expressionChanged();
|
||||
});
|
||||
|
||||
connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||
handleSourcePropertyChanged();
|
||||
expressionChanged();
|
||||
});
|
||||
}
|
||||
|
||||
int BindingModelBackendDelegate::currentRow() const
|
||||
void BindingModelBackendDelegate::update(const BindingProperty &property, AbstractView *view)
|
||||
{
|
||||
return m_currentRow;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::setCurrentRow(int i)
|
||||
{
|
||||
// See BindingDelegate::createEditor
|
||||
|
||||
if (m_currentRow == i)
|
||||
if (!property.isValid())
|
||||
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());
|
||||
|
||||
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);
|
||||
QString targetName = QString::fromUtf8(property.name());
|
||||
m_targetNode = idOrTypeName(property.parentModelNode());
|
||||
|
||||
auto modelNodes = addName(availableModelNodes(view), sourceNodeName);
|
||||
m_sourceNode.setModel(modelNodes);
|
||||
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);
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
//reset
|
||||
auto targetProperties = addName(availableTargetProperties(property), targetName);
|
||||
m_property.setModel(targetProperties);
|
||||
m_property.setCurrentText(targetName);
|
||||
|
||||
emit targetNodeChanged();
|
||||
}
|
||||
|
||||
QString BindingModelBackendDelegate::targetNode() const
|
||||
@@ -595,54 +281,22 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
|
||||
return &m_sourceNodeProperty;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleSourceNodeChanged()
|
||||
void BindingModelBackendDelegate::expressionChanged() const
|
||||
{
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->connectionView(), return );
|
||||
QTC_ASSERT(model, return);
|
||||
|
||||
const QString sourceNode = m_sourceNode.currentText();
|
||||
const QString sourceProperty = m_sourceNodeProperty.currentText();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
if (sourceProperty.isEmpty())
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
else
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
|
||||
[&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());
|
||||
});
|
||||
int row = model->currentIndex();
|
||||
model->commitExpression(row, expression);
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -3,132 +3,98 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <modelnode.h>
|
||||
#include <abstractproperty.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <modelnode.h>
|
||||
#include <studioquickwidget.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ConnectionView;
|
||||
class BindingModelBackendDelegate;
|
||||
class BindingModelItem;
|
||||
class ConnectionView;
|
||||
|
||||
class BindingModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
public:
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
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);
|
||||
void bindingChanged(const BindingProperty &bindingProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
void selectionChanged(const QList<ModelNode> &selectedNodes);
|
||||
|
||||
ConnectionView *connectionView() const;
|
||||
BindingProperty bindingPropertyForRow(int rowNumber) const;
|
||||
QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
|
||||
QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
|
||||
void deleteBindindByRow(int rowNumber);
|
||||
void addBindingForCurrentNode();
|
||||
void resetModel();
|
||||
BindingModelBackendDelegate *delegate() const;
|
||||
|
||||
int currentIndex() const;
|
||||
BindingProperty currentProperty() const;
|
||||
BindingProperty propertyForRow(int row) const;
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void reset(const QList<ModelNode> &selectedNodes = {});
|
||||
void setCurrentIndex(int i);
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty,
|
||||
QString *sourceNode,
|
||||
QString *sourceProperty);
|
||||
void setCurrentProperty(const AbstractProperty &property);
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
void updateItem(const BindingProperty &property);
|
||||
void removeItem(const AbstractProperty &property);
|
||||
|
||||
void commitExpression(int row, const QString &expression);
|
||||
|
||||
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;
|
||||
BindingModelBackendDelegate *delegate() const;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
std::optional<int> rowForProperty(const AbstractProperty &property) const;
|
||||
BindingModelItem *itemForRow(int row) const;
|
||||
BindingModelItem *itemForProperty(const AbstractProperty &property) const;
|
||||
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
|
||||
private:
|
||||
ConnectionView *m_connectionView;
|
||||
bool m_lock = false;
|
||||
bool m_handleDataChanged = false;
|
||||
QString m_exceptionError;
|
||||
int m_currentIndex = 0;
|
||||
ConnectionView *m_connectionView = nullptr;
|
||||
BindingModelBackendDelegate *m_delegate = nullptr;
|
||||
int m_currentIndex = -1;
|
||||
};
|
||||
|
||||
class BindingModelBackendDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
|
||||
|
||||
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
|
||||
|
||||
signals:
|
||||
void targetNodeChanged();
|
||||
|
||||
public:
|
||||
BindingModelBackendDelegate(BindingModel *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
//void nameChanged();
|
||||
void targetNodeChanged();
|
||||
void update(const BindingProperty &property, AbstractView *view);
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleException();
|
||||
QString targetNode() const;
|
||||
void expressionChanged() const;
|
||||
|
||||
StudioQmlComboBoxBackend *property();
|
||||
StudioQmlComboBoxBackend *sourceNode();
|
||||
StudioQmlComboBoxBackend *sourceProperty();
|
||||
|
||||
void handleSourceNodeChanged();
|
||||
void handleSourcePropertyChanged();
|
||||
|
||||
QString m_targetNode;
|
||||
StudioQmlComboBoxBackend m_property;
|
||||
StudioQmlComboBoxBackend m_sourceNode;
|
||||
StudioQmlComboBoxBackend m_sourceNodeProperty;
|
||||
QString m_exceptionError;
|
||||
int m_currentRow = -1;
|
||||
QString m_targetNode;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -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.
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -80,10 +80,6 @@ void ConnectionModel::resetModel()
|
||||
for (const ModelNode &modelNode : connectionView()->allModelNodes())
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0);
|
||||
connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "connectionview.h"
|
||||
#include "connectionviewwidget.h"
|
||||
|
||||
#include "backendmodel.h"
|
||||
#include "bindingmodel.h"
|
||||
@@ -70,9 +69,13 @@ public:
|
||||
"ConnectionsEditorEditorBackend", 1);
|
||||
|
||||
map->setProperties(
|
||||
{{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())},
|
||||
{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())},
|
||||
{"dynamicPropertiesModel",
|
||||
{{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"dynamicPropertiesModel",
|
||||
QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}});
|
||||
|
||||
qmlRegisterType<ConnectionModelBackendDelegate>("ConnectionsEditorEditorBackend",
|
||||
@@ -98,7 +101,6 @@ public:
|
||||
// init the first load of the QML UI elements
|
||||
reloadQmlSource();
|
||||
}
|
||||
|
||||
~ConnectionViewQuickWidget() = default;
|
||||
|
||||
static QString qmlSourcesPath()
|
||||
@@ -136,17 +138,13 @@ private:
|
||||
};
|
||||
|
||||
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
|
||||
: AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()),
|
||||
m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(false, 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);
|
||||
}
|
||||
: AbstractView{externalDependencies}
|
||||
, m_connectionModel(new ConnectionModel(this))
|
||||
, m_bindingModel(new BindingModel(this))
|
||||
, m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this))
|
||||
, m_backendModel(new BackendModel(this))
|
||||
, m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
|
||||
{}
|
||||
|
||||
ConnectionView::~ConnectionView()
|
||||
{
|
||||
@@ -156,25 +154,22 @@ ConnectionView::~ConnectionView()
|
||||
void ConnectionView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
bindingModel()->reset();
|
||||
dynamicPropertiesModel()->reset();
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
backendModel()->resetModel();
|
||||
}
|
||||
|
||||
void ConnectionView::modelAboutToBeDetached(Model *model)
|
||||
{
|
||||
AbstractView::modelAboutToBeDetached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
bindingModel()->reset();
|
||||
dynamicPropertiesModel()->reset();
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
}
|
||||
|
||||
void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/)
|
||||
{
|
||||
//bindings
|
||||
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*/)
|
||||
{
|
||||
connectionModel()->resetModel();
|
||||
bindingModel()->resetModel();
|
||||
dynamicPropertiesModel()->resetModel();
|
||||
bindingModel()->reset();
|
||||
dynamicPropertiesModel()->reset();
|
||||
}
|
||||
|
||||
void ConnectionView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
|
||||
@@ -212,10 +207,10 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &p
|
||||
{
|
||||
for (const AbstractProperty &property : propertyList) {
|
||||
if (property.isBindingProperty()) {
|
||||
bindingModel()->bindingRemoved(property.toBindingProperty());
|
||||
dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty());
|
||||
bindingModel()->removeItem(property);
|
||||
dynamicPropertiesModel()->removeItem(property);
|
||||
} else if (property.isVariantProperty()) {
|
||||
dynamicPropertiesModel()->variantRemoved(property.toVariantProperty());
|
||||
dynamicPropertiesModel()->removeItem(property);
|
||||
} else if (property.isSignalHandlerProperty()) {
|
||||
connectionModel()->removeRowFromTable(property.toSignalHandlerProperty());
|
||||
}
|
||||
@@ -227,7 +222,7 @@ void ConnectionView::variantPropertiesChanged(const QList<VariantProperty> &prop
|
||||
{
|
||||
for (const VariantProperty &variantProperty : propertyList) {
|
||||
if (variantProperty.isDynamic())
|
||||
dynamicPropertiesModel()->variantPropertyChanged(variantProperty);
|
||||
dynamicPropertiesModel()->updateItem(variantProperty);
|
||||
if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode())
|
||||
backendModel()->resetModel();
|
||||
|
||||
@@ -241,9 +236,9 @@ void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &prop
|
||||
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
for (const BindingProperty &bindingProperty : propertyList) {
|
||||
bindingModel()->bindingChanged(bindingProperty);
|
||||
bindingModel()->updateItem(bindingProperty);
|
||||
if (bindingProperty.isDynamic())
|
||||
dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty);
|
||||
dynamicPropertiesModel()->updateItem(bindingProperty);
|
||||
if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode())
|
||||
backendModel()->resetModel();
|
||||
|
||||
@@ -263,39 +258,8 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVector<SignalHandlerP
|
||||
void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
|
||||
const QList<ModelNode> & /*lastSelectedNodeList*/)
|
||||
{
|
||||
bindingModel()->selectionChanged(selectedNodeList);
|
||||
bindingModel()->reset(selectedNodeList);
|
||||
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*/)
|
||||
@@ -310,14 +274,7 @@ void ConnectionView::currentStateChanged(const ModelNode &)
|
||||
|
||||
WidgetInfo ConnectionView::widgetInfo()
|
||||
{
|
||||
/* Enable new connection editor here */
|
||||
const bool newEditor = true;
|
||||
|
||||
QWidget *widget = m_connectionViewWidget.data();
|
||||
if (newEditor)
|
||||
widget = m_connectionViewQuickWidget.data();
|
||||
|
||||
return createWidgetInfo(widget,
|
||||
return createWidgetInfo(m_connectionViewQuickWidget.data(),
|
||||
QLatin1String("ConnectionView"),
|
||||
WidgetInfo::LeftPane,
|
||||
0,
|
||||
@@ -334,31 +291,6 @@ bool ConnectionView::isWidgetEnabled()
|
||||
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
|
||||
{
|
||||
return m_connectionModel;
|
||||
|
@@ -51,9 +51,6 @@ public:
|
||||
|
||||
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||
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;
|
||||
|
||||
@@ -63,14 +60,8 @@ public:
|
||||
bool hasWidget() const override;
|
||||
bool isWidgetEnabled();
|
||||
|
||||
QTableView *connectionTableView() const;
|
||||
QTableView *bindingTableView() const;
|
||||
QTableView *dynamicPropertiesTableView() const;
|
||||
QTableView *backendView() const;
|
||||
|
||||
DynamicPropertiesModel *dynamicPropertiesModel() const;
|
||||
|
||||
ConnectionViewWidget *connectionViewWidget() const;
|
||||
ConnectionModel *connectionModel() const;
|
||||
BindingModel *bindingModel() const;
|
||||
BackendModel *backendModel() const;
|
||||
@@ -83,9 +74,7 @@ public:
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
private: //variables
|
||||
QPointer<ConnectionViewWidget> m_connectionViewWidget;
|
||||
|
||||
private:
|
||||
ConnectionModel *m_connectionModel;
|
||||
BindingModel *m_bindingModel;
|
||||
DynamicPropertiesModel *m_dynamicPropertiesModel;
|
||||
|
@@ -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 ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == BindingTab) {
|
||||
if (current.isValid()) {
|
||||
emit setEnabledRemoveButton(true);
|
||||
} else {
|
||||
emit setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == ConnectionTab) {
|
||||
if (current.isValid()) {
|
||||
emit setEnabledRemoveButton(true);
|
||||
} else {
|
||||
emit setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == DynamicPropertiesTab) {
|
||||
if (current.isValid()) {
|
||||
emit setEnabledRemoveButton(true);
|
||||
} else {
|
||||
emit setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*revious*/)
|
||||
{
|
||||
if (currentTab() == BackendTab) {
|
||||
if (current.isValid()) {
|
||||
emit setEnabledRemoveButton(true);
|
||||
} else {
|
||||
emit setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -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 ¤t, const QModelIndex &previous);
|
||||
void connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void backendTableViewSelectionChanged(const QModelIndex ¤t, 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
|
@@ -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>
|
@@ -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
|
@@ -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
|
@@ -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.
|
@@ -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.
|
File diff suppressed because it is too large
Load Diff
@@ -3,19 +3,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nodeinstanceglobal.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
#include "dynamicpropertiesitem.h"
|
||||
|
||||
#include "nodeinstanceglobal.h"
|
||||
#include "studioquickwidget.h"
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class AbstractProperty;
|
||||
class AbstractView;
|
||||
class BindingProperty;
|
||||
class AbstractProperty;
|
||||
class ModelNode;
|
||||
class VariantProperty;
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate;
|
||||
|
||||
@@ -23,112 +21,67 @@ class DynamicPropertiesModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
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(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
|
||||
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
void abstractPropertyChanged(const AbstractProperty &bindingProperty);
|
||||
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;
|
||||
DynamicPropertiesModelBackendDelegate *delegate() const;
|
||||
|
||||
AbstractView *view() const { return m_view; }
|
||||
AbstractProperty abstractPropertyForRow(int rowNumber) const;
|
||||
BindingProperty bindingPropertyForRow(int rowNumber) 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);
|
||||
int currentIndex() const;
|
||||
AbstractProperty currentProperty() const;
|
||||
AbstractProperty propertyForRow(int row) const;
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void reset(const QList<ModelNode> &modelNodes = {});
|
||||
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 currentIndexChanged();
|
||||
void commitPropertyType(int row, const TypeName &type);
|
||||
void commitPropertyName(int row, const PropertyName &name);
|
||||
void commitPropertyValue(int row, const QVariant &value);
|
||||
|
||||
void dispatchPropertyChanges(const AbstractProperty &abstractProperty);
|
||||
|
||||
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;
|
||||
|
||||
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:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void handleException();
|
||||
|
||||
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;
|
||||
int m_currentIndex = -1;
|
||||
|
||||
// TODO: Remove.
|
||||
QList<ModelNode> m_selectedNodes = {};
|
||||
bool m_explicitSelection = false;
|
||||
};
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate : public QObject
|
||||
@@ -137,40 +90,33 @@ class DynamicPropertiesModelBackendDelegate : public QObject
|
||||
|
||||
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
|
||||
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 *value READ value CONSTANT)
|
||||
//Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
|
||||
|
||||
public:
|
||||
DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr);
|
||||
|
||||
void update(const AbstractProperty &property);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
void nameChanged();
|
||||
void valueChanged();
|
||||
void targetNodeChanged();
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleTypeChanged();
|
||||
void handleNameChanged();
|
||||
void handleValueChanged();
|
||||
void handleException();
|
||||
QVariant variantValue() const;
|
||||
QString targetNode() const;
|
||||
|
||||
StudioQmlComboBoxBackend *type();
|
||||
|
||||
StudioQmlTextBackend *name();
|
||||
StudioQmlTextBackend *value();
|
||||
QString targetNode() const;
|
||||
|
||||
std::optional<int> m_internalNodeId;
|
||||
StudioQmlComboBoxBackend m_type;
|
||||
StudioQmlTextBackend m_name;
|
||||
StudioQmlTextBackend m_value;
|
||||
int m_currentRow = -1;
|
||||
QString m_exceptionError;
|
||||
QString m_targetNode;
|
||||
};
|
||||
|
||||
|
@@ -834,7 +834,7 @@ void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &
|
||||
ModelNode node(property.parentModelNode());
|
||||
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->variantPropertyChanged(property);
|
||||
m_dynamicPropertiesModel->updateItem(property);
|
||||
if (m_selectedMaterial.property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||
else
|
||||
@@ -869,7 +869,7 @@ void MaterialEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
|
||||
|
||||
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->bindingPropertyChanged(property);
|
||||
m_dynamicPropertiesModel->updateItem(property);
|
||||
if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||
else
|
||||
@@ -897,12 +897,8 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||
|
||||
void MaterialEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if (property.isBindingProperty())
|
||||
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
|
||||
else if (property.isVariantProperty())
|
||||
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
|
||||
}
|
||||
for (const auto &property : propertyList)
|
||||
m_dynamicPropertiesModel->removeItem(property);
|
||||
}
|
||||
|
||||
// request render image for the selected material node
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "bindingproperty.h"
|
||||
#include "propertyeditorvalue.h"
|
||||
#include "connectioneditorutils.h"
|
||||
|
||||
#include <dynamicpropertiesmodel.h>
|
||||
|
||||
@@ -96,7 +97,7 @@ QHash<int, QByteArray> DynamicPropertiesProxyModel::roleNames() const
|
||||
QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
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());
|
||||
|
||||
@@ -142,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const
|
||||
{
|
||||
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)
|
||||
@@ -162,12 +163,12 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (DynamicPropertiesModel::isValueType(typeName)) {
|
||||
QVariant value = DynamicPropertiesModel::defaultValueForType(typeName);
|
||||
if (isDynamicVariantPropertyType(typeName)) {
|
||||
QVariant value = defaultValueForType(typeName);
|
||||
VariantProperty variantProp = modelNode.variantProperty(name.toUtf8());
|
||||
variantProp.setDynamicTypeNameAndValue(typeName, value);
|
||||
} else {
|
||||
QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName);
|
||||
QString expression = defaultExpressionForType(typeName);
|
||||
|
||||
BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8());
|
||||
bindingProp.setDynamicTypeNameAndExpression(typeName, expression);
|
||||
@@ -261,7 +262,7 @@ PropertyEditorValue *DynamicPropertyRow::backendValue() const
|
||||
|
||||
void DynamicPropertyRow::remove()
|
||||
{
|
||||
m_model->dynamicPropertiesModel()->deleteDynamicPropertyByRow(m_row);
|
||||
m_model->dynamicPropertiesModel()->remove(m_row);
|
||||
}
|
||||
|
||||
PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue()
|
||||
@@ -283,7 +284,7 @@ void DynamicPropertyRow::setupBackendValue()
|
||||
if (!m_model)
|
||||
return;
|
||||
|
||||
AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row);
|
||||
AbstractProperty property = m_model->dynamicPropertiesModel()->propertyForRow(m_row);
|
||||
if (!property.isValid())
|
||||
return;
|
||||
|
||||
@@ -324,9 +325,9 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
m_lock = true;
|
||||
@@ -335,6 +336,10 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
|
||||
auto view = propertiesModel->view();
|
||||
RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__);
|
||||
try {
|
||||
if (property.isBindingProperty()) {
|
||||
convertBindingToVariantProperty(property.toBindingProperty(), value);
|
||||
} else if (property.isVariantProperty()) {
|
||||
VariantProperty variantProperty = property.toVariantProperty();
|
||||
QmlObjectNode objectNode = variantProperty.parentQmlObjectNode();
|
||||
if (view->currentState().isBaseState()
|
||||
&& !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) {
|
||||
@@ -346,6 +351,7 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
|
||||
if (objectNode.isValid() && objectNode.modelValue(name) != value)
|
||||
objectNode.setVariantProperty(name, value);
|
||||
}
|
||||
}
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
e.showException();
|
||||
@@ -358,7 +364,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression)
|
||||
return;
|
||||
|
||||
auto propertiesModel = m_model->dynamicPropertiesModel();
|
||||
AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row);
|
||||
AbstractProperty property = propertiesModel->propertyForRow(m_row);
|
||||
|
||||
BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name());
|
||||
|
||||
@@ -413,15 +419,15 @@ void DynamicPropertyRow::resetValue()
|
||||
auto propertiesModel = m_model->dynamicPropertiesModel();
|
||||
auto view = propertiesModel->view();
|
||||
|
||||
AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row);
|
||||
AbstractProperty property = propertiesModel->propertyForRow(m_row);
|
||||
TypeName typeName = property.dynamicTypeName();
|
||||
|
||||
if (view->currentState().isBaseState()) {
|
||||
if (DynamicPropertiesModel::isValueType(typeName)) {
|
||||
QVariant value = DynamicPropertiesModel::defaultValueForType(typeName);
|
||||
if (isDynamicVariantPropertyType(typeName)) {
|
||||
QVariant value = defaultValueForType(typeName);
|
||||
commitValue(value);
|
||||
} else {
|
||||
QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName);
|
||||
QString expression = defaultExpressionForType(typeName);
|
||||
commitExpression(expression);
|
||||
}
|
||||
} else {
|
||||
|
@@ -588,7 +588,7 @@ void TextureEditorView::variantPropertiesChanged(const QList<VariantProperty> &p
|
||||
ModelNode node(property.parentModelNode());
|
||||
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->variantPropertyChanged(property);
|
||||
m_dynamicPropertiesModel->updateItem(property);
|
||||
if (m_selectedTexture.property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
|
||||
else
|
||||
@@ -612,7 +612,7 @@ void TextureEditorView::bindingPropertiesChanged(const QList<BindingProperty> &p
|
||||
|
||||
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->bindingPropertyChanged(property);
|
||||
m_dynamicPropertiesModel->updateItem(property);
|
||||
if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
|
||||
else
|
||||
@@ -642,12 +642,8 @@ void TextureEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||
|
||||
void TextureEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if (property.isBindingProperty())
|
||||
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
|
||||
else if (property.isVariantProperty())
|
||||
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
|
||||
}
|
||||
for (const auto &property : propertyList)
|
||||
m_dynamicPropertiesModel->removeItem(property);
|
||||
}
|
||||
|
||||
void TextureEditorView::nodeReparented(const ModelNode &node,
|
||||
|
Reference in New Issue
Block a user