QmlDesigner: Fix the bug for dropping ListView when DataStore is missing

* Creating ListView will be postponed when the dataStore is just created
so that the model manager can update the information of DataStore.
* The initial delegate and model for the listview are faked in order to
show the same shape before and after the assignment.

Change-Id: I45ac7486890556136ca98fc131f90896efc3b839
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2024-02-19 15:18:24 +02:00
parent cae86d6977
commit 3e81485c6a
6 changed files with 169 additions and 55 deletions

View File

@@ -347,12 +347,6 @@ bool ensureDataStoreExists(bool &justCreated)
if (qmlDirSaver.finalize()) { if (qmlDirSaver.finalize()) {
justCreated = true; justCreated = true;
// Force code model reset to notice changes to existing module
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager)
modelManager->resetCodeModel();
return true; return true;
} }

View File

@@ -19,15 +19,16 @@
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace { namespace {
inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
@@ -139,6 +140,12 @@ void CollectionView::modelAttached(Model *model)
resetDataStoreNode(); resetDataStoreNode();
} }
void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model)
{
m_libraryInfoIsUpdated = false;
disconnect(m_documentUpdateConnection);
}
void CollectionView::nodeReparented(const ModelNode &node, void CollectionView::nodeReparented(const ModelNode &node,
[[maybe_unused]] const NodeAbstractProperty &newPropertyParent, [[maybe_unused]] const NodeAbstractProperty &newPropertyParent,
[[maybe_unused]] const NodeAbstractProperty &oldPropertyParent, [[maybe_unused]] const NodeAbstractProperty &oldPropertyParent,
@@ -292,6 +299,10 @@ void CollectionView::assignCollectionToNode(const QString &collectionName, const
} }
NodeProperty delegateProperty = node.nodeProperty("delegate"); NodeProperty delegateProperty = node.nodeProperty("delegate");
// Remove the old model node if is available
if (delegateProperty.modelNode())
delegateProperty.modelNode().destroy();
delegateProperty.setModelNode(rowItem); delegateProperty.setModelNode(rowItem);
} }
}); });
@@ -324,8 +335,29 @@ void CollectionView::ensureDataStoreExists()
{ {
bool filesJustCreated = false; bool filesJustCreated = false;
bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
if (filesExist && filesJustCreated) if (filesExist) {
resetDataStoreNode(); if (filesJustCreated) {
// Force code model reset to notice changes to existing module
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager) {
m_libraryInfoIsUpdated = false;
m_expectedDocumentUpdates.clear();
m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath()
<< CollectionEditorUtils::dataStoreJsonFilePath();
m_documentUpdateConnection = connect(modelManager,
&QmlJS::ModelManagerInterface::documentUpdated,
this,
&CollectionView::onDocumentUpdated);
modelManager->resetCodeModel();
}
resetDataStoreNode();
} else {
m_libraryInfoIsUpdated = true;
}
}
} }
QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const
@@ -387,9 +419,62 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node)
sourceModel->addCollectionToSource(dataStoreNode(), sourceModel->addCollectionToSource(dataStoreNode(),
newCollectionName, newCollectionName,
CollectionEditorUtils::defaultColorCollection()); CollectionEditorUtils::defaultColorCollection());
assignCollectionToNode(newCollectionName, node);
m_widget->openCollection(newCollectionName); m_widget->openCollection(newCollectionName);
new DelayedAssignCollectionToItem(this, node, newCollectionName);
} }
} }
void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc)
{
if (m_expectedDocumentUpdates.contains(doc->fileName()))
m_expectedDocumentUpdates.remove(doc->fileName());
if (m_expectedDocumentUpdates.isEmpty()) {
disconnect(m_documentUpdateConnection);
m_libraryInfoIsUpdated = true;
}
}
DelayedAssignCollectionToItem::DelayedAssignCollectionToItem(CollectionView *parent,
const ModelNode &node,
const QString &collectionName)
: QObject(parent)
, m_collectionView(parent)
, m_node(node)
, m_name(collectionName)
{
checkAndAssign();
}
void DelayedAssignCollectionToItem::checkAndAssign()
{
AbstractView *view = m_node.view();
if (!m_node || !m_collectionView || !view || ++m_counter > 50) {
deleteLater();
return;
}
bool dataStoreFound = false;
if (m_collectionView->isDataStoreReady()) {
for (const QmlTypeData &cppTypeData : view->rewriterView()->getQMLTypes()) {
if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore")
dataStoreFound = true;
}
if (!dataStoreFound && !m_rewriterAmended) {
m_collectionView->model()->rewriterView()->forceAmend();
m_rewriterAmended = true;
}
}
if (!dataStoreFound) {
QTimer::singleShot(100, this, &DelayedAssignCollectionToItem::checkAndAssign);
return;
}
m_collectionView->assignCollectionToNode(m_name, m_node);
deleteLater();
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -7,6 +7,9 @@
#include "datastoremodelnode.h" #include "datastoremodelnode.h"
#include "modelnode.h" #include "modelnode.h"
namespace QmlJS {
class Document;
}
namespace QmlDesigner { namespace QmlDesigner {
class CollectionWidget; class CollectionWidget;
@@ -23,6 +26,7 @@ public:
WidgetInfo widgetInfo() override; WidgetInfo widgetInfo() override;
void modelAttached(Model *model) override; void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void nodeReparented(const ModelNode &node, void nodeReparented(const ModelNode &node,
const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &newPropertyParent,
@@ -58,14 +62,41 @@ public:
void ensureDataStoreExists(); void ensureDataStoreExists();
QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const;
bool isDataStoreReady() const { return m_libraryInfoIsUpdated; }
private: private:
void refreshModel(); void refreshModel();
NodeMetaInfo jsonCollectionMetaInfo() const; NodeMetaInfo jsonCollectionMetaInfo() const;
NodeMetaInfo csvCollectionMetaInfo() const; NodeMetaInfo csvCollectionMetaInfo() const;
void ensureStudioModelImport(); void ensureStudioModelImport();
void onItemLibraryNodeCreated(const ModelNode &node); void onItemLibraryNodeCreated(const ModelNode &node);
void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc);
QPointer<CollectionWidget> m_widget; QPointer<CollectionWidget> m_widget;
std::unique_ptr<DataStoreModelNode> m_dataStore; std::unique_ptr<DataStoreModelNode> m_dataStore;
QSet<Utils::FilePath> m_expectedDocumentUpdates;
QMetaObject::Connection m_documentUpdateConnection;
bool m_libraryInfoIsUpdated = false;
}; };
class DelayedAssignCollectionToItem : public QObject
{
Q_OBJECT
public:
DelayedAssignCollectionToItem(CollectionView *parent,
const ModelNode &node,
const QString &collectionName);
public slots:
void checkAndAssign();
private:
QPointer<CollectionView> m_collectionView;
ModelNode m_node;
QString m_name;
int m_counter = 0;
bool m_rewriterAmended = false;
};
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -459,8 +459,16 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
view->executeInTransaction("assignCollectionToNode", [&]() { view->executeInTransaction("assignCollectionToNode", [&]() {
QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name()));
// Remove the old model node property if exists
NodeProperty modelNodeProperty = targetNode.nodeProperty("model");
if (modelNodeProperty.modelNode())
modelNodeProperty.modelNode().destroy();
// Assign the collection to the node
BindingProperty modelProperty = targetNode.bindingProperty("model"); BindingProperty modelProperty = targetNode.bindingProperty("model");
modelProperty.setExpression(identifier); modelProperty.setExpression(identifier);
if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) {
VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); VariantProperty textRoleProperty = targetNode.variantProperty("textRole");
const QVariant currentTextRoleValue = textRoleProperty.value(); const QVariant currentTextRoleValue = textRoleProperty.value();

View File

@@ -4,40 +4,38 @@
import QtQuick 1.0 import QtQuick 1.0
ListView { ListView {
width: 110 width: 160
height: 160 height: 80
model: ListModel { model: ListModel {
ListElement {
name: "Grey"
colorCode: "grey"
}
ListElement { ListElement {
name: "Red" name: "Red"
colorCode: "red" colorCode: "red"
} }
ListElement {
name: "Green"
colorCode: "green"
}
ListElement { ListElement {
name: "Blue" name: "Blue"
colorCode: "blue" colorCode: "blue"
} }
ListElement { ListElement {
name: "Green" name: "White"
colorCode: "green" colorCode: "white"
} }
} }
delegate: Item { delegate: Row {
width: 80 spacing: 5
height: 40 Rectangle {
x: 5 width: 100
Row { height: 20
id: row1 color: colorCode
spacing: 10 }
Rectangle { width: 40; height: 40; color: colorCode; }
Text { Text {
text: name width: 100
anchors.verticalCenter: parent.verticalCenter text: name
font.bold: true
}
} }
} }
} }

View File

@@ -4,40 +4,38 @@
import QtQuick 2.0 import QtQuick 2.0
ListView { ListView {
width: 110 width: 160
height: 160 height: 80
model: ListModel { model: ListModel {
ListElement {
name: "Grey"
colorCode: "grey"
}
ListElement { ListElement {
name: "Red" name: "Red"
colorCode: "red" colorCode: "red"
} }
ListElement {
name: "Green"
colorCode: "green"
}
ListElement { ListElement {
name: "Blue" name: "Blue"
colorCode: "blue" colorCode: "blue"
} }
ListElement { ListElement {
name: "Green" name: "White"
colorCode: "green" colorCode: "white"
} }
} }
delegate: Item { delegate: Row {
width: 80 spacing: 5
height: 40 Rectangle {
x: 5 width: 100
Row { height: 20
id: row1 color: colorCode
spacing: 10 }
Rectangle { width: 40; height: 40; color: colorCode; }
Text { Text {
text: name width: 100
anchors.verticalCenter: parent.verticalCenter text: name
font.bold: true
}
} }
} }
} }