forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
@@ -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
|
@@ -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
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user