Commit 198d8173 authored by Thomas Hartmann's avatar Thomas Hartmann
Browse files

QuickDesigner.navigator: Display all QGraphicsObject-type node

properties

The navigator now displays all properties whose type is a sub class
of QGraphicsObject, except for those properties inherited by Qt/Item.
The contents of the default property are displayed as direct children
of the model node's tree node.
Reparenting into and from the displayed properties is supported.

original author: Joerg Schummer
parent 8fa642d8
...@@ -31,14 +31,21 @@ ...@@ -31,14 +31,21 @@
#include <nodeabstractproperty.h> #include <nodeabstractproperty.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <nodeproperty.h>
#include <metainfo.h>
#include <propertymetainfo.h>
#include <qgraphicswidget.h>
#include <abstractview.h> #include <abstractview.h>
#include <invalididexception.h> #include <invalididexception.h>
#include <QMimeData> #include <QMimeData>
#include <QMessageBox> #include <QMessageBox>
#include <QApplication>
namespace QmlDesigner { namespace QmlDesigner {
const int NavigatorTreeModel::NavigatorRole = Qt::UserRole;
NavigatorTreeModel::NavigatorTreeModel(QObject *parent) NavigatorTreeModel::NavigatorTreeModel(QObject *parent)
: QStandardItemModel(parent), : QStandardItemModel(parent),
m_blockItemChangedSignal(false) m_blockItemChangedSignal(false)
...@@ -90,7 +97,7 @@ QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const ...@@ -90,7 +97,7 @@ QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const
continue; continue;
rowAlreadyUsedSet.insert(idIndex); rowAlreadyUsedSet.insert(idIndex);
stream << idIndex.data(Qt::UserRole).toUInt(); stream << idIndex.data(NavigatorRole).toUInt();
} }
mimeData->setData("application/vnd.modelnode.list", encodedData); mimeData->setData("application/vnd.modelnode.list", encodedData);
...@@ -98,21 +105,11 @@ QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const ...@@ -98,21 +105,11 @@ QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const
return mimeData; return mimeData;
} }
static bool isAnchestorInList(const ModelNode &node, const QList<ModelNode> &nodeList)
{
foreach (const ModelNode &nodeInList, nodeList) {
if (nodeInList.isAncestorOf(node))
return true;
}
return false;
}
bool NavigatorTreeModel::dropMimeData(const QMimeData *data, bool NavigatorTreeModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, Qt::DropAction action,
int row, int row,
int column, int column,
const QModelIndex &parentIndex) const QModelIndex &dropIndex)
{ {
if (action == Qt::IgnoreAction) if (action == Qt::IgnoreAction)
return true; return true;
...@@ -122,59 +119,57 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *data, ...@@ -122,59 +119,57 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *data,
return false; return false;
if (column > 1) if (column > 1)
return false; return false;
if (parentIndex.model() != this) if (dropIndex.model() != this)
return false; return false;
QModelIndex parentIndex, parentItemIndex;
QString parentPropertyName;
int targetIndex;
QModelIndex parentIdIndex = parentIndex; parentIndex = dropIndex.sibling(dropIndex.row(), 0);
parentIdIndex = parentIdIndex.sibling(parentIdIndex.row(), 0); targetIndex = (row > -1)? row : rowCount(parentIndex);
Q_ASSERT(parentIdIndex.isValid()); if (this->data(parentIndex, NavigatorRole).isValid()) {
parentItemIndex = parentIndex;
int targetIndex = 0; ModelNode parentNode = nodeForIndex(parentItemIndex);
if (row > -1) { if (!parentNode.metaInfo().hasDefaultProperty())
targetIndex = row; return false;
} else { targetIndex -= visibleProperties(parentNode).count();
targetIndex = rowCount(parentIdIndex); parentPropertyName = parentNode.metaInfo().defaultProperty();
}
else {
parentItemIndex = parentIndex.parent();
parentPropertyName = parentIndex.data(Qt::DisplayRole).toString();
} }
// Disallow dropping items between properties, which are listed first.
if (targetIndex < 0)
return false;
Q_ASSERT(parentItemIndex.isValid());
QByteArray encodedData = data->data("application/vnd.modelnode.list"); QByteArray encodedData = data->data("application/vnd.modelnode.list");
QDataStream stream(&encodedData, QIODevice::ReadOnly); QDataStream stream(&encodedData, QIODevice::ReadOnly);
ModelNode parentNode(nodeForIndex(parentIdIndex));
QList<ModelNode> nodeList; QList<ModelNode> nodeList;
while (!stream.atEnd()) { while (!stream.atEnd()) {
uint nodeHash; uint nodeHash;
stream >> nodeHash; stream >> nodeHash;
if (containsNodeHash(nodeHash)) {
ModelNode node(nodeForHash(nodeHash)); ModelNode node(nodeForHash(nodeHash));
if (!node.isValid() || (parentNode == node) || node.isAncestorOf(parentNode))
continue;
nodeList.append(node); nodeList.append(node);
} }
RewriterTransaction transaction = m_view->beginRewriterTransaction();
foreach (const ModelNode &node, nodeList) {
if (!isAnchestorInList(node, nodeList)) {
if (node.parentProperty().parentModelNode() != parentNode) {
if (node != parentNode) {
reparentModelNode(parentNode, node);
}
} }
if (node.parentProperty().isNodeListProperty()) { ModelNode parentNode(nodeForIndex(parentItemIndex));
int index = node.parentProperty().toNodeListProperty().toModelNodeList().indexOf(node); NodeAbstractProperty parentProperty = parentNode.nodeAbstractProperty(parentPropertyName);
if (index < targetIndex) { // item is first removed from oldIndex, then inserted at new index
--targetIndex; if (parentProperty.isNodeProperty() &&
} nodeList.count() > 1) {
if (index != targetIndex) { return false;
node.parentProperty().toNodeListProperty().slide(index, targetIndex);
}
}
}
} }
moveNodesInteractive(parentProperty, nodeList, targetIndex);
propagateInvisible(parentNode, isNodeInvisible(parentNode)); propagateInvisible(parentNode, isNodeInvisible(parentNode));
return false; // don't let the view do drag&drop on its own return false; // don't let the view do drag&drop on its own
...@@ -192,7 +187,7 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &n ...@@ -192,7 +187,7 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &n
idItem->setDragEnabled(true); idItem->setDragEnabled(true);
idItem->setDropEnabled(dropEnabled); idItem->setDropEnabled(dropEnabled);
idItem->setEditable(true); idItem->setEditable(true);
idItem->setData(hash, Qt::UserRole); idItem->setData(hash, NavigatorRole);
# ifdef _LOCK_ITEMS_ # ifdef _LOCK_ITEMS_
QStandardItem *lockItem = new QStandardItem; QStandardItem *lockItem = new QStandardItem;
...@@ -200,22 +195,34 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &n ...@@ -200,22 +195,34 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &n
lockItem->setDropEnabled(dropEnabled); lockItem->setDropEnabled(dropEnabled);
lockItem->setEditable(false); lockItem->setEditable(false);
lockItem->setCheckable(true); lockItem->setCheckable(true);
lockItem->setData(hash, Qt::UserRole); lockItem->setData(hash, NavigatorRole);
# endif # endif
QStandardItem *visibilityItem = new QStandardItem; QStandardItem *visibilityItem = new QStandardItem;
visibilityItem->setDropEnabled(dropEnabled); visibilityItem->setDropEnabled(dropEnabled);
visibilityItem->setCheckable(true); visibilityItem->setCheckable(true);
visibilityItem->setEditable(false); visibilityItem->setEditable(false);
visibilityItem->setData(hash, Qt::UserRole); visibilityItem->setData(hash, NavigatorRole);
if (node.isRootNode()) { if (node.isRootNode()) {
visibilityItem->setCheckable(false); visibilityItem->setCheckable(false);
} }
QMap<QString, QStandardItem *> propertyItems;
foreach (QString propertyName, visibleProperties(node)) {
QStandardItem *propertyItem = new QStandardItem;
propertyItem->setSelectable(false);
propertyItem->setDragEnabled(false);
propertyItem->setDropEnabled(dropEnabled);
propertyItem->setEditable(false);
propertyItem->setData(propertyName, Qt::DisplayRole);
propertyItems.insert(propertyName, propertyItem);
idItem->appendRow(propertyItem);
}
# ifdef _LOCK_ITEMS_ # ifdef _LOCK_ITEMS_
return ItemRow(idItem, lockItem, visibilityItem); return ItemRow(idItem, lockItem, visibilityItem, propertyItems);
# else # else
return ItemRow(idItem, visibilityItem); return ItemRow(idItem, visibilityItem, propertyItems);
# endif # endif
} }
...@@ -243,20 +250,34 @@ void NavigatorTreeModel::updateItemRow(const ModelNode &node) ...@@ -243,20 +250,34 @@ void NavigatorTreeModel::updateItemRow(const ModelNode &node)
/** /**
Updates the sibling position of the item, depending on the position in the model. Updates the sibling position of the item, depending on the position in the model.
*/ */
void NavigatorTreeModel::updateItemRowOrder(const ModelNode &node) void NavigatorTreeModel::updateItemRowOrder(const NodeListProperty &listProperty, const ModelNode &node, int oldIndex)
{ {
Q_UNUSED(oldIndex);
if (!containsNode(node)) if (!containsNode(node))
return; return;
ItemRow itemRow = itemRowForNode(node); ItemRow itemRow = itemRowForNode(node);
int currentRow = itemRow.idItem->row(); int currentRow = itemRow.idItem->row();
int newRow = currentRow; int newRow = listProperty.toModelNodeList().indexOf(node);
if (node.parentProperty().parentModelNode().isValid())
newRow = modelNodeChildren(node.parentProperty().parentModelNode()).indexOf(node);
Q_ASSERT(newRow >= 0); Q_ASSERT(newRow >= 0);
QStandardItem *parentIdItem = 0;
if (containsNode(listProperty.parentModelNode())) {
ItemRow parentRow = itemRowForNode(listProperty.parentModelNode());
parentIdItem = parentRow.propertyItems.value(listProperty.name());
if (!parentIdItem) {
parentIdItem = parentRow.idItem;
newRow += visibleProperties(listProperty.parentModelNode()).count();
}
}
else {
parentIdItem = itemRow.idItem->parent();
}
Q_ASSERT(parentIdItem);
if (currentRow != newRow) { if (currentRow != newRow) {
QStandardItem *parentIdItem = itemRow.idItem->parent();
QList<QStandardItem*> items = parentIdItem->takeRow(currentRow); QList<QStandardItem*> items = parentIdItem->takeRow(currentRow);
parentIdItem->insertRow(newRow, items); parentIdItem->insertRow(newRow, items);
} }
...@@ -266,8 +287,10 @@ void NavigatorTreeModel::handleChangedItem(QStandardItem *item) ...@@ -266,8 +287,10 @@ void NavigatorTreeModel::handleChangedItem(QStandardItem *item)
{ {
if (m_blockItemChangedSignal) if (m_blockItemChangedSignal)
return; return;
if (!data(item->index(), NavigatorRole).isValid())
return;
uint nodeHash = item->data(Qt::UserRole).toUInt(); uint nodeHash = item->data(NavigatorRole).toUInt();
Q_ASSERT(nodeHash && containsNodeHash(nodeHash)); Q_ASSERT(nodeHash && containsNodeHash(nodeHash));
ModelNode node = nodeForHash(nodeHash); ModelNode node = nodeForHash(nodeHash);
...@@ -323,7 +346,12 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode & ...@@ -323,7 +346,12 @@ NavigatorTreeModel::ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode &
void NavigatorTreeModel::setView(AbstractView *view) void NavigatorTreeModel::setView(AbstractView *view)
{ {
m_view = view; m_view = view;
m_hiddenProperties.clear();
if (view) {
ModelNode sampleItemNode(m_view->createModelNode("Qt/Item", 4, 7));
m_hiddenProperties << visibleProperties(sampleItemNode);
addSubTree(view->rootModelNode()); addSubTree(view->rootModelNode());
}
} }
void NavigatorTreeModel::clearView() void NavigatorTreeModel::clearView()
...@@ -346,7 +374,7 @@ QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const ...@@ -346,7 +374,7 @@ QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const
ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const
{ {
Q_ASSERT(index.isValid()); Q_ASSERT(index.isValid());
uint hash = index.data(Qt::UserRole).toUInt(); uint hash = index.data(NavigatorRole).toUInt();
Q_ASSERT(hash); Q_ASSERT(hash);
Q_ASSERT(containsNodeHash(hash)); Q_ASSERT(containsNodeHash(hash));
return nodeForHash(hash); return nodeForHash(hash);
...@@ -380,6 +408,8 @@ void NavigatorTreeModel::addSubTree(const ModelNode &node) ...@@ -380,6 +408,8 @@ void NavigatorTreeModel::addSubTree(const ModelNode &node)
Q_ASSERT(node.isValid()); Q_ASSERT(node.isValid());
Q_ASSERT(!containsNodeHash(qHash(node))); Q_ASSERT(!containsNodeHash(qHash(node)));
//updateItemRow(node, newRow);
// only add items that are in the modelNodeChildren list (that means, visible in the editor) // only add items that are in the modelNodeChildren list (that means, visible in the editor)
if (!node.isRootNode() if (!node.isRootNode()
&& !modelNodeChildren(node.parentProperty().parentModelNode()).contains(node)) { && !modelNodeChildren(node.parentProperty().parentModelNode()).contains(node)) {
...@@ -397,9 +427,16 @@ void NavigatorTreeModel::addSubTree(const ModelNode &node) ...@@ -397,9 +427,16 @@ void NavigatorTreeModel::addSubTree(const ModelNode &node)
// We assume that the node is always added to the _end_ of the property list. // We assume that the node is always added to the _end_ of the property list.
if (node.hasParentProperty()) { if (node.hasParentProperty()) {
ItemRow parentRow = itemRowForNode(node.parentProperty().parentModelNode()); AbstractProperty property(node.parentProperty());
if (parentRow.idItem) ItemRow parentRow = itemRowForNode(property.parentModelNode());
parentRow.idItem->appendRow(newRow.toList()); QStandardItem *parentItem = parentRow.propertyItems.value(property.name());
if (!parentItem) {
// Child nodes in the default property are added directly under the
// parent.
parentItem = parentRow.idItem;
}
if (parentItem)
parentItem->appendRow(newRow.toList());
} else { } else {
appendRow(newRow.toList()); appendRow(newRow.toList());
} }
...@@ -431,26 +468,111 @@ void NavigatorTreeModel::removeSubTree(const ModelNode &node) ...@@ -431,26 +468,111 @@ void NavigatorTreeModel::removeSubTree(const ModelNode &node)
m_nodeItemHash.remove(node); m_nodeItemHash.remove(node);
} }
void NavigatorTreeModel::reparentModelNode(const ModelNode &parentNode, const ModelNode &node) void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty parentProperty, const QList<ModelNode> &modelNodes, int targetIndex)
{ {
parentNode.nodeListProperty("data").reparentHere(node); QString propertyQmlType = qmlTypeInQtContainer(parentProperty.metaInfo().type());
RewriterTransaction transaction = m_view->beginRewriterTransaction();
foreach (const ModelNode &node, modelNodes) {
if (!node.isValid())
continue;
if (node != parentProperty.parentModelNode() &&
!node.isAncestorOf(parentProperty.parentModelNode()) &&
node.metaInfo().isSubclassOf(propertyQmlType, -1, -1)) {
if (node.parentProperty() != parentProperty) {
if (parentProperty.isNodeProperty()) {
ModelNode propertyNode = parentProperty.toNodeProperty().modelNode();
// Destruction of ancestors is not allowed
if (propertyNode.isAncestorOf(node)) {
continue;
}
if (propertyNode.isValid()) {
QApplication::setOverrideCursor(Qt::ArrowCursor);
if (QMessageBox::warning(0, tr("Warning"), tr("Reparenting the component %1 here will cause the component %2 to be deleted. Do you want to proceed?").arg(node.id(), propertyNode.id()), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
QApplication::restoreOverrideCursor();
continue;
}
QApplication::restoreOverrideCursor();
propertyNode.destroy();
}
}
parentProperty.reparentHere(node);
}
if (parentProperty.isNodeListProperty()) {
int index = parentProperty.toNodeListProperty().toModelNodeList().indexOf(node);
if (index < targetIndex) { // item is first removed from oldIndex, then inserted at new index
--targetIndex;
}
if (index != targetIndex) {
parentProperty.toNodeListProperty().slide(index, targetIndex);
}
}
}
}
} }
QList<ModelNode> NavigatorTreeModel::modelNodeChildren(const ModelNode &parentNode) QList<ModelNode> NavigatorTreeModel::modelNodeChildren(const ModelNode &parentNode)
{ {
QList<ModelNode> children; QList<ModelNode> children;
QStringList properties;
if (parentNode.hasProperty("children")) { if (parentNode.metaInfo().hasDefaultProperty())
children.append(parentNode.nodeListProperty("children").toModelNodeList()); properties << parentNode.metaInfo().defaultProperty();
}
properties << visibleProperties(parentNode);
if (parentNode.hasProperty("data")) { foreach (QString propertyName, properties) {
children.append(parentNode.nodeListProperty("data").toModelNodeList()); AbstractProperty property(parentNode.property(propertyName));
if (property.isNodeProperty())
children << property.toNodeProperty().modelNode();
else if (property.isNodeListProperty())
children << property.toNodeListProperty().toModelNodeList();
} }
return children; return children;
} }
QString NavigatorTreeModel::qmlTypeInQtContainer(const QString &qtContainerType) const
{
QString typeName(qtContainerType);
if (typeName.startsWith("QDeclarativeListProperty<") &&
typeName.endsWith(">")) {
typeName.remove(0, 25);
typeName.chop(1);
}
if (typeName.endsWith('*'))
typeName.chop(1);
return m_view->model()->metaInfo().fromQtTypes(typeName);
}
QStringList NavigatorTreeModel::visibleProperties(const ModelNode &node) const
{
QStringList propertyList;
foreach (PropertyMetaInfo propertyMetaInfo, node.metaInfo().properties().values()) {
if (!m_hiddenProperties.contains(propertyMetaInfo.name()) &&
propertyMetaInfo.name() != node.metaInfo().defaultProperty() &&
propertyMetaInfo.isReadable() &&
propertyMetaInfo.isWriteable()) {
QString qmlType = qmlTypeInQtContainer(propertyMetaInfo.type());
if (node.metaInfo().metaInfo().hasNodeMetaInfo(qmlType) &&
node.metaInfo().metaInfo().nodeMetaInfo(qmlType).isSubclassOf("QGraphicsObject", -1, -1)) {
propertyList << propertyMetaInfo.name();
}
}
}
return propertyList;
}
// along the lines of QObject::blockSignals // along the lines of QObject::blockSignals
bool NavigatorTreeModel::blockItemChangedSignal(bool block) bool NavigatorTreeModel::blockItemChangedSignal(bool block)
{ {
......
...@@ -50,8 +50,8 @@ class NavigatorTreeModel : public QStandardItemModel ...@@ -50,8 +50,8 @@ class NavigatorTreeModel : public QStandardItemModel
struct ItemRow { struct ItemRow {
ItemRow() ItemRow()
: idItem(0), lockItem(0), visibilityItem(0) {} : idItem(0), lockItem(0), visibilityItem(0) {}
ItemRow(QStandardItem *id, QStandardItem *lock, QStandardItem *visibility) ItemRow(QStandardItem *id, QStandardItem *lock, QStandardItem *visibility, const PropertyItemMap &properties)
: idItem(id), lockItem(lock), visibilityItem(visibility) {} : idItem(id), lockItem(lock), visibilityItem(visibility), propertyItems(properties) {}
QList<QStandardItem*> toList() const { QList<QStandardItem*> toList() const {
return QList<QStandardItem*>() << idItem << lockItem << visibilityItem; return QList<QStandardItem*>() << idItem << lockItem << visibilityItem;
...@@ -60,13 +60,14 @@ class NavigatorTreeModel : public QStandardItemModel ...@@ -60,13 +60,14 @@ class NavigatorTreeModel : public QStandardItemModel
QStandardItem *idItem; QStandardItem *idItem;
QStandardItem *lockItem; QStandardItem *lockItem;
QStandardItem *visibilityItem; QStandardItem *visibilityItem;
QMap<QString, QStandardItem *> propertyItems;
}; };
#else #else
struct ItemRow { struct ItemRow {
ItemRow() ItemRow()
: idItem(0), visibilityItem(0) {} : idItem(0), visibilityItem(0) {}
ItemRow(QStandardItem *id, QStandardItem *visibility) ItemRow(QStandardItem *id, QStandardItem *visibility, const QMap<QString, QStandardItem *> &properties)
: idItem(id), visibilityItem(visibility) {} : idItem(id), visibilityItem(visibility), propertyItems(properties) {}
QList<QStandardItem*> toList() const { QList<QStandardItem*> toList() const {
return QList<QStandardItem*>() << idItem << visibilityItem; return QList<QStandardItem*>() << idItem << visibilityItem;
...@@ -74,9 +75,12 @@ class NavigatorTreeModel : public QStandardItemModel ...@@ -74,9 +75,12 @@ class NavigatorTreeModel : public QStandardItemModel
QStandardItem *idItem; QStandardItem *idItem;
QStandardItem *visibilityItem; QStandardItem *visibilityItem;
QMap<QString, QStandardItem *> propertyItems;
}; };
#endif #endif
static const int NavigatorRole;
public: public:
NavigatorTreeModel(QObject *parent = 0); NavigatorTreeModel(QObject *parent = 0);