QmlDesigner: Add PropertyTreeModel

This model represents all properties in the document
in a tree model.
PropertyListProxyModel allows to flatten a level into a list model.
PropertyTreeModelDelegate exposed a single item to a combobox.

Change-Id: I9b56f1ecc9aa57777356bc795b5a15b17559ae24
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Thomas Hartmann
2023-08-29 12:16:26 +02:00
parent 0211943909
commit 3e0ae7951d
5 changed files with 1119 additions and 1 deletions

View File

@@ -13,7 +13,7 @@ add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=maybe-uninitial
add_qtc_library(QmlDesignerUtils STATIC add_qtc_library(QmlDesignerUtils STATIC
DEPENDS DEPENDS
Qt::Gui Utils Qt::QmlPrivate Qt::Gui Utils Qt::QmlPrivate Core
DEFINES QMLDESIGNERUTILS_LIBRARY DEFINES QMLDESIGNERUTILS_LIBRARY
PUBLIC_DEFINES $<$<BOOL:${QTC_STATIC_BUILD}>:QMLDESIGNER_STATIC_LIBRARY> PUBLIC_DEFINES $<$<BOOL:${QTC_STATIC_BUILD}>:QMLDESIGNER_STATIC_LIBRARY>
@@ -486,6 +486,7 @@ add_qtc_plugin(QmlDesigner
EXPLICIT_MOC EXPLICIT_MOC
components/propertyeditor/propertyeditorvalue.h components/propertyeditor/propertyeditorvalue.h
components/connectioneditor/connectionviewwidget.h components/connectioneditor/connectionviewwidget.h
qmldesignerplugin.h
EXTRA_TRANSLATIONS EXTRA_TRANSLATIONS
"${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner" "${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner"
PROPERTIES PROPERTIES
@@ -946,6 +947,7 @@ extend_qtc_plugin(QmlDesigner
delegates.cpp delegates.h delegates.cpp delegates.h
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
propertytreemodel.cpp propertytreemodel.h
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner

View File

@@ -0,0 +1,920 @@
// 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 "propertytreemodel.h"
#include "connectionview.h"
#include <bindingproperty.h>
#include <designeralgorithm.h>
#include <exception.h>
#include <model/modelutils.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <rewritertransaction.h>
#include <rewriterview.h>
#include <signalhandlerproperty.h>
#include <variantproperty.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QByteArrayView>
#include <QMessageBox>
#include <QTimer>
namespace QmlDesigner {
const std::vector<PropertyName> blockListProperties = {"children",
"data",
"childrenRect",
"icon",
"left",
"top",
"bottom",
"right",
"locale",
"objectName",
"transitions",
"states",
"resources",
"data",
"transformOrigin",
"transformOriginPoint",
"verticalCenter",
"horizontalCenter",
"anchors.bottom",
"anchors.top",
"anchors.left",
"anchors.right",
"anchors.fill",
"anchors.horizontalCenter",
"anchors.verticalCenter",
"anchors.centerIn",
"transform",
"visibleChildren"};
const std::vector<PropertyName> blockListSlots = {"childAt",
"contains",
"destroy",
"dumpItemTree",
"ensurePolished",
"grabToImage",
"mapFromGlobal",
"mapFromItem",
"mapToGlobal",
"mapToItem",
"valueAt",
"toString",
"getText",
"inputMethodQuery",
"positionAt",
"positionToRectangle",
"isRightToLeft"
};
const std::vector<PropertyName> priorityListSignals = {"clicked",
"doubleClicked",
"pressed",
"released",
"toggled",
"valueModified",
"valueChanged",
"checkedChanged",
"moved",
"accepted",
"editingFinished",
"entered",
"exited",
"canceled",
"triggered",
"stateChanged",
"started",
"stopped",
"finished"
"enabledChanged",
"visibleChanged",
"opacityChanged",
"rotationChanged"};
const std::vector<PropertyName> priorityListProperties
= {"opacity", "visible", "value", "x", "y", "width", "height",
"rotation", "color", "scale", "state", "enabled", "z", "text",
"pressed", "containsMouse", "checked", "hovered", "down", "clip", "parent"};
const std::vector<PropertyName> priorityListSlots = {"toggle",
"increase",
"decrease",
"clear",
"complete",
"pause",
"restart",
"resume",
"start",
"stop",
"forceActiveFocus"};
std::vector<PropertyName> properityLists()
{
std::vector<PropertyName> result;
result.insert(result.end(), priorityListSignals.begin(), priorityListSignals.end());
result.insert(result.end(), priorityListProperties.begin(), priorityListProperties.end());
result.insert(result.end(), priorityListSlots.begin(), priorityListSlots.end());
return result;
}
PropertyTreeModel::PropertyTreeModel(ConnectionView *parent)
: QAbstractItemModel(parent), m_connectionView(parent)
{}
void PropertyTreeModel::resetModel()
{
beginResetModel();
m_indexCache.clear();
m_indexHash.clear();
m_indexCount = 0;
m_nodeList = allModelNodesWithIdsSortedByDisplayName();
endResetModel();
testModel();
}
QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const
{
int internalId = index.internalId();
if (role == InternalIdRole)
return internalId;
if (role == RowRole)
return index.row();
if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole) {
if (!index.isValid())
return {};
if (internalId < 0)
return {};
QTC_ASSERT(internalId < m_indexCount, return {"assert"});
DataCacheItem item = m_indexHash[index.internalId()];
if (item.propertyName.isEmpty()) { //node
if (role == PropertyNameRole)
return item.modelNode.displayName();
return true; //nodes are always shown
}
if (role == ExpressionRole)
return QString(item.modelNode.id() + item.propertyName);
if (role == PropertyNameRole)
return item.propertyName;
static const auto priority = properityLists();
if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end())
return true; //listed priority properties
auto dynamic = getDynamicProperties(item.modelNode);
if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end())
return true; //dynamic properties have priority
return false;
}
// can be removed later since we only use the two roles above in QML
// just for testing
if (!(role == Qt::DisplayRole || role == Qt::FontRole))
return {};
if (!index.isValid())
return {};
if (internalId < 0)
return {};
QTC_ASSERT(internalId < m_indexCount, return {"assert"});
DataCacheItem item = m_indexHash[index.internalId()];
if (item.propertyName.isEmpty()) {
const QString name = item.modelNode.displayName();
if (role == Qt::DisplayRole)
return name;
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::DisplayRole)
return item.propertyName;
QFont f;
auto priority = properityLists();
if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end())
f.setBold(true);
auto dynamic = getDynamicProperties(item.modelNode);
if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end())
f.setBold(true);
return f;
}
Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const
{
return Qt::ItemIsEnabled;
}
QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const
{
int internalId = parent.internalId();
if (!m_connectionView->isAttached())
return {};
if (!hasIndex(row, column, parent))
return QModelIndex();
const int rootId = -1;
if (!parent.isValid())
return createIndex(0, 0, rootId);
if (internalId == rootId) { //root level model node
const ModelNode modelNode = m_nodeList[row];
return ensureModelIndex(modelNode, row);
}
//property
QTC_ASSERT(internalId >= 0, return {});
DataCacheItem item = m_indexHash[internalId];
QTC_ASSERT(item.modelNode.isValid(), return {});
if (!item.propertyName.isEmpty()) {
// "." aka sub property
auto properties = sortedDotPropertyNamesSignalsSlots(item.modelNode.metaInfo(),
item.propertyName);
PropertyName propertyName = properties[row];
return ensureModelIndex(item.modelNode, propertyName, row);
}
auto properties = sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode);
PropertyName propertyName = properties[row];
return ensureModelIndex(item.modelNode, propertyName, row);
}
QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return {};
int internalId = index.internalId();
if (internalId == -1)
return {};
QTC_ASSERT(internalId < m_indexCount, return {});
const DataCacheItem item = m_indexHash[index.internalId()];
// no property means the parent is the root item
if (item.propertyName.isEmpty())
return createIndex(0, 0, -1);
if (item.propertyName.contains(".")) {
auto list = item.propertyName.split('.');
DataCacheItem parent;
parent.modelNode = item.modelNode;
parent.propertyName = list.first();
if (auto iter = m_indexCache.find(parent); iter != m_indexCache.end()) {
const auto vector = sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode);
QList<PropertyName> list(vector.begin(), vector.end());
int row = list.indexOf(parent.propertyName);
return createIndex(row, 0, iter->internalIndex);
}
}
// find the parent
int row = m_nodeList.indexOf(item.modelNode);
return ensureModelIndex(item.modelNode, row);
}
QPersistentModelIndex PropertyTreeModel::indexForInernalIdAndRow(int internalId, int row)
{
return createIndex(row, 0, internalId);
}
int PropertyTreeModel::rowCount(const QModelIndex &parent) const
{
if (!m_connectionView->isAttached() || parent.column() > 0)
return 0;
if (!parent.isValid())
return 1;
int internalId = parent.internalId();
if (internalId == -1)
return m_nodeList.size();
QTC_ASSERT(internalId < m_indexCount, return 0);
DataCacheItem item = m_indexHash[internalId];
if (!item.propertyName.isEmpty()) {
if (item.modelNode.metaInfo().property(item.propertyName).isPointer()) {
auto subProbs = sortedDotPropertyNamesSignalsSlots(item.modelNode.metaInfo(),
item.propertyName);
return static_cast<int>(subProbs.size());
}
return 0;
}
return static_cast<int>(sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode).size());
}
int PropertyTreeModel::columnCount(const QModelIndex &) const
{
return 1;
}
void PropertyTreeModel::setPropertyType(PropertyTypes type)
{
if (m_type == type)
return;
m_type = type;
resetModel();
}
void PropertyTreeModel::setFilter(const QString &filter)
{
if (m_filter == filter)
return;
m_filter = filter;
resetModel();
}
QList<ModelNode> PropertyTreeModel::nodeList() const
{
return m_nodeList;
}
const std::vector<PropertyName> PropertyTreeModel::getProperties(const ModelNode &modelNode) const
{
return sortedAndFilteredPropertyNamesSignalsSlots(modelNode);
}
ModelNode PropertyTreeModel::getModelNodeForId(const QString &id) const
{
if (!m_connectionView->isAttached())
return {};
return m_connectionView->modelNodeForId(id);
}
QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, int row) const
{
DataCacheItem item;
item.modelNode = node;
auto iter = m_indexCache.find(item);
if (iter != m_indexCache.end())
return createIndex(row, 0, iter->internalIndex);
item.internalIndex = m_indexCount;
m_indexCount++;
m_indexHash.push_back(item);
m_indexCache.insert(item);
return createIndex(row, 0, item.internalIndex);
}
QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node,
const PropertyName &name,
int row) const
{
DataCacheItem item;
item.modelNode = node;
item.propertyName = name;
auto iter = m_indexCache.find(item);
if (iter != m_indexCache.end())
return createIndex(row, 0, iter->internalIndex);
item.internalIndex = m_indexCount;
m_indexCount++;
m_indexHash.push_back(item);
m_indexCache.insert(item);
return createIndex(row, 0, item.internalIndex);
}
void PropertyTreeModel::testModel()
{
qDebug() << Q_FUNC_INFO;
qDebug() << rowCount({});
QModelIndex rootIndex = index(0, 0);
qDebug() << rowCount(rootIndex);
QModelIndex firstItem = index(0, 0, rootIndex);
qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem);
firstItem = index(1, 0, rootIndex);
qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem);
firstItem = index(2, 0, rootIndex);
qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem);
QModelIndex firstProperty = index(0, 0, firstItem);
qDebug() << "fp" << data(firstProperty, Qt::DisplayRole) << rowCount(firstProperty);
qDebug() << m_indexCount << m_indexHash.size() << m_indexCache.size();
}
const QList<ModelNode> PropertyTreeModel::allModelNodesWithIdsSortedByDisplayName() const
{
if (!m_connectionView->isAttached())
return {};
return Utils::sorted(ModelUtils::allModelNodesWithId(m_connectionView),
[](const ModelNode &lhs, const ModelNode &rhs) {
return lhs.displayName() < rhs.displayName();
});
}
const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyNamesSignalsSlots(
const ModelNode &modelNode) const
{
std::vector<PropertyName> returnValue;
if (m_type == SignalType) {
returnValue = sortedAndFilteredSignalNames(modelNode.metaInfo());
} else if (m_type == SlotType) {
returnValue = sortedAndFilteredSlotNames(modelNode.metaInfo());
} else {
auto list = sortedAndFilteredPropertyNames(modelNode.metaInfo());
returnValue = getDynamicProperties(modelNode);
std::move(list.begin(), list.end(), std::back_inserter(returnValue));
}
if (m_filter.isEmpty() || modelNode.displayName().contains(m_filter))
return returnValue;
return Utils::filtered(returnValue, [this](const PropertyName &name) {
return name.contains(m_filter.toUtf8()) || name == m_filter.toUtf8();
});
}
const std::vector<PropertyName> PropertyTreeModel::getDynamicProperties(
const ModelNode &modelNode) const
{
QList<PropertyName> list = Utils::transform(modelNode.dynamicProperties(),
[](const AbstractProperty &property) {
return property.name();
});
QList<PropertyName> filtered
= Utils::filtered(list, [this, modelNode](const PropertyName &propertyName) {
PropertyName propertyType = modelNode.property(propertyName).dynamicTypeName();
switch (m_type) {
case AllTypes:
return true;
case NumberType:
return propertyType == "float" || propertyType == "double"
|| propertyType == "int";
case StringType:
return propertyType == "string";
case UrlType:
return propertyType == "url";
case ColorType:
return propertyType == "color";
case BoolType:
return propertyType == "bool";
default:
break;
}
return true;
});
return Utils::sorted(std::vector<PropertyName>(filtered.begin(), filtered.end()));
}
const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyNames(
const NodeMetaInfo &metaInfo, bool recursive) const
{
auto filtered = Utils::filtered(metaInfo.properties(),
[this, recursive](const PropertyMetaInfo &metaInfo) {
// if (!metaInfo.isWritable()) - lhs/rhs
const PropertyName name = metaInfo.name();
if (name.contains("."))
return false;
if (name.startsWith("icon."))
return false;
if (name.startsWith("transformOriginPoint."))
return false;
return filterProperty(name, metaInfo, recursive);
});
auto sorted = Utils::sorted(
Utils::transform(filtered, [](const PropertyMetaInfo &metaInfo) -> PropertyName {
return metaInfo.name();
}));
std::set<PropertyName> set(std::make_move_iterator(sorted.begin()),
std::make_move_iterator(sorted.end()));
auto checkedPriorityList = Utils::filtered(priorityListProperties,
[&set](const PropertyName &name) {
auto it = set.find(name);
const bool b = it != set.end();
if (b)
set.erase(it);
return b;
});
//const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties
std::vector<PropertyName> final(set.begin(), set.end());
std::move(final.begin(), final.end(), std::back_inserter(checkedPriorityList));
return checkedPriorityList;
}
const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSignalNames(
const NodeMetaInfo &metaInfo, bool recursive) const
{
Q_UNUSED(recursive);
const std::vector<PropertyName> priorityListSignals = {"clicked",
"doubleClicked",
"pressed",
"released",
"toggled",
"valueModified",
"valueChanged",
"checkedChanged",
"moved",
"accepted",
"editingFinished",
"entered",
"exited",
"canceled",
"triggered",
"stateChanged",
"started",
"stopped",
"finished"
"enabledChanged",
"visibleChanged",
"opacityChanged",
"rotationChanged"};
auto filtered
= Utils::filtered(metaInfo.signalNames(), [&priorityListSignals](const PropertyName &name) {
if (std::find(priorityListSignals.cbegin(), priorityListSignals.cend(), name)
!= priorityListSignals.cend())
return true;
if (name.endsWith("Changed")) //option?
return false;
return true;
});
auto sorted = Utils::sorted(filtered);
std::set<PropertyName> set(std::make_move_iterator(sorted.begin()),
std::make_move_iterator(sorted.end()));
auto checkedPriorityList = Utils::filtered(priorityListSignals,
[&set](const PropertyName &name) {
auto it = set.find(name);
const bool b = it != set.end();
if (b)
set.erase(it);
return b;
});
//const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties
std::vector<PropertyName> finalPropertyList(set.begin(), set.end());
std::move(finalPropertyList.begin(),
finalPropertyList.end(),
std::back_inserter(checkedPriorityList));
return checkedPriorityList;
}
const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSlotNames(
const NodeMetaInfo &metaInfo, bool recursive) const
{
Q_UNUSED(recursive);
auto priorityList = priorityListSlots;
auto filtered = Utils::filtered(metaInfo.slotNames(), [priorityList](const PropertyName &name) {
if (std::find(priorityListSlots.begin(), priorityListSlots.end(), name)
!= priorityListSlots.end())
return true;
if (name.startsWith("_"))
return false;
if (name.startsWith("q_"))
return false;
if (name.endsWith("Changed")) //option?
return false;
if (std::find(blockListSlots.begin(), blockListSlots.end(), name) != blockListSlots.end())
return false;
return true;
});
auto sorted = Utils::sorted(filtered);
std::set<PropertyName> set(std::make_move_iterator(sorted.begin()),
std::make_move_iterator(sorted.end()));
auto checkedPriorityList = Utils::filtered(priorityListSlots, [&set](const PropertyName &name) {
auto it = set.find(name);
const bool b = it != set.end();
if (b)
set.erase(it);
return b;
});
//const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties
std::vector<PropertyName> final(set.begin(), set.end());
std::move(final.begin(), final.end(), std::back_inserter(checkedPriorityList));
return checkedPriorityList;
}
const std::vector<PropertyName> PropertyTreeModel::sortedDotPropertyNames(
const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const
{
const PropertyName prefix = propertyName + '.';
auto filtered = Utils::filtered(metaInfo.properties(),
[this, prefix](const PropertyMetaInfo &metaInfo) {
const PropertyName name = metaInfo.name();
if (!name.startsWith(prefix))
return false;
return filterProperty(name, metaInfo, true);
});
auto sorted = Utils::sorted(
Utils::transform(filtered, [](const PropertyMetaInfo &metaInfo) -> PropertyName {
return metaInfo.name();
}));
if (sorted.size() == 0 && metaInfo.property(propertyName).propertyType().isQtObject()) {
return Utils::transform(sortedAndFilteredPropertyNames(metaInfo.property(propertyName)
.propertyType(),
true),
[propertyName](const PropertyName &name) -> PropertyName {
return propertyName + "." + name;
});
}
return sorted;
}
const std::vector<PropertyName> PropertyTreeModel::sortedDotPropertySignals(
const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const
{
return Utils::transform(sortedAndFilteredSignalNames(metaInfo.property(propertyName)
.propertyType(),
true),
[propertyName](const PropertyName &name) -> PropertyName {
return propertyName + "." + name;
});
}
const std::vector<PropertyName> PropertyTreeModel::sortedDotPropertySlots(
const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const
{
return Utils::transform(sortedAndFilteredSlotNames(metaInfo.property(propertyName).propertyType(),
true),
[propertyName](const PropertyName &name) -> PropertyName {
return propertyName + "." + name;
});
}
const std::vector<PropertyName> PropertyTreeModel::sortedDotPropertyNamesSignalsSlots(
const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const
{
if (m_type == SignalType) {
return sortedDotPropertySignals(metaInfo, propertyName);
} else if (m_type == SlotType) {
return sortedDotPropertySlots(metaInfo, propertyName);
} else {
return sortedDotPropertyNames(metaInfo, propertyName);
}
}
bool PropertyTreeModel::filterProperty(const PropertyName &name,
const PropertyMetaInfo &metaInfo,
bool recursive) const
{
if (std::find(blockListProperties.begin(), blockListProperties.end(), name)
!= blockListProperties.end())
return false;
const NodeMetaInfo propertyType = metaInfo.propertyType();
//We want to keep sub items with matching properties
if (!recursive && metaInfo.isPointer()
&& sortedAndFilteredPropertyNames(propertyType, true).size() > 0)
return true;
//TODO no type relaxation atm...
switch (m_type) {
case AllTypes:
return true;
case NumberType:
return propertyType.isNumber();
case StringType:
return propertyType.isString();
case UrlType:
return propertyType.isUrl();
//return propertyType.isString() || propertyType.isUrl();
case ColorType:
return propertyType.isColor();
//return propertyType.isString() || propertyType.isColor();
case BoolType:
return propertyType.isBool();
default:
break;
}
return true;
}
QHash<int, QByteArray> PropertyTreeModel::roleNames() const
{
static QHash<int, QByteArray> roleNames{{PropertyNameRole, "propertyName"},
{PropertyPriorityRole, "hasPriority"},
{ExpressionRole, "expression"}};
return roleNames;
}
PropertyListProxyModel::PropertyListProxyModel(PropertyTreeModel *parent)
: QAbstractListModel(), m_treeModel(parent)
{}
void PropertyListProxyModel::setRowandInternalId(int row, int internalId)
{
QTC_ASSERT(m_treeModel, return );
if (internalId == -1)
m_parentIndex = m_treeModel->index(0, 0);
else
m_parentIndex = m_treeModel->indexForInernalIdAndRow(internalId, row);
beginResetModel();
endResetModel();
}
int PropertyListProxyModel::rowCount(const QModelIndex &) const
{
QTC_ASSERT(m_treeModel, return 0);
return m_treeModel->rowCount(m_parentIndex);
}
QVariant PropertyListProxyModel::data(const QModelIndex &index, int role) const
{
QTC_ASSERT(m_treeModel, return 0);
auto treeIndex = m_treeModel->index(index.row(), 0, m_parentIndex);
return m_treeModel->data(treeIndex, role);
}
PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent)
{
connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() {
handleNameChanged();
});
connect(&m_idCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() {
handleIdChanged();
});
}
void PropertyTreeModelDelegate::setPropertyType(PropertyTreeModel::PropertyTypes type)
{
m_model.setPropertyType(type);
}
void PropertyTreeModelDelegate::setup(const QString &id, const QString &name, bool *nameExists)
{
m_model.resetModel();
QStringList idLists = Utils::transform(m_model.nodeList(),
[](const ModelNode &node) { return node.id(); });
if (!idLists.contains(id))
idLists.prepend(id);
m_idCombboBox.setModel(idLists);
m_idCombboBox.setCurrentText(id);
const auto modelNode = m_model.getModelNodeForId(id);
//m_nameCombboBox
std::vector<QString> nameVector = Utils::transform(m_model.getProperties(modelNode),
[](const PropertyName &name) {
return QString::fromUtf8(name);
});
QStringList nameList;
nameList.reserve(nameVector.size());
std::copy(nameVector.begin(), nameVector.end(), std::back_inserter(nameList));
if (!nameList.contains(name)) {
if (!nameExists)
nameList.prepend(name);
else
*nameExists = false;
}
m_nameCombboBox.setModel(nameList);
m_nameCombboBox.setCurrentText(name);
}
QString PropertyTreeModelDelegate::id() const
{
return m_idCombboBox.currentText();
}
QString PropertyTreeModelDelegate::name() const
{
return m_nameCombboBox.currentText();
}
void PropertyTreeModelDelegate::handleNameChanged()
{
const auto id = m_idCombboBox.currentText();
const auto name = m_nameCombboBox.currentText();
setup(id, name);
emit commitData();
// commit data
}
void PropertyTreeModelDelegate::handleIdChanged()
{
const auto id = m_idCombboBox.currentText();
const auto name = m_nameCombboBox.currentText();
bool exists = true;
setup(id, name, &exists);
if (!exists) {
auto model = m_nameCombboBox.model();
model.prepend("---");
m_nameCombboBox.setModel(model);
m_nameCombboBox.setCurrentText("---");
//We do not commit invalid name
} else {
emit commitData();
//commit data
}
}
StudioQmlComboBoxBackend *PropertyTreeModelDelegate::nameCombboBox()
{
return &m_nameCombboBox;
}
StudioQmlComboBoxBackend *PropertyTreeModelDelegate::idCombboBox()
{
return &m_idCombboBox;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,186 @@
// 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 "propertymetainfo.h"
#include <modelnode.h>
#include <studioquickwidget.h>
#include <QAbstractItemModel>
#include <QAbstractListModel>
#include <set>
#include <vector>
namespace QmlDesigner {
class AbstractProperty;
class ModelNode;
class BindingProperty;
class SignalHandlerProperty;
class VariantProperty;
class ConnectionView;
class PropertyTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum UserRoles {
PropertyNameRole = Qt::UserRole + 1,
PropertyPriorityRole,
ExpressionRole,
RowRole,
InternalIdRole
};
enum PropertyTypes {
AllTypes,
NumberType,
StringType,
ColorType,
SignalType,
SlotType,
UrlType,
BoolType
};
PropertyTreeModel(ConnectionView *parent = nullptr);
void resetModel();
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
QPersistentModelIndex indexForInernalIdAndRow(int internalId, int row);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
struct DataCacheItem
{
ModelNode modelNode;
PropertyName propertyName;
int internalIndex = -1;
};
void setPropertyType(PropertyTypes type);
void setFilter(const QString &filter);
QList<ModelNode> nodeList() const;
const std::vector<PropertyName> getProperties(const ModelNode &modelNode) const;
ModelNode getModelNodeForId(const QString &id) const;
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QModelIndex ensureModelIndex(const ModelNode &node, int row) const;
QModelIndex ensureModelIndex(const ModelNode &node, const PropertyName &name, int row) const;
void testModel();
const QList<ModelNode> allModelNodesWithIdsSortedByDisplayName() const;
const std::vector<PropertyName> sortedAndFilteredPropertyNamesSignalsSlots(
const ModelNode &modelNode) const;
const std::vector<PropertyName> getDynamicProperties(const ModelNode &modelNode) const;
const std::vector<PropertyName> sortedAndFilteredPropertyNames(const NodeMetaInfo &metaInfo,
bool recursive = false) const;
const std::vector<PropertyName> sortedAndFilteredSignalNames(const NodeMetaInfo &metaInfo,
bool recursive = false) const;
const std::vector<PropertyName> sortedAndFilteredSlotNames(const NodeMetaInfo &metaInfo,
bool recursive = false) const;
const std::vector<PropertyName> sortedDotPropertyNames(const NodeMetaInfo &metaInfo,
const PropertyName &propertyName) const;
const std::vector<PropertyName> sortedDotPropertySignals(const NodeMetaInfo &metaInfo,
const PropertyName &propertyName) const;
const std::vector<PropertyName> sortedDotPropertySlots(const NodeMetaInfo &metaInfo,
const PropertyName &propertyName) const;
const std::vector<PropertyName> sortedDotPropertyNamesSignalsSlots(
const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const;
bool filterProperty(const PropertyName &name,
const PropertyMetaInfo &metaInfo,
bool recursive) const;
ConnectionView *m_connectionView;
mutable std::set<DataCacheItem> m_indexCache;
mutable std::vector<DataCacheItem> m_indexHash;
mutable int m_indexCount = 0;
QList<ModelNode> m_nodeList;
PropertyTypes m_type = AllTypes;
QString m_filter;
};
class PropertyListProxyModel : public QAbstractListModel
{
Q_OBJECT
public:
PropertyListProxyModel(PropertyTreeModel *parent);
void setRowandInternalId(int row, int internalId);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
ModelNode m_modelNode;
PropertyName m_propertyName;
QPersistentModelIndex m_parentIndex;
PropertyTreeModel *m_treeModel = nullptr;
};
inline bool operator==(const PropertyTreeModel::DataCacheItem &lhs,
const PropertyTreeModel::DataCacheItem &rhs)
{
return lhs.modelNode == rhs.modelNode && lhs.propertyName == rhs.propertyName;
}
inline bool operator<(const PropertyTreeModel::DataCacheItem &lhs,
const PropertyTreeModel::DataCacheItem &rhs)
{
return (lhs.modelNode.id() + lhs.propertyName) < (rhs.modelNode.id() + rhs.propertyName);
}
class PropertyTreeModelDelegate : public QObject
{
Q_OBJECT
Q_PROPERTY(StudioQmlComboBoxBackend *name READ nameCombboBox CONSTANT)
Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT)
public:
explicit PropertyTreeModelDelegate(ConnectionView *parent = nullptr);
void setPropertyType(PropertyTreeModel::PropertyTypes type);
void setup(const QString &id, const QString &name, bool *nameExists = nullptr);
QString id() const;
QString name() const;
signals:
void commitData();
private:
void handleNameChanged();
void handleIdChanged();
StudioQmlComboBoxBackend *nameCombboBox();
StudioQmlComboBoxBackend *idCombboBox();
StudioQmlComboBoxBackend m_nameCombboBox;
StudioQmlComboBoxBackend m_idCombboBox;
PropertyTreeModel::PropertyTypes m_type;
PropertyTreeModel m_model;
};
} // namespace QmlDesigner

View File

@@ -3,6 +3,7 @@
#include "modelutils.h" #include "modelutils.h"
#include <abstractview.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
#include <projectstorage/projectstorage.h> #include <projectstorage/projectstorage.h>
#include <projectstorage/sourcepathcache.h> #include <projectstorage/sourcepathcache.h>
@@ -154,4 +155,11 @@ QList<ModelNode> pruneChildren(const QList<ModelNode> &nodes)
return backNodes; return backNodes;
} }
QList<ModelNode> allModelNodesWithId(AbstractView *view)
{
QTC_ASSERT(view->isAttached(), return {});
return Utils::filtered(view->allModelNodes(),
[&](const ModelNode &node) { return node.hasId(); });
}
} // namespace QmlDesigner::ModelUtils } // namespace QmlDesigner::ModelUtils

View File

@@ -36,4 +36,6 @@ QMLDESIGNERCORE_EXPORT QString componentFilePath(const ModelNode &node);
QMLDESIGNERCORE_EXPORT QList<ModelNode> pruneChildren(const QList<ModelNode> &nodes); QMLDESIGNERCORE_EXPORT QList<ModelNode> pruneChildren(const QList<ModelNode> &nodes);
QMLDESIGNERCORE_EXPORT QList<ModelNode> allModelNodesWithId(AbstractView *view);
} // namespace QmlDesigner::ModelUtils } // namespace QmlDesigner::ModelUtils