QmlDesigner: Integrate component generation

It is only activated if the project storage is activated

Task-number: QDS-10578
Change-Id: Id93673eba470aa37a249072b3ef9e0231499095a
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2023-09-07 14:49:44 +02:00
parent 95d40394da
commit ebe1c889b7
22 changed files with 387 additions and 110 deletions

View File

@@ -575,6 +575,9 @@ extend_qtc_plugin(QmlDesigner
modelnodeoperations.cpp modelnodeoperations.h
formatoperation.cpp formatoperation.h
navigation2d.cpp navigation2d.h
propertyeditorcomponentgenerator.cpp propertyeditorcomponentgenerator.h
propertycomponentgenerator.cpp propertycomponentgenerator.h
propertycomponentgeneratorinterface.h
qmldesignericonprovider.cpp qmldesignericonprovider.h
qmleditormenu.cpp qmleditormenu.h
selectioncontext.cpp selectioncontext.h

View File

@@ -5,6 +5,7 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/set_algorithm.h>
#include <model.h>
@@ -205,10 +206,10 @@ std::optional<PropertyComponentGenerator::Entry> createEntry(QmlJS::SimpleReader
needsTypeArg};
}
PropertyComponentGenerator::Entries createEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration,
Model *model,
const QString &templatesPath)
std::tuple<PropertyComponentGenerator::Entries, bool> createEntries(
QmlJS::SimpleReaderNode::Ptr templateConfiguration, Model *model, const QString &templatesPath)
{
bool hasInvalidTemplates = false;
PropertyComponentGenerator::Entries entries;
entries.reserve(32);
@@ -216,12 +217,14 @@ PropertyComponentGenerator::Entries createEntries(QmlJS::SimpleReaderNode::Ptr t
for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) {
if (auto entry = createEntry(node.get(), model, templatesPath))
entries.push_back(*entry);
else
hasInvalidTemplates = true;
}
return entries;
return {entries, hasInvalidTemplates};
}
QStringList createImports(QmlJS::SimpleReaderNode::Ptr templateConfiguration)
QStringList createImports(QmlJS::SimpleReaderNode *templateConfiguration)
{
auto property = templateConfiguration->property("imports");
return Utils::transform<QStringList>(property.value.toList(),
@@ -232,17 +235,11 @@ QStringList createImports(QmlJS::SimpleReaderNode::Ptr templateConfiguration)
PropertyComponentGenerator::PropertyComponentGenerator(const QString &propertyEditorResourcesPath,
Model *model)
: m_entries(createEntries(createTemplateConfiguration(propertyEditorResourcesPath),
model,
propertyEditorResourcesPath + "/PropertyTemplates/"))
: m_templateConfiguration{createTemplateConfiguration(propertyEditorResourcesPath)}
, m_propertyTemplatesPath{propertyEditorResourcesPath + "/PropertyTemplates/"}
{
auto templateConfiguration = createTemplateConfiguration(propertyEditorResourcesPath);
m_entries = createEntries(templateConfiguration,
model,
propertyEditorResourcesPath + "/PropertyTemplates/");
m_imports = createImports(templateConfiguration);
setModel(model);
m_imports = createImports(m_templateConfiguration.get());
}
PropertyComponentGenerator::Property PropertyComponentGenerator::create(const PropertyMetaInfo &property) const
@@ -257,6 +254,61 @@ PropertyComponentGenerator::Property PropertyComponentGenerator::create(const Pr
return generateComplexComponent(property, propertyType);
}
void PropertyComponentGenerator::setModel(Model *model)
{
if (model && m_model && m_model->projectStorage() == model->projectStorage()) {
m_model = model;
return;
}
if (model) {
setEntries(m_templateConfiguration, model, m_propertyTemplatesPath);
} else {
m_entries.clear();
m_entryTypeIds.clear();
}
m_model = model;
}
namespace {
bool insect(const TypeIds &first, const TypeIds &second)
{
bool intersecting = false;
std::set_intersection(first.begin(),
first.end(),
second.begin(),
second.end(),
Utils::make_iterator([&](const auto &) { intersecting = true; }));
return intersecting;
}
} // namespace
void PropertyComponentGenerator::setEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration,
Model *model,
const QString &propertyTemplatesPath)
{
auto [entries, hasInvalidTemplates] = createEntries(templateConfiguration,
model,
propertyTemplatesPath);
m_entries = std::move(entries);
m_hasInvalidTemplates = hasInvalidTemplates;
m_entryTypeIds = Utils::transform<TypeIds>(m_entries,
[](const auto &entry) { return entry.type.id(); });
std::sort(m_entryTypeIds.begin(), m_entryTypeIds.end());
}
void PropertyComponentGenerator::refreshMetaInfos(const TypeIds &deletedTypeIds)
{
if (!insect(deletedTypeIds, m_entryTypeIds) && !m_hasInvalidTemplates)
return;
setEntries(m_templateConfiguration, m_model, m_propertyTemplatesPath);
}
const PropertyComponentGenerator::Entry *PropertyComponentGenerator::findEntry(const NodeMetaInfo &type) const
{
auto found = std::find_if(m_entries.begin(), m_entries.end(), [&](const auto &entry) {

View File

@@ -10,6 +10,8 @@
#include <qmljs/qmljssimplereader.h>
#include <QPointer>
#include <optional>
#include <variant>
#include <vector>
@@ -36,6 +38,10 @@ public:
QStringList imports() const override { return m_imports; }
void setModel(Model *model);
void refreshMetaInfos(const TypeIds &deletedTypeIds);
private:
const Entry *findEntry(const NodeMetaInfo &type) const;
QString generateSubComponentText(Utils::SmallStringView propertyBaseName,
@@ -45,9 +51,18 @@ private:
Property generateComplexComponent(const PropertyMetaInfo &property,
const NodeMetaInfo &propertyType) const;
void setEntries(QmlJS::SimpleReaderNode::Ptr templateConfiguration,
Model *model,
const QString &propertyTemplatesPath);
private:
Entries m_entries;
TypeIds m_entryTypeIds;
QStringList m_imports;
QPointer<Model> m_model;
QmlJS::SimpleReaderNode::Ptr m_templateConfiguration;
QString m_propertyTemplatesPath;
bool m_hasInvalidTemplates = false;
};
} // namespace QmlDesigner

View File

@@ -75,13 +75,11 @@ Utils::SmallStringView propertyName(const GeneratorProperty &property)
property);
}
PropertyMetaInfos getUnmangedProperties(const NodeMetaInfo &nodeInfo)
PropertyMetaInfos getUnmangedProperties(const NodeMetaInfos &prototypes)
{
PropertyMetaInfos properties;
properties.reserve(128);
auto prototypes = nodeInfo.selfAndPrototypes();
for (const auto &prototype : prototypes) {
if (prototype.propertyEditorPathId())
break;
@@ -117,12 +115,12 @@ GeneratorProperties createSortedGeneratorProperties(
}
QString createPropertySections(const PropertyComponentGeneratorType &propertyGenerator,
const NodeMetaInfo &nodeInfo)
const NodeMetaInfos &prototypeChain)
{
QString propertyComponents;
propertyComponents.reserve(100000);
auto generatorProperties = createSortedGeneratorProperties(getUnmangedProperties(nodeInfo),
auto generatorProperties = createSortedGeneratorProperties(getUnmangedProperties(prototypeChain),
propertyGenerator);
const auto begin = generatorProperties.begin();
@@ -143,12 +141,12 @@ QString createPropertySections(const PropertyComponentGeneratorType &propertyGen
} // namespace
PropertyEditorTemplateGenerator::PropertyEditorTemplateGenerator(
PropertyEditorComponentGenerator::PropertyEditorComponentGenerator(
const PropertyComponentGeneratorType &propertyGenerator)
: m_propertyGenerator{propertyGenerator}
{}
QString PropertyEditorTemplateGenerator::create(const NodeMetaInfo &nodeInfo, bool isComponent)
QString PropertyEditorComponentGenerator::create(const NodeMetaInfos &prototypeChain, bool isComponent)
{
return QString{R"xy(
%1
@@ -171,7 +169,7 @@ QString PropertyEditorTemplateGenerator::create(const NodeMetaInfo &nodeInfo, bo
.arg(createImports(m_propertyGenerator.imports()),
componentButton(isComponent),
QObject::tr("Exposed Custom Properties"),
createPropertySections(m_propertyGenerator, nodeInfo));
createPropertySections(m_propertyGenerator, prototypeChain));
}
} // namespace QmlDesigner

View File

@@ -14,13 +14,13 @@ using PropertyComponentGeneratorType = PropertyComponentGeneratorInterface;
#else
using PropertyComponentGeneratorType = PropertyComponentGenerator;
#endif
class PropertyEditorTemplateGenerator
class PropertyEditorComponentGenerator
{
public:
PropertyEditorTemplateGenerator(const PropertyComponentGeneratorType &propertyGenerator);
PropertyEditorComponentGenerator(const PropertyComponentGeneratorType &propertyGenerator);
QString create(const NodeMetaInfo &nodeInfo, bool isComponent);
[[nodiscard]] QString create(const NodeMetaInfos &prototypeChain, bool isComponent);
private:
const PropertyComponentGeneratorType &m_propertyGenerator;

View File

@@ -20,6 +20,7 @@
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <projectstorage/sourcepathcache.h>
#include <theme.h>
@@ -65,6 +66,8 @@ PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache,
, m_timerId(0)
, m_stackedWidget(new PropertyEditorWidget())
, m_qmlBackEndForCurrentType(nullptr)
, m_propertyComponentGenerator{QmlDesigner::PropertyEditorQmlBackend::propertyEditorResourcesPath(),
model()}
, m_locked(false)
, m_setupCompleted(false)
, m_singleShotTimer(new QTimer(this))
@@ -373,6 +376,11 @@ void PropertyEditorView::currentTimelineChanged(const ModelNode &)
m_qmlBackEndForCurrentType->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
}
void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds)
{
m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds);
}
void PropertyEditorView::updateSize()
{
if (!m_qmlBackEndForCurrentType)
@@ -439,8 +447,8 @@ void PropertyEditorView::resetView()
namespace {
std::tuple<NodeMetaInfo, QUrl> diffType(const NodeMetaInfo &commonAncestor,
const NodeMetaInfo &specificsClassMetaInfo)
[[maybe_unused]] std::tuple<NodeMetaInfo, QUrl> diffType(const NodeMetaInfo &commonAncestor,
const NodeMetaInfo &specificsClassMetaInfo)
{
NodeMetaInfo diffClassMetaInfo;
QUrl qmlSpecificsFile;
@@ -463,9 +471,9 @@ std::tuple<NodeMetaInfo, QUrl> diffType(const NodeMetaInfo &commonAncestor,
return {diffClassMetaInfo, qmlSpecificsFile};
}
QString getSpecificQmlData(const NodeMetaInfo &commonAncestor,
const ModelNode &selectedNode,
const NodeMetaInfo &diffClassMetaInfo)
[[maybe_unused]] QString getSpecificQmlData(const NodeMetaInfo &commonAncestor,
const ModelNode &selectedNode,
const NodeMetaInfo &diffClassMetaInfo)
{
if (commonAncestor.isValid() && diffClassMetaInfo != selectedNode.metaInfo())
return PropertyEditorQmlBackend::templateGeneration(commonAncestor,
@@ -534,33 +542,90 @@ void setupWidget(PropertyEditorQmlBackend *currentQmlBackend,
stackedWidget->setCurrentWidget(currentQmlBackend->widget());
currentQmlBackend->contextObject()->triggerSelectionChanged();
}
[[maybe_unused]] auto findPaneAndSpecificsPath(const NodeMetaInfos &prototypes,
const SourcePathCacheInterface &pathCache)
{
Utils::PathString panePath;
Utils::PathString specificsPath;
for (const NodeMetaInfo &prototype : prototypes) {
auto sourceId = prototype.propertyEditorPathId();
if (sourceId) {
auto path = pathCache.sourcePath(sourceId);
if (path.endsWith("Pane.qml")) {
panePath = path;
if (panePath.size() && specificsPath.size())
return std::make_tuple(panePath, specificsPath);
} else if (path.endsWith("Specifics.qml")) {
specificsPath = path;
if (panePath.size() && specificsPath.size())
return std::make_tuple(panePath, specificsPath);
}
}
}
return std::make_tuple(panePath, specificsPath);
}
} // namespace
void PropertyEditorView::setupQmlBackend()
{
const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode);
if constexpr (useProjectStorage()) {
auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes();
auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes,
m_selectedNode.isComponent());
auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes,
model()->pathCache());
PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
QUrl::fromLocalFile(
QString{panePath}),
m_imageCache,
m_stackedWidget,
this);
const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo(
commonAncestor);
setupCurrentQmlBackend(currentQmlBackend,
m_selectedNode,
QUrl::fromLocalFile(QString{specificsPath}),
currentState(),
this,
specificQmlData);
auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo);
setupWidget(currentQmlBackend, this, m_stackedWidget);
QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo);
m_qmlBackEndForCurrentType = currentQmlBackend;
PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
qmlFileUrl,
m_imageCache,
m_stackedWidget,
this);
setupInsight(rootModelNode(), currentQmlBackend);
} else {
const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(
m_selectedNode);
setupCurrentQmlBackend(
currentQmlBackend, m_selectedNode, qmlSpecificsFile, currentState(), this, specificQmlData);
const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo(
commonAncestor);
setupWidget(currentQmlBackend, this, m_stackedWidget);
auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo);
m_qmlBackEndForCurrentType = currentQmlBackend;
QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo);
setupInsight(rootModelNode(), currentQmlBackend);
PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash,
qmlFileUrl,
m_imageCache,
m_stackedWidget,
this);
setupCurrentQmlBackend(currentQmlBackend,
m_selectedNode,
qmlSpecificsFile,
currentState(),
this,
specificQmlData);
setupWidget(currentQmlBackend, this, m_stackedWidget);
m_qmlBackEndForCurrentType = currentQmlBackend;
setupInsight(rootModelNode(), currentQmlBackend);
}
}
void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
@@ -646,6 +711,9 @@ void PropertyEditorView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
if constexpr (useProjectStorage())
m_propertyComponentGenerator.setModel(model);
if (debug)
qDebug() << Q_FUNC_INFO;

View File

@@ -9,6 +9,7 @@
#include <QObject>
#include <QTimer>
#include <propertyeditorcomponentgenerator.h>
QT_BEGIN_NAMESPACE
class QEvent;
@@ -81,6 +82,8 @@ public:
void currentTimelineChanged(const ModelNode &node) override;
void refreshMetaInfos(const TypeIds &deletedTypeIds) override;
protected:
void timerEvent(QTimerEvent *event) override;
void setupPane(const TypeName &typeName);
@@ -113,6 +116,8 @@ private: //variables
QString m_qmlDir;
QHash<QString, PropertyEditorQmlBackend *> m_qmlBackendHash;
PropertyEditorQmlBackend *m_qmlBackEndForCurrentType;
PropertyComponentGenerator m_propertyComponentGenerator;
PropertyEditorComponentGenerator m_propertyEditorComponentGenerator{m_propertyComponentGenerator};
bool m_locked;
bool m_setupCompleted;
QTimer *m_singleShotTimer;

View File

@@ -148,7 +148,7 @@ public:
virtual void modelAttached(Model *model);
virtual void modelAboutToBeDetached(Model *model);
virtual void refreshMetaInfos();
virtual void refreshMetaInfos(const TypeIds &deletedTypeIds);
virtual void nodeCreated(const ModelNode &createdNode);
virtual void nodeAboutToBeRemoved(const ModelNode &removedNode);

View File

@@ -136,6 +136,7 @@ public:
void setMetaInfo(const MetaInfo &metaInfo);
NodeMetaInfo boolMetaInfo() const;
NodeMetaInfo doubleMetaInfo() const;
NodeMetaInfo flowViewFlowActionAreaMetaInfo() const;
NodeMetaInfo flowViewFlowDecisionMetaInfo() const;
NodeMetaInfo flowViewFlowItemMetaInfo() const;

View File

@@ -164,7 +164,7 @@ void AbstractView::modelAboutToBeDetached(Model *)
removeModel();
}
void AbstractView::refreshMetaInfos() {}
void AbstractView::refreshMetaInfos(const TypeIds &) {}
/*!
\enum QmlDesigner::AbstractView::PropertyChangeFlag

View File

@@ -83,7 +83,8 @@ ModelPrivate::ModelPrivate(Model *model,
m_currentStateNode = m_rootInternalNode;
m_currentTimelineNode = m_rootInternalNode;
projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback);
if constexpr (useProjectStorage())
projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback);
}
ModelPrivate::ModelPrivate(Model *model,
@@ -106,7 +107,8 @@ ModelPrivate::ModelPrivate(Model *model,
m_currentStateNode = m_rootInternalNode;
m_currentTimelineNode = m_rootInternalNode;
projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback);
if constexpr (useProjectStorage())
projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback);
}
ModelPrivate::ModelPrivate(Model *model,
@@ -129,7 +131,8 @@ ModelPrivate::ModelPrivate(Model *model,
ModelPrivate::~ModelPrivate()
{
projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback);
if constexpr (useProjectStorage())
projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback);
};
void ModelPrivate::detachAllViews()
@@ -391,9 +394,9 @@ void ModelPrivate::setTypeId(InternalNode *node, Utils::SmallStringView typeName
}
}
void ModelPrivate::emitRefreshMetaInfos()
void ModelPrivate::emitRefreshMetaInfos(const TypeIds &deletedTypeIds)
{
notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(); });
notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(deletedTypeIds); });
}
void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet)
@@ -2086,6 +2089,16 @@ NodeMetaInfo Model::boolMetaInfo() const
}
}
NodeMetaInfo Model::doubleMetaInfo() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;
return createNodeMetaInfo<QML, DoubleType>();
} else {
return metaInfo("QML.double");
}
}
template<const auto &moduleName, const auto &typeName>
NodeMetaInfo Model::createNodeMetaInfo() const
{

View File

@@ -317,7 +317,7 @@ private:
EnabledViewRange enabledViews() const;
ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName);
void setTypeId(InternalNode *node, Utils::SmallStringView typeName);
void emitRefreshMetaInfos();
void emitRefreshMetaInfos(const TypeIds &deletedTypeIds);
public:
NotNullPointer<ProjectStorageType> projectStorage = nullptr;
@@ -326,7 +326,8 @@ public:
private:
Model *m_model = nullptr;
MetaInfo m_metaInfo;
std::function<void()> m_metaInfoRefreshCallback{[&] { emitRefreshMetaInfos(); }};
std::function<void(const TypeIds &deletedTypeIds)> m_metaInfoRefreshCallback{
[&](const TypeIds &deletedTypeIds) { emitRefreshMetaInfos(deletedTypeIds); }};
Imports m_imports;
Imports m_possibleImportList;
Imports m_usedImportList;

View File

@@ -53,6 +53,7 @@ public:
void synchronize(Storage::Synchronization::SynchronizationPackage package) override
{
TypeIds deletedTypeIds;
Sqlite::withImmediateTransaction(database, [&] {
AliasPropertyDeclarations insertedAliasPropertyDeclarations;
AliasPropertyDeclarations updatedAliasPropertyDeclarations;
@@ -61,7 +62,6 @@ public:
PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
TypeIds updatedTypeIds;
updatedTypeIds.reserve(package.types.size());
@@ -111,7 +111,7 @@ public:
commonTypeCache_.resetTypeIds();
});
callRefreshMetaInfoCallback();
callRefreshMetaInfoCallback(deletedTypeIds);
}
void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override
@@ -123,12 +123,12 @@ public:
});
}
void addRefreshCallback(std::function<void()> *callback) override
void addRefreshCallback(std::function<void(const TypeIds &deletedTypeIds)> *callback) override
{
m_refreshCallbacks.push_back(callback);
}
void removeRefreshCallback(std::function<void()> *callback) override
void removeRefreshCallback(std::function<void(const TypeIds &deletedTypeIds)> *callback) override
{
m_refreshCallbacks.erase(
std::find(m_refreshCallbacks.begin(), m_refreshCallbacks.end(), callback));
@@ -616,10 +616,10 @@ private:
return selectAllModulesStatement.template valuesWithTransaction<Module, 128>();
}
void callRefreshMetaInfoCallback()
void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds)
{
for (auto *callback : m_refreshCallbacks)
(*callback)();
(*callback)(deletedTypeIds);
}
class AliasPropertyDeclaration
@@ -2787,7 +2787,7 @@ public:
Initializer initializer;
mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}};
Storage::Info::CommonTypeCache<ProjectStorageInterface> commonTypeCache_{*this};
std::vector<std::function<void()> *> m_refreshCallbacks;
std::vector<std::function<void(const TypeIds &deletedTypeIds)> *> m_refreshCallbacks;
ReadWriteStatement<1, 3> upsertTypeStatement{
"INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO "
"UPDATE SET traits=excluded.traits WHERE traits IS NOT "

View File

@@ -27,8 +27,8 @@ public:
virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0;
virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0;
virtual void addRefreshCallback(std::function<void()> *callback) = 0;
virtual void removeRefreshCallback(std::function<void()> *callback) = 0;
virtual void addRefreshCallback(std::function<void(const TypeIds &deletedTypeIds)> *callback) = 0;
virtual void removeRefreshCallback(std::function<void(const TypeIds &deletedTypeIds)> *callback) = 0;
virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration>

View File

@@ -3,6 +3,7 @@
#pragma once
#include "projectstorage.h"
#include "projectstorageexceptions.h"
#include "projectstorageids.h"
#include "sourcepath.h"
@@ -15,6 +16,7 @@
#include <sqlitetransaction.h>
#include <algorithm>
#include <utility>
namespace QmlDesigner {

View File

@@ -56,5 +56,5 @@ public:
AbstractView::PropertyChangeFlags propertyChange),
(override));
MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override));
MOCK_METHOD(void, refreshMetaInfos, (), (override));
MOCK_METHOD(void, refreshMetaInfos, (const QmlDesigner::TypeIds &), (override));
};

View File

@@ -122,6 +122,16 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId,
exportedTypeName[typeId].emplace_back(moduleId, typeName);
}
void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName)
{
ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(TypeId{}));
ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName)))
.WillByDefault(Return(TypeId{}));
exportedTypeName.erase(typeId);
}
PropertyDeclarationId ProjectStorageMock::createProperty(TypeId typeId,
Utils::SmallString name,
PropertyDeclarationTraits traits,
@@ -149,6 +159,24 @@ PropertyDeclarationId ProjectStorageMock::createProperty(TypeId typeId,
return propertyId;
}
void ProjectStorageMock::removeProperty(QmlDesigner::TypeId typeId, Utils::SmallString name)
{
auto propertyId = propertyDeclarationId(typeId, name);
ON_CALL(*this, propertyDeclarationId(Eq(typeId), Eq(name)))
.WillByDefault(Return(PropertyDeclarationId{}));
ON_CALL(*this, propertyName(Eq(propertyId)))
.WillByDefault(Return(std::optional<Utils::SmallString>{}));
ON_CALL(*this, propertyDeclaration(Eq(propertyId)))
.WillByDefault(Return(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>{}));
ON_CALL(*this, propertyDeclarationIds(Eq(typeId)))
.WillByDefault(Return(QVarLengthArray<PropertyDeclarationId, 128>{}));
ON_CALL(*this, localPropertyDeclarationIds(Eq(typeId)))
.WillByDefault(Return(QVarLengthArray<PropertyDeclarationId, 128>{}));
}
QmlDesigner::PropertyDeclarationId ProjectStorageMock::createProperty(
QmlDesigner::TypeId typeId, Utils::SmallString name, QmlDesigner::TypeId propertyTypeId)
{
@@ -187,6 +215,14 @@ void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &s
}
}
}
void setType(TypeId typeId, ModuleId moduleId, Utils::SmallStringView typeName, ProjectStorageMock &storage)
{
ON_CALL(storage, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
ON_CALL(storage, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName)))
.WillByDefault(Return(typeId));
}
} // namespace
TypeId ProjectStorageMock::createType(ModuleId moduleId,
@@ -205,13 +241,12 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId,
static TypeId typeId;
incrementBasicId(typeId);
ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName)))
.WillByDefault(Return(typeId));
setType(typeId, moduleId, typeName, *this);
addBaseProperties(typeId, baseTypeIds, *this);
addExportedTypeName(typeId, moduleId, typeName);
ON_CALL(*this, exportedTypeNames(Eq(typeId))).WillByDefault([&](TypeId id) {
return exportedTypeName[id];
});
PropertyDeclarationId defaultPropertyDeclarationId;
if (defaultPropertyName.size()) {
if (!defaultPropertyTypeId) {
@@ -225,8 +260,7 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId,
}
ON_CALL(*this, type(Eq(typeId)))
.WillByDefault(
Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits}));
.WillByDefault(Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits}));
ON_CALL(*this, isBasedOn(Eq(typeId), Eq(typeId))).WillByDefault(Return(true));
@@ -240,11 +274,20 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId,
ON_CALL(*this, prototypeAndSelfIds(Eq(typeId))).WillByDefault(Return(selfAndPrototypes));
ON_CALL(*this, prototypeIds(Eq(typeId))).WillByDefault(Return(baseTypeIds));
addBaseProperties(typeId, baseTypeIds, *this);
return typeId;
}
void ProjectStorageMock::removeType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName)
{
auto oldTypeId = typeId(moduleId, typeName);
setType(TypeId{}, moduleId, typeName, *this);
removeExportedTypeName(oldTypeId, moduleId, typeName);
ON_CALL(*this, type(Eq(oldTypeId))).WillByDefault(Return(std::optional<Storage::Info::Type>{}));
}
QmlDesigner::TypeId ProjectStorageMock::createType(QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::Storage::TypeTraits typeTraits,
@@ -286,6 +329,13 @@ QmlDesigner::TypeId ProjectStorageMock::createValue(QmlDesigner::ModuleId module
return createType(moduleId, typeName, Storage::TypeTraits::Value, baseTypeIds);
}
ProjectStorageMock::ProjectStorageMock()
{
ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) {
return exportedTypeName[id];
});
}
void ProjectStorageMock::setupQtQuick()
{
setupIsBasedOn(*this);

View File

@@ -16,6 +16,7 @@
class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface
{
public:
ProjectStorageMock();
virtual ~ProjectStorageMock() = default;
void setupQtQuick();
@@ -45,6 +46,10 @@ public:
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName);
void removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName);
QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
Utils::SmallStringView defaultPropertyName,
@@ -54,6 +59,8 @@ public:
QmlDesigner::TypeIds baseTypeIds = {},
QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{});
void removeType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName);
QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::Storage::TypeTraits typeTraits,
@@ -87,6 +94,8 @@ public:
Utils::SmallString name,
QmlDesigner::TypeId propertyTypeId);
void removeProperty(QmlDesigner::TypeId typeId, Utils::SmallString name);
void createSignal(QmlDesigner::TypeId typeId, Utils::SmallString name);
void createFunction(QmlDesigner::TypeId typeId, Utils::SmallString name);
void setPropertyEditorPathId(QmlDesigner::TypeId typeId, QmlDesigner::SourceId sourceId);
@@ -100,8 +109,14 @@ public:
(const QmlDesigner::Storage::Imports imports, QmlDesigner::SourceId sourceId),
(override));
MOCK_METHOD(void, addRefreshCallback, (std::function<void()> * callback), (override));
MOCK_METHOD(void, removeRefreshCallback, (std::function<void()> * callback), (override));
MOCK_METHOD(void,
addRefreshCallback,
(std::function<void(const QmlDesigner::TypeIds &)> * callback),
(override));
MOCK_METHOD(void,
removeRefreshCallback,
(std::function<void(const QmlDesigner::TypeIds &)> * callback),
(override));
MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override));

View File

@@ -319,4 +319,56 @@ TEST_F(PropertyComponentGenerator, get_imports)
Eq("import StudioTheme 1.0 as StudioTheme")));
}
TEST_F(PropertyComponentGenerator, set_model_to_null_removes_creates_only_monostates)
{
QString expected = getExpectedContent("real", "x", "x");
auto xProperty = itemMetaInfo.property("x");
generator.setModel(nullptr);
ASSERT_THAT(generator.create(xProperty), VariantWith<std::monostate>(std::monostate{}));
}
TEST_F(PropertyComponentGenerator, set_model_fromn_null_updates_internal_state)
{
generator.setModel(nullptr);
QString expected = getExpectedContent("real", "x", "x");
auto xProperty = itemMetaInfo.property("x");
generator.setModel(&model);
ASSERT_THAT(generator.create(xProperty), IsBasicProperty(StrippedStringEq(expected)));
}
TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_deleted)
{
auto xProperty = itemMetaInfo.property("x");
auto doubleMetaInfo = model.doubleMetaInfo();
projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(),
projectStorageMock.createModule("QML"),
"real");
generator.refreshMetaInfos({doubleMetaInfo.id()});
ASSERT_THAT(generator.create(xProperty), VariantWith<std::monostate>(std::monostate{}));
}
TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_added)
{
QString expected = getExpectedContent("real", "x", "x");
auto xProperty = itemMetaInfo.property("x");
auto doubleMetaInfo = model.doubleMetaInfo();
projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(),
projectStorageMock.createModule("QML"),
"real");
generator.refreshMetaInfos({doubleMetaInfo.id()});
projectStorageMock.addExportedTypeName(doubleMetaInfo.id(),
projectStorageMock.createModule("QML"),
"real");
generator.refreshMetaInfos({});
ASSERT_THAT(generator.create(xProperty), IsBasicProperty(StrippedStringEq(expected)));
}
} // namespace

View File

@@ -14,7 +14,7 @@ using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty;
using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty;
using QmlDesigner::PropertyMetaInfo;
class PropertyEditorTemplateGenerator : public ::testing::Test
class PropertyEditorComponentGenerator : public ::testing::Test
{
protected:
QmlDesigner::NodeMetaInfo createType(Utils::SmallStringView name,
@@ -85,13 +85,13 @@ protected:
QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(10);
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock{sourceId};
NiceMock<PropertyComponentGeneratorMock> propertyGeneratorMock;
QmlDesigner::PropertyEditorTemplateGenerator generator{propertyGeneratorMock};
QmlDesigner::PropertyEditorComponentGenerator generator{propertyGeneratorMock};
QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick");
QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo");
QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId<double>();
};
TEST_F(PropertyEditorTemplateGenerator, no_properties_and_no_imports)
TEST_F(PropertyEditorComponentGenerator, no_properties_and_no_imports)
{
QString expectedText{
R"xy(
@@ -110,12 +110,12 @@ TEST_F(PropertyEditorTemplateGenerator, no_properties_and_no_imports)
}
})xy"};
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, properties_without_component_are_not_shows)
TEST_F(PropertyEditorComponentGenerator, properties_without_component_are_not_shows)
{
QString expectedText{
R"xy(
@@ -135,12 +135,12 @@ TEST_F(PropertyEditorTemplateGenerator, properties_without_component_are_not_sho
})xy"};
createProperty(fooTypeInfo.id(), "x", {}, dummyTypeId);
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, show_component_button_for_a_component_node)
TEST_F(PropertyEditorComponentGenerator, show_component_button_for_a_component_node)
{
QString expectedText{
R"xy(
@@ -160,12 +160,12 @@ TEST_F(PropertyEditorTemplateGenerator, show_component_button_for_a_component_no
}
})xy"};
auto text = generator.create(fooTypeInfo, true);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), true);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, imports)
TEST_F(PropertyEditorComponentGenerator, imports)
{
QString expectedText{
R"xy(
@@ -187,12 +187,12 @@ TEST_F(PropertyEditorTemplateGenerator, imports)
})xy"};
setImports({"import QtQtuick", "import Studio 2.1"});
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, basic_property)
TEST_F(PropertyEditorComponentGenerator, basic_property)
{
QString expectedText{
R"xy(
@@ -220,12 +220,12 @@ TEST_F(PropertyEditorTemplateGenerator, basic_property)
})xy"};
createBasicProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Double{}");
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, basic_properties_with_base_type)
TEST_F(PropertyEditorComponentGenerator, basic_properties_with_base_type)
{
QString expectedText{
R"xy(
@@ -256,12 +256,12 @@ TEST_F(PropertyEditorTemplateGenerator, basic_properties_with_base_type)
auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()});
createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}");
auto text = generator.create(superFooInfo, false);
auto text = generator.create(superFooInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator,
TEST_F(PropertyEditorComponentGenerator,
only_handle_basic_properties_for_types_without_specifics_or_panes)
{
QString expectedText{
@@ -293,12 +293,12 @@ TEST_F(PropertyEditorTemplateGenerator,
createBasicProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperDouble{}");
projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId);
auto text = generator.create(superFooInfo, false);
auto text = generator.create(superFooInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, order_basic_property)
TEST_F(PropertyEditorComponentGenerator, order_basic_property)
{
QString expectedText{
R"xy(
@@ -330,12 +330,12 @@ TEST_F(PropertyEditorTemplateGenerator, order_basic_property)
createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "AnotherX{}");
createBasicProperty(fooTypeInfo.id(), "y", {}, dummyTypeId, "AndY{}");
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, complex_property)
TEST_F(PropertyEditorComponentGenerator, complex_property)
{
QString expectedText{
R"xy(
@@ -356,12 +356,12 @@ TEST_F(PropertyEditorTemplateGenerator, complex_property)
})xy"};
createComplexProperty(fooTypeInfo.id(), "value", {}, dummyTypeId, "Complex{}");
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, complex_properties_with_base_type)
TEST_F(PropertyEditorComponentGenerator, complex_properties_with_base_type)
{
QString expectedText{
R"xy(
@@ -385,12 +385,12 @@ TEST_F(PropertyEditorTemplateGenerator, complex_properties_with_base_type)
auto superFooInfo = createType("SuperFoo", {fooTypeInfo.id()});
createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}");
auto text = generator.create(superFooInfo, false);
auto text = generator.create(superFooInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator,
TEST_F(PropertyEditorComponentGenerator,
only_handle_complex_properties_for_types_without_specifics_or_panes)
{
QString expectedText{
@@ -415,12 +415,12 @@ TEST_F(PropertyEditorTemplateGenerator,
createComplexProperty(superFooInfo.id(), "value", {}, dummyTypeId, "SuperComplex{}");
projectStorageMock.setPropertyEditorPathId(fooTypeInfo.id(), sourceId);
auto text = generator.create(superFooInfo, false);
auto text = generator.create(superFooInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, ordered_complex_property)
TEST_F(PropertyEditorComponentGenerator, ordered_complex_property)
{
QString expectedText{
R"xy(
@@ -445,12 +445,12 @@ TEST_F(PropertyEditorTemplateGenerator, ordered_complex_property)
createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "ComplexFont{}");
createComplexProperty(fooTypeInfo.id(), "anchors", {}, dummyTypeId, "Anchors{}");
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}
TEST_F(PropertyEditorTemplateGenerator, basic_is_placed_before_complex_components)
TEST_F(PropertyEditorComponentGenerator, basic_is_placed_before_complex_components)
{
QString expectedText{
R"xy(
@@ -480,7 +480,7 @@ TEST_F(PropertyEditorTemplateGenerator, basic_is_placed_before_complex_component
createBasicProperty(fooTypeInfo.id(), "x", {}, dummyTypeId, "Double{}");
createComplexProperty(fooTypeInfo.id(), "font", {}, dummyTypeId, "Font{}");
auto text = generator.create(fooTypeInfo, false);
auto text = generator.create(fooTypeInfo.selfAndPrototypes(), false);
ASSERT_THAT(text, StrippedStringEq(expectedText));
}

View File

@@ -928,14 +928,16 @@ TEST_F(Model, remove_refresh_callback_from_project_storage)
TEST_F(Model, refresh_callback_is_calling_abstract_view)
{
std::function<void()> *callback = nullptr;
const QmlDesigner::TypeIds typeIds = {QmlDesigner::TypeId::create(3),
QmlDesigner::TypeId::create(1)};
std::function<void(const QmlDesigner::TypeIds &)> *callback = nullptr;
ON_CALL(projectStorageMock, addRefreshCallback(_)).WillByDefault([&](auto *c) { callback = c; });
QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}};
model.attachView(&viewMock);
EXPECT_CALL(viewMock, refreshMetaInfos());
EXPECT_CALL(viewMock, refreshMetaInfos(typeIds));
(*callback)();
(*callback)(typeIds);
}
} // namespace

View File

@@ -7195,11 +7195,11 @@ TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name)
TEST_F(ProjectStorage, call_refresh_callback_after_synchronization)
{
auto package{createSimpleSynchronizationPackage()};
MockFunction<void()> callbackMock;
MockFunction<void(const QmlDesigner::TypeIds &)> callbackMock;
auto callback = callbackMock.AsStdFunction();
storage.addRefreshCallback(&callback);
EXPECT_CALL(callbackMock, Call());
EXPECT_CALL(callbackMock, Call(_));
storage.synchronize(package);
}