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,
|
||||
Document,
|
||||
NodeInstancePropertyOverwrite,
|
||||
NodeInstanceAuxiliary
|
||||
NodeInstanceAuxiliary,
|
||||
Persistent
|
||||
};
|
||||
|
||||
enum class View3DActionType {
|
||||
|
||||
@@ -361,6 +361,7 @@ extend_qtc_library(QmlDesignerCore
|
||||
abstractview.cpp
|
||||
anchorline.cpp
|
||||
annotation.cpp
|
||||
auxiliarypropertystorageview.cpp auxiliarypropertystorageview.h
|
||||
bindingproperty.cpp
|
||||
componenttextmodifier.cpp
|
||||
documentmessage.cpp
|
||||
|
||||
@@ -20,17 +20,19 @@
|
||||
#include <itemlibraryview.h>
|
||||
#include <materialbrowserview.h>
|
||||
#include <materialeditorview.h>
|
||||
#include <model/auxiliarypropertystorageview.h>
|
||||
#include <navigatorview.h>
|
||||
#include <nodeinstanceview.h>
|
||||
#include <propertyeditorview.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <rewriterview.h>
|
||||
#include <stateseditorview.h>
|
||||
#include <texteditorview.h>
|
||||
#include <textureeditorview.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <sqlitedatabase.h>
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <advanceddockingsystem/dockwidget.h>
|
||||
@@ -49,6 +51,7 @@ public:
|
||||
ViewManagerData(AsynchronousImageCache &imageCache,
|
||||
ExternalDependenciesInterface &externalDependencies)
|
||||
: debugView{externalDependencies}
|
||||
, auxiliaryDataKeyView{auxiliaryDataDatabase, externalDependencies}
|
||||
, designerActionManagerView{externalDependencies}
|
||||
, nodeInstanceView(QCoreApplication::arguments().contains("-capture-puppet-stream")
|
||||
? capturingConnectionManager
|
||||
@@ -78,6 +81,11 @@ public:
|
||||
CapturingConnectionManager capturingConnectionManager;
|
||||
QmlModelState savedState;
|
||||
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;
|
||||
NodeInstanceView nodeInstanceView;
|
||||
ContentLibraryView contentLibraryView;
|
||||
@@ -201,7 +209,8 @@ QList<AbstractView *> ViewManager::views() const
|
||||
QList<AbstractView *> ViewManager::standardViews() const
|
||||
{
|
||||
#ifndef QTC_USE_QML_DESIGNER_LITE
|
||||
QList<AbstractView *> list = {&d->edit3DView,
|
||||
QList<AbstractView *> list = {&d->auxiliaryDataKeyView,
|
||||
&d->edit3DView,
|
||||
&d->formEditorView,
|
||||
&d->textEditorView,
|
||||
&d->assetsLibraryView,
|
||||
|
||||
@@ -244,6 +244,9 @@ QTextStream &operator<<(QTextStream &stream, AuxiliaryDataType type)
|
||||
case AuxiliaryDataType::Temporary:
|
||||
stream << "Temporary";
|
||||
break;
|
||||
case AuxiliaryDataType::Persistent:
|
||||
stream << "Persistent";
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
@@ -249,6 +249,7 @@ public:
|
||||
const QList<DocumentMessage> &warnings);
|
||||
|
||||
QList<ModelNode> selectedNodes(AbstractView *view) const;
|
||||
void setSelectedModelNodes(const QList<ModelNode> &selectedNodeList);
|
||||
|
||||
void clearMetaInfoCache();
|
||||
|
||||
|
||||
@@ -152,10 +152,10 @@ public:
|
||||
void destroy();
|
||||
|
||||
QString id() const;
|
||||
void ensureIdExists();
|
||||
[[nodiscard]] QString validId();
|
||||
void setIdWithRefactoring(const QString &id);
|
||||
void setIdWithoutRefactoring(const QString &id);
|
||||
void ensureIdExists() const;
|
||||
[[nodiscard]] QString validId() const;
|
||||
void setIdWithRefactoring(const QString &id) const;
|
||||
void setIdWithoutRefactoring(const QString &id) const;
|
||||
static bool isValidId(const QString &id);
|
||||
static QString getIdValidityErrorMessage(const QString &id);
|
||||
|
||||
@@ -192,6 +192,7 @@ public:
|
||||
void removeAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
||||
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
||||
bool hasAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name) const;
|
||||
bool hasAuxiliaryData(AuxiliaryDataType type) const;
|
||||
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
||||
AuxiliaryDatasView auxiliaryData() const;
|
||||
|
||||
|
||||
@@ -427,14 +427,7 @@ QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode>
|
||||
*/
|
||||
void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList)
|
||||
{
|
||||
QList<ModelNode> unlockedNodes;
|
||||
|
||||
for (const auto &modelNode : selectedNodeList) {
|
||||
if (!ModelUtils::isThisOrAncestorLocked(modelNode))
|
||||
unlockedNodes.push_back(modelNode);
|
||||
}
|
||||
|
||||
model()->d->setSelectedNodes(toInternalNodeList(unlockedNodes));
|
||||
model()->setSelectedModelNodes(selectedNodeList);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
std::optional<QVariant> InternalNode::auxiliaryData(AuxiliaryDataKeyView key) const
|
||||
@@ -99,6 +107,13 @@ bool InternalNode::hasAuxiliaryData(AuxiliaryDataKeyView key) const
|
||||
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 data;
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
bool setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data);
|
||||
bool removeAuxiliaryData(AuxiliaryDataKeyView key);
|
||||
bool hasAuxiliaryData(AuxiliaryDataKeyView key) const;
|
||||
bool hasAuxiliaryData(AuxiliaryDataType type) const;
|
||||
AuxiliaryDatasForType auxiliaryData(AuxiliaryDataType type) const;
|
||||
AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); }
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "model.h"
|
||||
#include "internalnode_p.h"
|
||||
#include "model_p.h"
|
||||
#include "modelutils.h"
|
||||
#include <modelnode.h>
|
||||
|
||||
#include "../projectstorage/sourcepath.h"
|
||||
@@ -2039,6 +2040,18 @@ QList<ModelNode> Model::selectedNodes(AbstractView *view) const
|
||||
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()
|
||||
{
|
||||
d->m_nodeMetaInfoCache.clear();
|
||||
@@ -2064,7 +2077,6 @@ SourceId Model::fileUrlSourceId() const
|
||||
*/
|
||||
void Model::setFileUrl(const QUrl &url)
|
||||
{
|
||||
QTC_ASSERT(url.isValid() && url.isLocalFile(), qDebug() << "url:" << url; return);
|
||||
Internal::WriteLocker locker(d.get());
|
||||
d->setFileUrl(url);
|
||||
}
|
||||
|
||||
@@ -80,13 +80,13 @@ QString ModelNode::id() const
|
||||
return m_internalNode->id;
|
||||
}
|
||||
|
||||
void ModelNode::ensureIdExists()
|
||||
void ModelNode::ensureIdExists() const
|
||||
{
|
||||
if (!hasId())
|
||||
setIdWithoutRefactoring(model()->generateNewId(simplifiedTypeName()));
|
||||
}
|
||||
|
||||
QString ModelNode::validId()
|
||||
QString ModelNode::validId() const
|
||||
{
|
||||
ensureIdExists();
|
||||
|
||||
@@ -162,7 +162,7 @@ bool ModelNode::hasId() const
|
||||
return !m_internalNode->id.isEmpty();
|
||||
}
|
||||
|
||||
void ModelNode::setIdWithRefactoring(const QString &id)
|
||||
void ModelNode::setIdWithRefactoring(const QString &id) const
|
||||
{
|
||||
if (isValid()) {
|
||||
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());
|
||||
if (!isValid())
|
||||
@@ -601,7 +601,8 @@ static QList<ModelNode> descendantNodes(const ModelNode &node)
|
||||
static void removeModelNodeFromSelection(const ModelNode &node)
|
||||
{
|
||||
// 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);
|
||||
for (const ModelNode &descendantNode : descendants)
|
||||
@@ -609,7 +610,7 @@ static void removeModelNodeFromSelection(const ModelNode &node)
|
||||
|
||||
selectedList.removeAll(node);
|
||||
|
||||
node.view()->setSelectedModelNodes(selectedList);
|
||||
model->setSelectedModelNodes(selectedList);
|
||||
}
|
||||
|
||||
/*! \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
|
||||
{
|
||||
if (isValid()) {
|
||||
if (key.type == AuxiliaryDataType::Persistent)
|
||||
ensureIdExists();
|
||||
Internal::WriteLocker locker(m_model.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
|
||||
{
|
||||
if (isValid())
|
||||
if (isValid()) {
|
||||
if (key.type == AuxiliaryDataType::Persistent)
|
||||
ensureIdExists();
|
||||
|
||||
m_model->d->setAuxiliaryData(internalNode(), key, data);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataType type,
|
||||
Utils::SmallStringView name,
|
||||
const QVariant &data) const
|
||||
{
|
||||
if (isValid())
|
||||
if (isValid()) {
|
||||
if (type == AuxiliaryDataType::Persistent)
|
||||
ensureIdExists();
|
||||
|
||||
m_model->d->setAuxiliaryData(internalNode(), {type, name}, data);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelNode::removeAuxiliaryData(AuxiliaryDataKeyView key) const
|
||||
{
|
||||
if (isValid()) {
|
||||
if (key.type == AuxiliaryDataType::Persistent)
|
||||
ensureIdExists();
|
||||
|
||||
Internal::WriteLocker locker(m_model.data());
|
||||
m_model->d->removeAuxiliaryData(internalNode(), key);
|
||||
}
|
||||
@@ -1040,6 +1054,14 @@ bool ModelNode::hasAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView
|
||||
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
|
||||
{
|
||||
if (!isValid())
|
||||
|
||||
Reference in New Issue
Block a user