forked from qt-creator/qt-creator
QmlDesigner: Add persistent auxiliary storage
The persistent storage is saving the data in-between program executions. A sqlite database is providing the backend. The model nodes need an id to be identified. Change-Id: I24e4ea5184c04cb6a9e3828059ca593ee41d271e Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -20,7 +20,8 @@ enum class AuxiliaryDataType {
|
|||||||
Temporary,
|
Temporary,
|
||||||
Document,
|
Document,
|
||||||
NodeInstancePropertyOverwrite,
|
NodeInstancePropertyOverwrite,
|
||||||
NodeInstanceAuxiliary
|
NodeInstanceAuxiliary,
|
||||||
|
Persistent
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class View3DActionType {
|
enum class View3DActionType {
|
||||||
|
@@ -361,6 +361,7 @@ extend_qtc_library(QmlDesignerCore
|
|||||||
abstractview.cpp
|
abstractview.cpp
|
||||||
anchorline.cpp
|
anchorline.cpp
|
||||||
annotation.cpp
|
annotation.cpp
|
||||||
|
auxiliarypropertystorageview.cpp auxiliarypropertystorageview.h
|
||||||
bindingproperty.cpp
|
bindingproperty.cpp
|
||||||
componenttextmodifier.cpp
|
componenttextmodifier.cpp
|
||||||
documentmessage.cpp
|
documentmessage.cpp
|
||||||
|
@@ -20,17 +20,19 @@
|
|||||||
#include <itemlibraryview.h>
|
#include <itemlibraryview.h>
|
||||||
#include <materialbrowserview.h>
|
#include <materialbrowserview.h>
|
||||||
#include <materialeditorview.h>
|
#include <materialeditorview.h>
|
||||||
|
#include <model/auxiliarypropertystorageview.h>
|
||||||
#include <navigatorview.h>
|
#include <navigatorview.h>
|
||||||
#include <nodeinstanceview.h>
|
#include <nodeinstanceview.h>
|
||||||
#include <propertyeditorview.h>
|
#include <propertyeditorview.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
#include <rewriterview.h>
|
#include <rewriterview.h>
|
||||||
#include <stateseditorview.h>
|
#include <stateseditorview.h>
|
||||||
#include <texteditorview.h>
|
#include <texteditorview.h>
|
||||||
#include <textureeditorview.h>
|
#include <textureeditorview.h>
|
||||||
#include <qmldesignerplugin.h>
|
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <sqlitedatabase.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
#include <advanceddockingsystem/dockwidget.h>
|
#include <advanceddockingsystem/dockwidget.h>
|
||||||
@@ -49,6 +51,7 @@ public:
|
|||||||
ViewManagerData(AsynchronousImageCache &imageCache,
|
ViewManagerData(AsynchronousImageCache &imageCache,
|
||||||
ExternalDependenciesInterface &externalDependencies)
|
ExternalDependenciesInterface &externalDependencies)
|
||||||
: debugView{externalDependencies}
|
: debugView{externalDependencies}
|
||||||
|
, auxiliaryDataKeyView{auxiliaryDataDatabase, externalDependencies}
|
||||||
, designerActionManagerView{externalDependencies}
|
, designerActionManagerView{externalDependencies}
|
||||||
, nodeInstanceView(QCoreApplication::arguments().contains("-capture-puppet-stream")
|
, nodeInstanceView(QCoreApplication::arguments().contains("-capture-puppet-stream")
|
||||||
? capturingConnectionManager
|
? capturingConnectionManager
|
||||||
@@ -78,6 +81,11 @@ public:
|
|||||||
CapturingConnectionManager capturingConnectionManager;
|
CapturingConnectionManager capturingConnectionManager;
|
||||||
QmlModelState savedState;
|
QmlModelState savedState;
|
||||||
Internal::DebugView debugView;
|
Internal::DebugView debugView;
|
||||||
|
Sqlite::Database auxiliaryDataDatabase{
|
||||||
|
Utils::PathString{Core::ICore::userResourcePath("auxiliary_data.db").toString()},
|
||||||
|
Sqlite::JournalMode::Wal,
|
||||||
|
Sqlite::LockingMode::Normal};
|
||||||
|
AuxiliaryPropertyStorageView auxiliaryDataKeyView;
|
||||||
DesignerActionManagerView designerActionManagerView;
|
DesignerActionManagerView designerActionManagerView;
|
||||||
NodeInstanceView nodeInstanceView;
|
NodeInstanceView nodeInstanceView;
|
||||||
ContentLibraryView contentLibraryView;
|
ContentLibraryView contentLibraryView;
|
||||||
@@ -201,7 +209,8 @@ QList<AbstractView *> ViewManager::views() const
|
|||||||
QList<AbstractView *> ViewManager::standardViews() const
|
QList<AbstractView *> ViewManager::standardViews() const
|
||||||
{
|
{
|
||||||
#ifndef QTC_USE_QML_DESIGNER_LITE
|
#ifndef QTC_USE_QML_DESIGNER_LITE
|
||||||
QList<AbstractView *> list = {&d->edit3DView,
|
QList<AbstractView *> list = {&d->auxiliaryDataKeyView,
|
||||||
|
&d->edit3DView,
|
||||||
&d->formEditorView,
|
&d->formEditorView,
|
||||||
&d->textEditorView,
|
&d->textEditorView,
|
||||||
&d->assetsLibraryView,
|
&d->assetsLibraryView,
|
||||||
|
@@ -244,6 +244,9 @@ QTextStream &operator<<(QTextStream &stream, AuxiliaryDataType type)
|
|||||||
case AuxiliaryDataType::Temporary:
|
case AuxiliaryDataType::Temporary:
|
||||||
stream << "Temporary";
|
stream << "Temporary";
|
||||||
break;
|
break;
|
||||||
|
case AuxiliaryDataType::Persistent:
|
||||||
|
stream << "Persistent";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
|
@@ -249,6 +249,7 @@ public:
|
|||||||
const QList<DocumentMessage> &warnings);
|
const QList<DocumentMessage> &warnings);
|
||||||
|
|
||||||
QList<ModelNode> selectedNodes(AbstractView *view) const;
|
QList<ModelNode> selectedNodes(AbstractView *view) const;
|
||||||
|
void setSelectedModelNodes(const QList<ModelNode> &selectedNodeList);
|
||||||
|
|
||||||
void clearMetaInfoCache();
|
void clearMetaInfoCache();
|
||||||
|
|
||||||
|
@@ -152,10 +152,10 @@ public:
|
|||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
QString id() const;
|
QString id() const;
|
||||||
void ensureIdExists();
|
void ensureIdExists() const;
|
||||||
[[nodiscard]] QString validId();
|
[[nodiscard]] QString validId() const;
|
||||||
void setIdWithRefactoring(const QString &id);
|
void setIdWithRefactoring(const QString &id) const;
|
||||||
void setIdWithoutRefactoring(const QString &id);
|
void setIdWithoutRefactoring(const QString &id) const;
|
||||||
static bool isValidId(const QString &id);
|
static bool isValidId(const QString &id);
|
||||||
static QString getIdValidityErrorMessage(const QString &id);
|
static QString getIdValidityErrorMessage(const QString &id);
|
||||||
|
|
||||||
@@ -192,6 +192,7 @@ public:
|
|||||||
void removeAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
void removeAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
||||||
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
||||||
bool hasAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
bool hasAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
||||||
|
bool hasAuxiliaryData(AuxiliaryDataType type) const;
|
||||||
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
||||||
AuxiliaryDatasView auxiliaryData() const;
|
AuxiliaryDatasView auxiliaryData() const;
|
||||||
|
|
||||||
|
@@ -427,14 +427,7 @@ QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode>
|
|||||||
*/
|
*/
|
||||||
void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList)
|
void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList)
|
||||||
{
|
{
|
||||||
QList<ModelNode> unlockedNodes;
|
model()->setSelectedModelNodes(selectedNodeList);
|
||||||
|
|
||||||
for (const auto &modelNode : selectedNodeList) {
|
|
||||||
if (!ModelUtils::isThisOrAncestorLocked(modelNode))
|
|
||||||
unlockedNodes.push_back(modelNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
model()->d->setSelectedNodes(toInternalNodeList(unlockedNodes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractView::setSelectedModelNode(const ModelNode &modelNode)
|
void AbstractView::setSelectedModelNode(const ModelNode &modelNode)
|
||||||
|
@@ -0,0 +1,201 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "auxiliarypropertystorageview.h"
|
||||||
|
|
||||||
|
#include <sqlitedatabase.h>
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QIODevice>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct Initializer
|
||||||
|
{
|
||||||
|
Initializer(Sqlite::Database &database, bool isInitialized)
|
||||||
|
{
|
||||||
|
if (!isInitialized) {
|
||||||
|
createAuxiliaryPropertiesTable(database);
|
||||||
|
}
|
||||||
|
database.setIsInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createAuxiliaryPropertiesTable(Sqlite::Database &database)
|
||||||
|
{
|
||||||
|
Sqlite::StrictTable table;
|
||||||
|
table.setUseIfNotExists(true);
|
||||||
|
table.setUseWithoutRowId(true);
|
||||||
|
table.setName("auxiliaryProperties");
|
||||||
|
auto &filePathColumn = table.addColumn("filePath", Sqlite::StrictColumnType::Text);
|
||||||
|
auto &idColumn = table.addColumn("id", Sqlite::StrictColumnType::Text);
|
||||||
|
auto &keyColumn = table.addColumn("key", Sqlite::StrictColumnType::Text);
|
||||||
|
table.addColumn("value", Sqlite::StrictColumnType::Blob);
|
||||||
|
|
||||||
|
table.addPrimaryKeyContraint({filePathColumn, idColumn, keyColumn});
|
||||||
|
|
||||||
|
table.initialize(database);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
Entry(Utils::SmallStringView nodeId, Utils::SmallStringView key, Sqlite::BlobView value)
|
||||||
|
: nodeId{nodeId}
|
||||||
|
, key{key}
|
||||||
|
, value{value}
|
||||||
|
{}
|
||||||
|
|
||||||
|
Utils::SmallStringView nodeId;
|
||||||
|
Utils::SmallStringView key;
|
||||||
|
Sqlite::BlobView value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChangeEntry
|
||||||
|
{
|
||||||
|
ChangeEntry(Utils::SmallStringView id, Utils::SmallStringView name)
|
||||||
|
: id{id}
|
||||||
|
, name{name}
|
||||||
|
{}
|
||||||
|
|
||||||
|
friend bool operator<(const ChangeEntry &first, const ChangeEntry &second)
|
||||||
|
{
|
||||||
|
return std::tie(first.id, first.name) < std::tie(second.id, second.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::SmallString id;
|
||||||
|
Utils::SmallString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct AuxiliaryPropertyStorageView::Data
|
||||||
|
{
|
||||||
|
Data(Sqlite::Database &database)
|
||||||
|
: database{database}
|
||||||
|
{
|
||||||
|
exclusiveTransaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Sqlite::Database &database;
|
||||||
|
Sqlite::ExclusiveNonThrowingDestructorTransaction<Sqlite::Database> exclusiveTransaction{database};
|
||||||
|
Initializer initializer{database, database.isInitialized()};
|
||||||
|
Sqlite::WriteStatement<4> upsertValue{
|
||||||
|
"INSERT INTO auxiliaryProperties(filePath, id, key, value) "
|
||||||
|
"VALUES(?1, ?2, ?3, ?4) "
|
||||||
|
"ON CONFLICT DO UPDATE SET value=excluded.value",
|
||||||
|
database};
|
||||||
|
Sqlite::ReadStatement<3, 1> selectValues{
|
||||||
|
"SELECT id, key, value FROM auxiliaryProperties WHERE filePath=?1", database};
|
||||||
|
Sqlite::WriteStatement<0> removeValues{"DELETE FROM auxiliaryProperties", database};
|
||||||
|
Sqlite::WriteStatement<3> removeValue{
|
||||||
|
"DELETE FROM auxiliaryProperties WHERE filePath=?1 AND id=?2 AND key=?3", database};
|
||||||
|
std::set<ChangeEntry, std::less<>> changeEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuxiliaryPropertyStorageView::AuxiliaryPropertyStorageView(
|
||||||
|
Sqlite::Database &database, ExternalDependenciesInterface &externalDependencies)
|
||||||
|
: AbstractView{externalDependencies}
|
||||||
|
, d{std::make_unique<Data>(database)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
AuxiliaryPropertyStorageView::~AuxiliaryPropertyStorageView() = default;
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::modelAttached(Model *model)
|
||||||
|
{
|
||||||
|
auto entryRange = d->selectValues.rangeWithTransaction<Entry>(
|
||||||
|
Utils::PathString{model->fileUrl().path()});
|
||||||
|
|
||||||
|
for (const auto entry : entryRange) {
|
||||||
|
auto node = model->modelNodeForId(QString{entry.nodeId});
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto array = QByteArray::fromRawData(entry.value.cdata(), entry.value.ssize());
|
||||||
|
QDataStream dataStream{array};
|
||||||
|
|
||||||
|
QVariant value;
|
||||||
|
dataStream >> value;
|
||||||
|
|
||||||
|
node.setAuxiliaryDataWithoutLock({AuxiliaryDataType::Persistent, entry.key}, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractView::modelAttached(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::modelAboutToBeDetached(Model *model)
|
||||||
|
{
|
||||||
|
Utils::PathString filePath{model->fileUrl().path()};
|
||||||
|
|
||||||
|
auto update = [&] {
|
||||||
|
for (const auto &[id, name] : d->changeEntries) {
|
||||||
|
QByteArray array;
|
||||||
|
QDataStream dataStream{&array, QIODevice::WriteOnly};
|
||||||
|
auto node = model->modelNodeForId(id.toQString());
|
||||||
|
|
||||||
|
auto value = node.auxiliaryData(AuxiliaryDataType::Persistent, name);
|
||||||
|
if (value) {
|
||||||
|
dataStream << *value;
|
||||||
|
|
||||||
|
Sqlite::BlobView blob{array};
|
||||||
|
|
||||||
|
d->upsertValue.write(filePath, id, name, blob);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
d->removeValue.write(filePath, id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (d->changeEntries.size()) {
|
||||||
|
Sqlite::withImmediateTransaction(d->database, update);
|
||||||
|
|
||||||
|
d->changeEntries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::nodeIdChanged(const ModelNode &node,
|
||||||
|
const QString &newIdArg,
|
||||||
|
const QString &oldIdArg)
|
||||||
|
{
|
||||||
|
Utils::SmallString newId = newIdArg;
|
||||||
|
Utils::SmallString oldId = oldIdArg;
|
||||||
|
for (auto entry : node.auxiliaryData()) {
|
||||||
|
if (entry.first.type != AuxiliaryDataType::Persistent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
d->changeEntries.emplace(oldId, entry.first.name);
|
||||||
|
d->changeEntries.emplace(newId, entry.first.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
||||||
|
{
|
||||||
|
if (removedNode.hasId()) {
|
||||||
|
Utils::SmallString id = removedNode.id();
|
||||||
|
for (auto entry : removedNode.auxiliaryData()) {
|
||||||
|
if (entry.first.type != AuxiliaryDataType::Persistent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
d->changeEntries.emplace(id, entry.first.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::auxiliaryDataChanged(const ModelNode &node,
|
||||||
|
AuxiliaryDataKeyView key,
|
||||||
|
const QVariant &)
|
||||||
|
{
|
||||||
|
if (isAttached() && key.type == AuxiliaryDataType::Persistent)
|
||||||
|
d->changeEntries.emplace(Utils::SmallString{node.id()}, key.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuxiliaryPropertyStorageView::resetForTestsOnly()
|
||||||
|
{
|
||||||
|
Sqlite::withImmediateTransaction(d->database, [&] { d->removeValues.execute(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
class AuxiliaryPropertyStorageView final : public AbstractView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AuxiliaryPropertyStorageView(Sqlite::Database &database,
|
||||||
|
ExternalDependenciesInterface &externalDependencies);
|
||||||
|
~AuxiliaryPropertyStorageView();
|
||||||
|
|
||||||
|
void modelAttached(Model *model) override;
|
||||||
|
void modelAboutToBeDetached(Model *model) override;
|
||||||
|
|
||||||
|
void nodeIdChanged(const ModelNode &node, const QString &newId, const QString &oldId) override;
|
||||||
|
|
||||||
|
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
||||||
|
|
||||||
|
void auxiliaryDataChanged(const ModelNode &node,
|
||||||
|
AuxiliaryDataKeyView type,
|
||||||
|
const QVariant &data) override;
|
||||||
|
|
||||||
|
struct Data;
|
||||||
|
|
||||||
|
void resetForTestsOnly();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Data> d;
|
||||||
|
};
|
||||||
|
} // namespace QmlDesigner
|
@@ -51,6 +51,14 @@ auto find(Type &&auxiliaryDatas, AuxiliaryDataKeyView key)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
auto find(Type &&auxiliaryDatas, AuxiliaryDataType type)
|
||||||
|
{
|
||||||
|
return std::find_if(auxiliaryDatas.begin(), auxiliaryDatas.end(), [&](const auto &element) {
|
||||||
|
return element.first.type == type;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::optional<QVariant> InternalNode::auxiliaryData(AuxiliaryDataKeyView key) const
|
std::optional<QVariant> InternalNode::auxiliaryData(AuxiliaryDataKeyView key) const
|
||||||
@@ -99,6 +107,13 @@ bool InternalNode::hasAuxiliaryData(AuxiliaryDataKeyView key) const
|
|||||||
return found != m_auxiliaryDatas.end();
|
return found != m_auxiliaryDatas.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InternalNode::hasAuxiliaryData(AuxiliaryDataType type) const
|
||||||
|
{
|
||||||
|
auto found = find(m_auxiliaryDatas, type);
|
||||||
|
|
||||||
|
return found != m_auxiliaryDatas.end();
|
||||||
|
}
|
||||||
|
|
||||||
AuxiliaryDatasForType InternalNode::auxiliaryData(AuxiliaryDataType type) const
|
AuxiliaryDatasForType InternalNode::auxiliaryData(AuxiliaryDataType type) const
|
||||||
{
|
{
|
||||||
AuxiliaryDatasForType data;
|
AuxiliaryDatasForType data;
|
||||||
|
@@ -76,6 +76,7 @@ public:
|
|||||||
bool setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data);
|
bool setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data);
|
||||||
bool removeAuxiliaryData(AuxiliaryDataKeyView key);
|
bool removeAuxiliaryData(AuxiliaryDataKeyView key);
|
||||||
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
||||||
|
bool hasAuxiliaryData(AuxiliaryDataType type) const;
|
||||||
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
||||||
AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); }
|
AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); }
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "internalnode_p.h"
|
#include "internalnode_p.h"
|
||||||
#include "model_p.h"
|
#include "model_p.h"
|
||||||
|
#include "modelutils.h"
|
||||||
#include <modelnode.h>
|
#include <modelnode.h>
|
||||||
|
|
||||||
#include "../projectstorage/sourcepath.h"
|
#include "../projectstorage/sourcepath.h"
|
||||||
@@ -2039,6 +2040,18 @@ QList<ModelNode> Model::selectedNodes(AbstractView *view) const
|
|||||||
return d->toModelNodeList(d->selectedNodes(), view);
|
return d->toModelNodeList(d->selectedNodes(), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList)
|
||||||
|
{
|
||||||
|
QList<ModelNode> unlockedNodes;
|
||||||
|
|
||||||
|
for (const auto &modelNode : selectedNodeList) {
|
||||||
|
if (!ModelUtils::isThisOrAncestorLocked(modelNode))
|
||||||
|
unlockedNodes.push_back(modelNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->setSelectedNodes(toInternalNodeList(unlockedNodes));
|
||||||
|
}
|
||||||
|
|
||||||
void Model::clearMetaInfoCache()
|
void Model::clearMetaInfoCache()
|
||||||
{
|
{
|
||||||
d->m_nodeMetaInfoCache.clear();
|
d->m_nodeMetaInfoCache.clear();
|
||||||
@@ -2064,7 +2077,6 @@ SourceId Model::fileUrlSourceId() const
|
|||||||
*/
|
*/
|
||||||
void Model::setFileUrl(const QUrl &url)
|
void Model::setFileUrl(const QUrl &url)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(url.isValid() && url.isLocalFile(), qDebug() << "url:" << url; return);
|
|
||||||
Internal::WriteLocker locker(d.get());
|
Internal::WriteLocker locker(d.get());
|
||||||
d->setFileUrl(url);
|
d->setFileUrl(url);
|
||||||
}
|
}
|
||||||
|
@@ -80,13 +80,13 @@ QString ModelNode::id() const
|
|||||||
return m_internalNode->id;
|
return m_internalNode->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelNode::ensureIdExists()
|
void ModelNode::ensureIdExists() const
|
||||||
{
|
{
|
||||||
if (!hasId())
|
if (!hasId())
|
||||||
setIdWithoutRefactoring(model()->generateNewId(simplifiedTypeName()));
|
setIdWithoutRefactoring(model()->generateNewId(simplifiedTypeName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ModelNode::validId()
|
QString ModelNode::validId() const
|
||||||
{
|
{
|
||||||
ensureIdExists();
|
ensureIdExists();
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ bool ModelNode::hasId() const
|
|||||||
return !m_internalNode->id.isEmpty();
|
return !m_internalNode->id.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelNode::setIdWithRefactoring(const QString &id)
|
void ModelNode::setIdWithRefactoring(const QString &id) const
|
||||||
{
|
{
|
||||||
if (isValid()) {
|
if (isValid()) {
|
||||||
if (model()->rewriterView() && !id.isEmpty()
|
if (model()->rewriterView() && !id.isEmpty()
|
||||||
@@ -174,7 +174,7 @@ void ModelNode::setIdWithRefactoring(const QString &id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelNode::setIdWithoutRefactoring(const QString &id)
|
void ModelNode::setIdWithoutRefactoring(const QString &id) const
|
||||||
{
|
{
|
||||||
Internal::WriteLocker locker(m_model.data());
|
Internal::WriteLocker locker(m_model.data());
|
||||||
if (!isValid())
|
if (!isValid())
|
||||||
@@ -601,7 +601,8 @@ static QList<ModelNode> descendantNodes(const ModelNode &node)
|
|||||||
static void removeModelNodeFromSelection(const ModelNode &node)
|
static void removeModelNodeFromSelection(const ModelNode &node)
|
||||||
{
|
{
|
||||||
// remove nodes from the active selection
|
// remove nodes from the active selection
|
||||||
QList<ModelNode> selectedList = node.view()->selectedModelNodes();
|
auto model = node.model();
|
||||||
|
QList<ModelNode> selectedList = model->selectedNodes(node.view());
|
||||||
|
|
||||||
const QList<ModelNode> descendants = descendantNodes(node);
|
const QList<ModelNode> descendants = descendantNodes(node);
|
||||||
for (const ModelNode &descendantNode : descendants)
|
for (const ModelNode &descendantNode : descendants)
|
||||||
@@ -609,7 +610,7 @@ static void removeModelNodeFromSelection(const ModelNode &node)
|
|||||||
|
|
||||||
selectedList.removeAll(node);
|
selectedList.removeAll(node);
|
||||||
|
|
||||||
node.view()->setSelectedModelNodes(selectedList);
|
model->setSelectedModelNodes(selectedList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief complete removes this ModelNode from the Model
|
/*! \brief complete removes this ModelNode from the Model
|
||||||
@@ -995,6 +996,8 @@ void ModelNode::setAuxiliaryData(AuxiliaryDataType type,
|
|||||||
void ModelNode::setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data) const
|
void ModelNode::setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data) const
|
||||||
{
|
{
|
||||||
if (isValid()) {
|
if (isValid()) {
|
||||||
|
if (key.type == AuxiliaryDataType::Persistent)
|
||||||
|
ensureIdExists();
|
||||||
Internal::WriteLocker locker(m_model.data());
|
Internal::WriteLocker locker(m_model.data());
|
||||||
m_model->d->setAuxiliaryData(internalNode(), key, data);
|
m_model->d->setAuxiliaryData(internalNode(), key, data);
|
||||||
}
|
}
|
||||||
@@ -1002,21 +1005,32 @@ void ModelNode::setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data)
|
|||||||
|
|
||||||
void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataKeyView key, const QVariant &data) const
|
void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataKeyView key, const QVariant &data) const
|
||||||
{
|
{
|
||||||
if (isValid())
|
if (isValid()) {
|
||||||
|
if (key.type == AuxiliaryDataType::Persistent)
|
||||||
|
ensureIdExists();
|
||||||
|
|
||||||
m_model->d->setAuxiliaryData(internalNode(), key, data);
|
m_model->d->setAuxiliaryData(internalNode(), key, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataType type,
|
void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataType type,
|
||||||
Utils::SmallStringView name,
|
Utils::SmallStringView name,
|
||||||
const QVariant &data) const
|
const QVariant &data) const
|
||||||
{
|
{
|
||||||
if (isValid())
|
if (isValid()) {
|
||||||
|
if (type == AuxiliaryDataType::Persistent)
|
||||||
|
ensureIdExists();
|
||||||
|
|
||||||
m_model->d->setAuxiliaryData(internalNode(), {type, name}, data);
|
m_model->d->setAuxiliaryData(internalNode(), {type, name}, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelNode::removeAuxiliaryData(AuxiliaryDataKeyView key) const
|
void ModelNode::removeAuxiliaryData(AuxiliaryDataKeyView key) const
|
||||||
{
|
{
|
||||||
if (isValid()) {
|
if (isValid()) {
|
||||||
|
if (key.type == AuxiliaryDataType::Persistent)
|
||||||
|
ensureIdExists();
|
||||||
|
|
||||||
Internal::WriteLocker locker(m_model.data());
|
Internal::WriteLocker locker(m_model.data());
|
||||||
m_model->d->removeAuxiliaryData(internalNode(), key);
|
m_model->d->removeAuxiliaryData(internalNode(), key);
|
||||||
}
|
}
|
||||||
@@ -1040,6 +1054,14 @@ bool ModelNode::hasAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView
|
|||||||
return hasAuxiliaryData({type, name});
|
return hasAuxiliaryData({type, name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModelNode::hasAuxiliaryData(AuxiliaryDataType type) const
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_internalNode->hasAuxiliaryData(type);
|
||||||
|
}
|
||||||
|
|
||||||
AuxiliaryDatasForType ModelNode::auxiliaryData(AuxiliaryDataType type) const
|
AuxiliaryDatasForType ModelNode::auxiliaryData(AuxiliaryDataType type) const
|
||||||
{
|
{
|
||||||
if (!isValid())
|
if (!isValid())
|
||||||
|
@@ -534,6 +534,42 @@ std::ostream &operator<<(std::ostream &out, FlagIs flagIs)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const BasicAuxiliaryDataKey<Utils::SmallStringView> &key)
|
||||||
|
{
|
||||||
|
return out << "(" << key.name << ", " << key.type << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const BasicAuxiliaryDataKey<Utils::SmallString> &key)
|
||||||
|
{
|
||||||
|
return out << "(" << key.name << ", " << key.type << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, AuxiliaryDataType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case AuxiliaryDataType::None:
|
||||||
|
out << "None";
|
||||||
|
break;
|
||||||
|
case AuxiliaryDataType::Temporary:
|
||||||
|
out << "Temporary";
|
||||||
|
break;
|
||||||
|
case AuxiliaryDataType::Document:
|
||||||
|
out << "Document";
|
||||||
|
break;
|
||||||
|
case AuxiliaryDataType::NodeInstancePropertyOverwrite:
|
||||||
|
out << "NodeInstancePropertyOverwrite";
|
||||||
|
break;
|
||||||
|
case AuxiliaryDataType::NodeInstanceAuxiliary:
|
||||||
|
out << "NodeInstanceAuxiliary";
|
||||||
|
break;
|
||||||
|
case AuxiliaryDataType::Persistent:
|
||||||
|
out << "Persistent";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Cache {
|
namespace Cache {
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext)
|
std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext)
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <designercore/model/modelresourcemanagementfwd.h>
|
#include <designercore/model/modelresourcemanagementfwd.h>
|
||||||
|
#include <qmlpuppetcommunication/interfaces/nodeinstanceglobal.h>
|
||||||
#include <utils/cpplanguage_details.h>
|
#include <utils/cpplanguage_details.h>
|
||||||
#include <utils/smallstringio.h>
|
#include <utils/smallstringio.h>
|
||||||
|
|
||||||
@@ -126,6 +127,8 @@ class NodeMetaInfo;
|
|||||||
class PropertyMetaInfo;
|
class PropertyMetaInfo;
|
||||||
struct CompoundPropertyMetaInfo;
|
struct CompoundPropertyMetaInfo;
|
||||||
enum class FlagIs : unsigned int;
|
enum class FlagIs : unsigned int;
|
||||||
|
template<typename NameType>
|
||||||
|
class BasicAuxiliaryDataKey;
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const ModelNode &node);
|
std::ostream &operator<<(std::ostream &out, const ModelNode &node);
|
||||||
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
|
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
|
||||||
@@ -142,6 +145,9 @@ std::ostream &operator<<(std::ostream &out, const NodeMetaInfo &metaInfo);
|
|||||||
std::ostream &operator<<(std::ostream &out, const PropertyMetaInfo &metaInfo);
|
std::ostream &operator<<(std::ostream &out, const PropertyMetaInfo &metaInfo);
|
||||||
std::ostream &operator<<(std::ostream &out, const CompoundPropertyMetaInfo &metaInfo);
|
std::ostream &operator<<(std::ostream &out, const CompoundPropertyMetaInfo &metaInfo);
|
||||||
std::ostream &operator<<(std::ostream &out, FlagIs flagIs);
|
std::ostream &operator<<(std::ostream &out, FlagIs flagIs);
|
||||||
|
std::ostream &operator<<(std::ostream &out, const BasicAuxiliaryDataKey<Utils::SmallStringView> &key);
|
||||||
|
std::ostream &operator<<(std::ostream &out, const BasicAuxiliaryDataKey<Utils::SmallString> &key);
|
||||||
|
std::ostream &operator<<(std::ostream &out, AuxiliaryDataType type);
|
||||||
|
|
||||||
namespace Cache {
|
namespace Cache {
|
||||||
class SourceContext;
|
class SourceContext;
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <span>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
@@ -24,4 +26,41 @@ ostream &operator<<(ostream &out, const vector<T> &vector)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Extent>
|
||||||
|
ostream &operator<<(ostream &out, const span<T, Extent> &span)
|
||||||
|
{
|
||||||
|
out << "[";
|
||||||
|
|
||||||
|
for (auto current = span.begin(); current != span.end(); ++current) {
|
||||||
|
out << *current;
|
||||||
|
|
||||||
|
if (std::next(current) != span.end())
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "]";
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename First, typename Second>
|
||||||
|
ostream &operator<<(ostream &out, const pair<First, Second> &pair)
|
||||||
|
{
|
||||||
|
out << "{";
|
||||||
|
|
||||||
|
out << pair.first;
|
||||||
|
|
||||||
|
out << ", ";
|
||||||
|
|
||||||
|
out << pair.second;
|
||||||
|
|
||||||
|
out << "}";
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ostream &operator<<(ostream &out, std::byte byte)
|
||||||
|
{
|
||||||
|
return out << std::hex << static_cast<int>(byte) << std::dec;
|
||||||
|
}
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
@@ -80,6 +80,7 @@ add_qtc_library(TestDesignerCore OBJECT
|
|||||||
metainfo/nodemetainfo.cpp
|
metainfo/nodemetainfo.cpp
|
||||||
model/abstractproperty.cpp
|
model/abstractproperty.cpp
|
||||||
model/abstractview.cpp
|
model/abstractview.cpp
|
||||||
|
model/auxiliarypropertystorageview.cpp model/auxiliarypropertystorageview.h
|
||||||
model/annotation.cpp
|
model/annotation.cpp
|
||||||
model/bindingproperty.cpp
|
model/bindingproperty.cpp
|
||||||
model/import.cpp
|
model/import.cpp
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# qmldesigner/designercore/model
|
# qmldesigner/designercore/model
|
||||||
extend_qtc_test(unittest
|
extend_qtc_test(unittest
|
||||||
SOURCES
|
SOURCES
|
||||||
|
auxiliarypropertystorageview-test.cpp
|
||||||
import-test.cpp
|
import-test.cpp
|
||||||
model-test.cpp
|
model-test.cpp
|
||||||
modelnode-test.cpp
|
modelnode-test.cpp
|
||||||
|
@@ -0,0 +1,230 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <googletest.h>
|
||||||
|
|
||||||
|
#include <mocks/externaldependenciesmock.h>
|
||||||
|
#include <mocks/modelresourcemanagementmock.h>
|
||||||
|
#include <mocks/projectstoragemock.h>
|
||||||
|
#include <mocks/sourcepathcachemock.h>
|
||||||
|
|
||||||
|
#include <model/auxiliarypropertystorageview.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using QmlDesigner::AuxiliaryDataKey;
|
||||||
|
using QmlDesigner::AuxiliaryDataType;
|
||||||
|
using QmlDesigner::ModelNode;
|
||||||
|
|
||||||
|
template<typename NameMatcher>
|
||||||
|
auto AuxiliaryProperty(AuxiliaryDataType type, const NameMatcher &nameMatcher, const QVariant &value)
|
||||||
|
{
|
||||||
|
return Pair(AllOf(Field(&AuxiliaryDataKey::type, type),
|
||||||
|
Field(&AuxiliaryDataKey::name, nameMatcher)),
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuxiliaryPropertyStorageView : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
struct StaticData
|
||||||
|
{
|
||||||
|
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
|
||||||
|
};
|
||||||
|
|
||||||
|
~AuxiliaryPropertyStorageView() { view.resetForTestsOnly(); }
|
||||||
|
|
||||||
|
static void SetUpTestSuite() { staticData = std::make_unique<StaticData>(); }
|
||||||
|
|
||||||
|
static void TearDownTestSuite() { staticData.reset(); }
|
||||||
|
|
||||||
|
inline static std::unique_ptr<StaticData> staticData;
|
||||||
|
Sqlite::Database &database = staticData->database;
|
||||||
|
NiceMock<SourcePathCacheMockWithPaths> pathCacheMock{"/path/foo.qml"};
|
||||||
|
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock{pathCacheMock.sourceId};
|
||||||
|
NiceMock<ModelResourceManagementMock> resourceManagementMock;
|
||||||
|
QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")};
|
||||||
|
QmlDesigner::Model model{{projectStorageMock, pathCacheMock},
|
||||||
|
"Item",
|
||||||
|
imports,
|
||||||
|
pathCacheMock.path.toQString(),
|
||||||
|
std::make_unique<ModelResourceManagementMockWrapper>(
|
||||||
|
resourceManagementMock)};
|
||||||
|
QmlDesigner::Model model2{{projectStorageMock, pathCacheMock},
|
||||||
|
"Item",
|
||||||
|
imports,
|
||||||
|
pathCacheMock.path.toQString(),
|
||||||
|
std::make_unique<ModelResourceManagementMockWrapper>(
|
||||||
|
resourceManagementMock)};
|
||||||
|
NiceMock<ExternalDependenciesMock> externalDependenciesMock;
|
||||||
|
QmlDesigner::AuxiliaryPropertyStorageView view{database, externalDependenciesMock};
|
||||||
|
ModelNode rootNode = model.rootModelNode();
|
||||||
|
ModelNode rootNode2 = model2.rootModelNode();
|
||||||
|
QmlDesigner::AuxiliaryDataKeyView persistentFooKey{AuxiliaryDataType::Persistent, "foo"};
|
||||||
|
QmlDesigner::AuxiliaryDataKeyView temporaryFooKey{AuxiliaryDataType::Temporary, "foo"};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, store_persistent_auxiliary_property)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, store_color_auxiliary_property)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
QColor color{Qt::red};
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, color);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", color)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, do_not_store_temporary_auxiliary_property)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(temporaryFooKey, "text");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Temporary, "foo", "text"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, do_not_load_node_without_id)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, do_not_store_node_without_id)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, do_not_load_from_different_document)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model2.setFileUrl(QUrl{"/path/foo2.qml"});
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, removing_node_erases_entries)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
auto node = model.createModelNode("Item");
|
||||||
|
node.setIdWithoutRefactoring("node1");
|
||||||
|
node.setAuxiliaryData(persistentFooKey, "some value");
|
||||||
|
auto node2 = model2.createModelNode("Item");
|
||||||
|
node2.setIdWithoutRefactoring("node1");
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
|
||||||
|
// act
|
||||||
|
node.destroy();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(node2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "some value"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, changing_node_id_is_saving_data_under_new_id)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root2");
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
model.detachView(&view);
|
||||||
|
model.attachView(&view);
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setIdWithoutRefactoring("root2");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AuxiliaryPropertyStorageView, changing_node_id_is_removing_data_from_old_id)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
model.attachView(&view);
|
||||||
|
rootNode.setIdWithoutRefactoring("root");
|
||||||
|
rootNode2.setIdWithoutRefactoring("root");
|
||||||
|
rootNode.setAuxiliaryData(persistentFooKey, "text");
|
||||||
|
model.detachView(&view);
|
||||||
|
model.attachView(&view);
|
||||||
|
|
||||||
|
// act
|
||||||
|
rootNode.setIdWithoutRefactoring("root2");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
model.detachView(&view);
|
||||||
|
model2.attachView(&view);
|
||||||
|
ASSERT_THAT(rootNode2.auxiliaryData(),
|
||||||
|
Not(Contains(AuxiliaryProperty(AuxiliaryDataType::Persistent, "foo", "text"))));
|
||||||
|
}
|
||||||
|
} // namespace
|
@@ -9,10 +9,12 @@
|
|||||||
|
|
||||||
#include "conditionally-disabled-tests.h"
|
#include "conditionally-disabled-tests.h"
|
||||||
|
|
||||||
#include "../printers/gtest-creator-printing.h"
|
|
||||||
#include "../printers/gtest-qt-printing.h"
|
|
||||||
#include "../printers/gtest-std-printing.h"
|
#include "../printers/gtest-std-printing.h"
|
||||||
|
|
||||||
|
#include "../printers/gtest-qt-printing.h"
|
||||||
|
|
||||||
|
#include "../printers/gtest-creator-printing.h"
|
||||||
|
|
||||||
#include "../utils/google-using-declarations.h"
|
#include "../utils/google-using-declarations.h"
|
||||||
|
|
||||||
#include "../matchers/unittest-matchers.h"
|
#include "../matchers/unittest-matchers.h"
|
||||||
|
Reference in New Issue
Block a user