Commit 8c2467d4 authored by Thomas Hartmann's avatar Thomas Hartmann
Browse files

QmlDesigner: Reimplement navigator model



This patch gets rid of of QStandardItemModel.
Instead we implement a simple tree model without
any explicit structure. The model simply maps
the generic Model to a tree.

The advantage of the new model is that is does not use
a shadow data structure (QStandardItemModel) anymore.
The original model is always directly mapped to the tree model.
This makes initialization a lot faster and and there cannot
be any synchronization issues anymore.

Change-Id: I0e71ff2d4067f29a4c25c78ad3d626a5daf206a4
Reviewed-by: Tim Jenssen's avatarTim Jenssen <tim.jenssen@qt.io>
parent d79dde6d
......@@ -51,23 +51,15 @@ IconCheckboxItemDelegate::IconCheckboxItemDelegate(QObject *parent,
m_navigatorTreeModel(treeModel)
{}
static bool indexIsHolingModelNode(const QModelIndex &modelIndex)
{
return modelIndex.data(NavigatorTreeModel::InternalIdRole).isValid();
}
QSize IconCheckboxItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
const QModelIndex &modelIndex) const
const QModelIndex & /*modelIndex*/) const
{
if (indexIsHolingModelNode(modelIndex))
return QSize(15, 20);
return QSize();
return QSize(15, 20);
}
static bool isChecked(NavigatorTreeModel *navigatorTreeMode, const QModelIndex &modelIndex)
static bool isChecked(NavigatorTreeModel *navigatorTreeModel, const QModelIndex &modelIndex)
{
return navigatorTreeMode->itemFromIndex(modelIndex)->checkState() == Qt::Checked;
return navigatorTreeModel->data(modelIndex, Qt::CheckStateRole) == Qt::Checked;
}
void IconCheckboxItemDelegate::paint(QPainter *painter,
......@@ -77,23 +69,22 @@ void IconCheckboxItemDelegate::paint(QPainter *painter,
const int yOffset = (styleOption.rect.height()
- (m_checkedPixmap.height() / painter->device()->devicePixelRatio())) / 2;
const int xOffset = 2;
if (indexIsHolingModelNode(modelIndex)) {
painter->save();
if (styleOption.state & QStyle::State_Selected)
NavigatorTreeView::drawSelectionBackground(painter, styleOption);
if (!m_navigatorTreeModel->nodeForIndex(modelIndex).isRootNode()) {
painter->save();
if (styleOption.state & QStyle::State_Selected)
NavigatorTreeView::drawSelectionBackground(painter, styleOption);
if (m_navigatorTreeModel->isNodeInvisible(modelIndex))
painter->setOpacity(0.5);
if (!m_navigatorTreeModel->modelNodeForIndex(modelIndex).isRootNode()) {
const bool checked = isChecked(m_navigatorTreeModel, modelIndex);
painter->drawPixmap(styleOption.rect.x() + xOffset, styleOption.rect.y() + yOffset,
checked ? m_checkedPixmap : m_uncheckedPixmap);
}
if (m_navigatorTreeModel->isNodeVisible(modelIndex))
painter->setOpacity(0.5);
painter->restore();
const bool checked = isChecked(m_navigatorTreeModel, modelIndex);
painter->drawPixmap(styleOption.rect.x() + xOffset, styleOption.rect.y() + yOffset,
checked ? m_checkedPixmap : m_uncheckedPixmap);
}
painter->restore();
}
} // namespace QmlDesigner
......@@ -116,19 +116,16 @@ static int drawIcon(QPainter *painter, const QStyleOptionViewItem &styleOption,
}
static QRect drawText(QPainter *painter,
const QStyleOptionViewItem &styleOption,
const QModelIndex &modelIndex,
int iconOffset)
const QStyleOptionViewItem &styleOption,
const QModelIndex &modelIndex,
int iconOffset)
{
QString displayString = modelIndex.data(Qt::DisplayRole).toString();
if (displayString.isEmpty())
displayString = modelIndex.data(NavigatorTreeModel::SimplifiedTypeNameRole).toString();
displayString = modelIndex.data(Qt::DisplayRole).toString();
QPoint displayStringOffset;
int width = 0;
if (modelIndex.data(NavigatorTreeModel::InvisibleRole).toBool())
painter->setOpacity(0.5);
// Check text length does not exceed available space
int extraSpace = 12 + iconOffset;
......@@ -171,9 +168,12 @@ void NameItemDelegate::paint(QPainter *painter,
int iconOffset = drawIcon(painter, styleOption, modelIndex);
if (m_navigatorTreeModel->isNodeVisible(modelIndex))
painter->setOpacity(0.5);
QRect textFrame = drawText(painter, styleOption, modelIndex, iconOffset);
if (modelIndex.data(NavigatorTreeModel::ErrorRole).toBool())
if (m_navigatorTreeModel->hasError(modelIndex))
drawRedWavyUnderLine(painter, styleOption, textFrame);
painter->restore();
......@@ -196,7 +196,7 @@ QWidget *NameItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem & /*option*/,
const QModelIndex &index) const
{
if (!m_navigatorTreeModel->hasNodeForIndex(index))
if (!m_navigatorTreeModel->hasModelNodeForIndex(index))
return 0;
return new QLineEdit(parent);
......@@ -204,8 +204,8 @@ QWidget *NameItemDelegate::createEditor(QWidget *parent,
void NameItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
ModelNode node = m_navigatorTreeModel->nodeForIndex(index);
QString value = node.id();
const ModelNode node = m_navigatorTreeModel->modelNodeForIndex(index);
const QString value = node.id();
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(value);
......
......@@ -38,110 +38,71 @@ class Model;
class AbstractView;
class ModelNode;
#ifdef _LOCK_ITEMS_
struct ItemRow {
ItemRow()
: idItem(0), lockItem(0), visibilityItem(0) {}
ItemRow(QStandardItem *id, QStandardItem *lock, QStandardItem *visibility, const QMap<QString, QStandardItem *> &properties)
: idItem(id), lockItem(lock), visibilityItem(visibility), propertyItems(properties) {}
QList<QStandardItem*> toList() const {
return {idItem, lockItem, visibilityItem};
}
QStandardItem *idItem;
QStandardItem *lockItem;
QStandardItem *visibilityItem;
QMap<QString, QStandardItem *> propertyItems;
};
#else
struct ItemRow {
ItemRow()
: idItem(0), visibilityItem(0) {}
ItemRow(QStandardItem *id, QStandardItem *exportI, QStandardItem *visibility, const QMap<QString, QStandardItem *> &properties)
: idItem(id), exportItem(exportI), visibilityItem(visibility), propertyItems(properties) {}
QList<QStandardItem*> toList() const {
return {idItem, exportItem, visibilityItem};
}
QStandardItem *idItem;
QStandardItem *exportItem;
QStandardItem *visibilityItem;
QMap<QString, QStandardItem *> propertyItems;
};
#endif
class NavigatorTreeModel : public QStandardItemModel
class NavigatorTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum {
InternalIdRole = Qt::UserRole
,InvisibleRole = Qt::UserRole + 1
,SimplifiedTypeNameRole = Qt::UserRole + 2
,ErrorRole = Qt::UserRole + 3
};
NavigatorTreeModel(QObject *parent = 0);
explicit NavigatorTreeModel(QObject *parent = 0);
~NavigatorTreeModel();
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDragActions() const;
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
void setView(AbstractView *view);
ModelNode modelNodeForIndex(const QModelIndex &index) const;
bool hasModelNodeForIndex(const QModelIndex &index) const;
QStringList mimeTypes() const;
QMimeData *mimeData(const QModelIndexList &indexes) const;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
const QModelIndex &parent);
const QModelIndex &parent) override;
void setView(AbstractView *view);
void clearView();
QModelIndex indexForNode(const ModelNode &node) const;
ModelNode nodeForIndex(const QModelIndex &index) const;
bool hasNodeForIndex(const QModelIndex &index) const;
bool isInTree(const ModelNode &node) const;
bool isNodeInvisible(const QModelIndex &index) const;
bool isNodeInvisible(const ModelNode &node) const;
QModelIndex indexForModelNode(const ModelNode &node) const;
void addSubTree(const ModelNode &node);
void removeSubTree(const ModelNode &node);
void updateItemRow(const ModelNode &node);
QModelIndex createIndexFromModelNode(int row, int column, const ModelNode &modelNode) const;
void setId(const QModelIndex &index, const QString &id);
void setExported(const QModelIndex &index, bool exported);
void setVisible(const QModelIndex &index, bool visible);
void setId(const QModelIndex &index, const QString &newId);
void openContextMenu(const QPoint &position);
void openContextMenu(const QPoint &p);
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
ItemRow itemRowForNode(const ModelNode &node);
bool blockItemChangedSignal(bool block);
void notifyDataChanged(const ModelNode &modelNode);
void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes);
void notifyModelNodesInserted(const QList<ModelNode> &modelNodes);
void notifyModelNodesMoved(const QList<ModelNode> &modelNodes);
bool isNodeVisible(const ModelNode &modelNode) const;
bool isNodeVisible(const QModelIndex &index) const;
bool hasError(const QModelIndex &index) const;
private:
void handleChangedItem(QStandardItem *item);
ItemRow createItemRow(const ModelNode &node);
void updateItemRow(const ModelNode &node, ItemRow row);
void handleChangedIdItem(QStandardItem *idItem, ModelNode &modelNode);
void handleChangedExportItem(QStandardItem *exportItem, ModelNode &modelNode);
void handleChangedVisibilityItem(QStandardItem *visibilityItem, ModelNode &modelNode);
void handleChangedExport(const ModelNode &modelNode, bool exportItem);
void moveNodesInteractive(NodeAbstractProperty &parentProperty, const QList<ModelNode> &modelNodes, int targetIndex);
void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleItemLibraryImageDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
private:
QHash<ModelNode, ItemRow> m_nodeItemHash;
QPointer<AbstractView> m_view;
bool m_blockItemChangedSignal;
mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash;
};
} // namespace QmlDesigner
......@@ -31,10 +31,10 @@
#include "navigatortreemodel.h"
#include "qproxystyle.h"
#include "metainfo.h"
#include <metainfo.h>
#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
#include <utils/icon.h>
#include <utils/utilsicons.h>
#include <QLineEdit>
#include <QPen>
......@@ -67,17 +67,9 @@ public:
if (option->state & QStyle::State_MouseOver)
mouseOverStateSavedFrameRectangle = option->rect;
if (option->state & QStyle::State_Selected) {
if (option->state & QStyle::State_Selected)
NavigatorTreeView::drawSelectionBackground(painter, *option);
} else {
// // 3D shadows
// painter->save();
// painter->setPen(QColor(255, 255, 255, 15));
// painter->drawLine(option->rect.topLeft(), option->rect.topRight());
// painter->setPen(QColor(0, 0, 0, 25));
// painter->drawLine(option->rect.bottomLeft(),option->rect.bottomRight());
// painter->restore();
}
} else if (element == PE_IndicatorItemViewItemDrop) {
// between elements and on elements we have a width
if (option->rect.width() > 0) {
......@@ -171,6 +163,8 @@ NavigatorTreeView::NavigatorTreeView(QWidget *parent)
{
setStyle(new TableViewStyle(this));
setMinimumWidth(240);
setRootIsDecorated(false);
setIndentation(indentation() * 0.5);
}
void NavigatorTreeView::drawSelectionBackground(QPainter *painter, const QStyleOption &option)
......
......@@ -26,25 +26,28 @@
#include "navigatorview.h"
#include "navigatortreemodel.h"
#include "navigatorwidget.h"
#include "nameitemdelegate.h"
#include "iconcheckboxitemdelegate.h"
#include "qmldesignerconstants.h"
#include "qmldesignericons.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <utils/icon.h>
#include <utils/utilsicons.h>
#include "nameitemdelegate.h"
#include "iconcheckboxitemdelegate.h"
#include <bindingproperty.h>
#include <designmodecontext.h>
#include <nodeproperty.h>
#include <nodelistproperty.h>
#include <variantproperty.h>
#include <QHeaderView>
#include <qmlitemnode.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <utils/icon.h>
#include <utils/utilsicons.h>
#include <QHeaderView>
static inline void setScenePos(const QmlDesigner::ModelNode &modelNode,const QPointF &pos)
{
if (modelNode.hasParentProperty() && QmlDesigner::QmlItemNode::isValidQmlItemNode(modelNode.parentProperty().parentModelNode())) {
......@@ -64,14 +67,17 @@ static inline void setScenePos(const QmlDesigner::ModelNode &modelNode,const QPo
namespace QmlDesigner {
NavigatorView::NavigatorView(QObject* parent) :
AbstractView(parent),
m_blockSelectionChangedSignal(false),
m_widget(new NavigatorWidget(this)),
m_treeModel(new NavigatorTreeModel(this))
AbstractView(parent),
m_blockSelectionChangedSignal(false),
m_widget(new NavigatorWidget(this)),
m_treeModel(new NavigatorTreeModel(this))
{
#ifndef QMLDESIGNER_TEST
Internal::NavigatorContext *navigatorContext = new Internal::NavigatorContext(m_widget.data());
Core::ICore::addContextObject(navigatorContext);
#endif
m_treeModel->setView(this);
m_widget->setTreeModel(m_treeModel.data());
connect(treeWidget()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &NavigatorView::changeSelection);
......@@ -81,10 +87,8 @@ NavigatorView::NavigatorView(QObject* parent) :
connect(m_widget.data(), &NavigatorWidget::downButtonClicked, this, &NavigatorView::downButtonClicked);
connect(m_widget.data(), &NavigatorWidget::upButtonClicked, this, &NavigatorView::upButtonClicked);
treeWidget()->setIndentation(treeWidget()->indentation() * 0.5);
NameItemDelegate *idDelegate = new NameItemDelegate(this,
m_treeModel.data());
#ifndef QMLDESIGNER_TEST
NameItemDelegate *idDelegate = new NameItemDelegate(this, m_treeModel.data());
IconCheckboxItemDelegate *showDelegate =
new IconCheckboxItemDelegate(this,
Utils::Icons::EYE_OPEN_TOOLBAR.pixmap(),
......@@ -99,19 +103,20 @@ NavigatorView::NavigatorView(QObject* parent) :
#ifdef _LOCK_ITEMS_
IconCheckboxItemDelegate *lockDelegate = new IconCheckboxItemDelegate(this,":/qmldesigner/images/lock.png",
":/qmldesigner/images/hole.png",m_treeModel.data());
":/qmldesigner/images/hole.png",m_treeModel.data());
#endif
treeWidget()->setItemDelegateForColumn(0,idDelegate);
treeWidget()->setItemDelegateForColumn(0, idDelegate);
#ifdef _LOCK_ITEMS_
treeWidget()->setItemDelegateForColumn(1,lockDelegate);
treeWidget()->setItemDelegateForColumn(2,showDelegate);
#else
treeWidget()->setItemDelegateForColumn(1,exportDelegate);
treeWidget()->setItemDelegateForColumn(2,showDelegate);
treeWidget()->setItemDelegateForColumn(1, exportDelegate);
treeWidget()->setItemDelegateForColumn(2, showDelegate);
#endif
#endif //QMLDESIGNER_TEST
}
NavigatorView::~NavigatorView()
......@@ -138,14 +143,11 @@ void NavigatorView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
m_treeModel->setView(this);
QTreeView *treeView = treeWidget();
treeView->expandAll();
treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
treeView->header()->resizeSection(1,26);
treeView->setRootIsDecorated(false);
treeView->setIndentation(20);
#ifdef _LOCK_ITEMS_
treeView->header()->resizeSection(2,20);
......@@ -154,8 +156,6 @@ void NavigatorView::modelAttached(Model *model)
void NavigatorView::modelAboutToBeDetached(Model *model)
{
m_treeModel->removeSubTree(rootModelNode());
m_treeModel->clearView();
AbstractView::modelAboutToBeDetached(model);
}
......@@ -166,117 +166,91 @@ void NavigatorView::importsChanged(const QList<Import> &/*addedImports*/, const
void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & propertyList, PropertyChangeFlags /*propertyChange*/)
{
foreach (const BindingProperty &bindingProperty, propertyList) {
for (const BindingProperty &bindingProperty : propertyList) {
/* If a binding property that exports an item using an alias property has
* changed, we have to update the affected item.
*/
if (bindingProperty.isAliasExport())
m_treeModel->updateItemRow(modelNodeForId(bindingProperty.expression()));
m_treeModel->notifyDataChanged(modelNodeForId(bindingProperty.expression()));
}
}
void NavigatorView::nodeAboutToBeRemoved(const ModelNode &removedNode)
void NavigatorView::nodeAboutToBeRemoved(const ModelNode & /*removedNode*/)
{
m_treeModel->removeSubTree(removedNode);
}
void NavigatorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty & newPropertyParent, const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
void NavigatorView::nodeRemoved(const ModelNode &removedNode,
const NodeAbstractProperty & /*parentProperty*/,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
bool blocked = blockSelectionChangedSignal(true);
m_treeModel->removeSubTree(node);
if (node.isInHierarchy())
m_treeModel->addSubTree(node);
// make sure selection is in sync again
updateItemSelection();
if (newPropertyParent.parentModelNode().isValid()) {
QModelIndex index = m_treeModel->indexForNode(newPropertyParent.parentModelNode());
treeWidget()->expand(index);
}
m_treeModel->notifyModelNodesRemoved({removedNode});
}
blockSelectionChangedSignal(blocked);
void NavigatorView::nodeReparented(const ModelNode &modelNode,
const NodeAbstractProperty & /*newPropertyParent*/,
const NodeAbstractProperty & oldPropertyParent,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
if (!oldPropertyParent.isValid())
m_treeModel->notifyModelNodesInserted({modelNode});
else
m_treeModel->notifyModelNodesMoved({modelNode});
treeWidget()->expand(m_treeModel->indexForModelNode(modelNode));
}
void NavigatorView::nodeIdChanged(const ModelNode& node, const QString & /*newId*/, const QString & /*oldId*/)
void NavigatorView::nodeIdChanged(const ModelNode& modelNode, const QString & /*newId*/, const QString & /*oldId*/)
{
if (m_treeModel->isInTree(node))
m_treeModel->updateItemRow(node);
m_treeModel->notifyDataChanged(modelNode);
}
void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList)
void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/)
{
foreach (const AbstractProperty &property, propertyList) {
if (property.isNodeAbstractProperty()) {
NodeAbstractProperty nodeAbstractProperty(property.toNodeListProperty());
foreach (const ModelNode &childNode, nodeAbstractProperty.directSubNodes()) {
m_treeModel->removeSubTree(childNode);
}
}
}
}
void NavigatorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
QList<ModelNode> modelNodes;
for (const AbstractProperty &property : propertyList) {
ModelNode node = property.parentModelNode();
/* Update export alias state if property from root note was removed and the root node was not selected. */
/* The check for the selection is an optimization to reduce updates. */
if (node.isRootNode() && !selectedModelNodes().isEmpty() && !selectedModelNodes().first().isRootNode()) {
foreach (const ModelNode &modelNode, node.allSubModelNodes())
m_treeModel->updateItemRow(modelNode);
if (property.isNodeAbstractProperty()) {
NodeAbstractProperty nodeAbstractProperty(property.toNodeListProperty());
modelNodes.append(nodeAbstractProperty.directSubNodes());
}
}
m_treeModel->notifyModelNodesRemoved(modelNodes);
}
void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/)
{
if (m_treeModel->isInTree(rootModelNode()))
m_treeModel->updateItemRow(rootModelNode());
m_treeModel->notifyDataChanged(rootModelNode());
}
void NavigatorView::nodeTypeChanged(const ModelNode &node, const TypeName &, int , int)
void NavigatorView::nodeTypeChanged(const ModelNode &modelNode, const TypeName &, int , int)
{
if (m_treeModel->isInTree(node))
m_treeModel->updateItemRow(node);
m_treeModel->notifyDataChanged(modelNode);
}
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, const PropertyName & name, const QVariant & /*data*/)
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode,
const PropertyName & /*name*/,
const QVariant & /*data*/)
{
if (name == "invisible" && m_treeModel->isInTree(modelNode))
{
// update model
m_treeModel->updateItemRow(modelNode);
// repaint row (id and icon)
foreach (const ModelNode &currentModelNode, modelNode.allSubModelNodesAndThisNode()) {
QModelIndex index = m_treeModel->indexForNode(currentModelNode);
treeWidget()->update(index);
treeWidget()->update(index.sibling(index.row(),index.column()+1));
}
}
m_treeModel->notifyDataChanged(modelNode);
}
void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList)
{
foreach (const ModelNode &currentModelNode, errorNodeList)
m_treeModel->updateItemRow(currentModelNode);
foreach (const ModelNode &modelNode, errorNodeList)
m_treeModel->notifyDataChanged(modelNode);
}