QmlDesigner: Improve node creation with project storage

The file url was missing, so no valid source id could be created. That
lead to invalid imports.

The qualified path for the node instances is now created in the node
instance view.

Change-Id: I2685ab2fdbdb0fa8a5f89793be52c8fae2b8db8c
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
This commit is contained in:
Marco Bubke
2024-04-11 18:13:10 +02:00
parent c9f9804edf
commit db84bc43a9
15 changed files with 95 additions and 29 deletions

View File

@@ -64,13 +64,14 @@ namespace QmlDesigner {
DesignDocument acts as a facade to a model representing a qml document,
and the different views/widgets accessing it.
*/
DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies,
DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath,
ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies)
#ifdef QDS_USE_PROJECTSTORAGE
: m_documentModel(Model::create(projectStorageDependencies,
"Item",
{Import::createLibraryImport("QtQuick")},
{},
filePath,
std::make_unique<ModelResourceManagement>()))
#else
: m_documentModel(

View File

@@ -41,7 +41,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject
Q_OBJECT
public:
DesignDocument(ProjectStorageDependencies projectStorageDependencies,
DesignDocument(const QUrl &filePath,
ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies);
~DesignDocument() override;

View File

@@ -166,6 +166,7 @@ public:
NodeMetaInfo qtQmlConnectionsMetaInfo() const;
NodeMetaInfo qtQmlModelsListModelMetaInfo() const;
NodeMetaInfo qtQmlModelsListElementMetaInfo() const;
NodeMetaInfo qtQmlXmlListModelXmlListModelRoleMetaInfo() const;
NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const;
NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const;

View File

@@ -167,6 +167,7 @@ public:
bool isQtMultimediaSoundEffect() const;
bool isQtObject() const;
bool isQtQmlConnections() const;
bool isQtQmlModelsListElement() const;
bool isQtQuick3DBakedLightmap() const;
bool isQtQuick3DBuffer() const;
bool isQtQuick3DCamera() const;
@@ -176,7 +177,6 @@ public:
bool isQtQuick3DInstanceList() const;
bool isQtQuick3DInstanceListEntry() const;
bool isQtQuick3DLight() const;
bool isQtQuickListElement() const;
bool isQtQuickListModel() const;
bool isQtQuickListView() const;
bool isQtQuick3DMaterial() const;

View File

@@ -64,6 +64,8 @@
#include <qmlitemnode.h>
#include <rewriterview.h>
#include <projectstorage/projectstorage.h>
#include <utils/hdrimage.h>
#include <coreplugin/messagemanager.h>
@@ -205,22 +207,17 @@ NodeInstanceView::~NodeInstanceView()
static bool isSkippedRootNode(const ModelNode &node)
{
static const PropertyNameList skipList({"Qt.ListModel", "QtQuick.ListModel", "Qt.ListModel", "QtQuick.ListModel"});
if (skipList.contains(node.type()))
return true;
return false;
return node.metaInfo().isQtQuickListModel();
}
static bool isSkippedNode(const ModelNode &node)
{
static const PropertyNameList skipList({"QtQuick.XmlRole", "Qt.XmlRole", "QtQuick.ListElement", "Qt.ListElement"});
auto model = node.model();
if (skipList.contains(node.type()))
return true;
auto listElement = model->qtQmlModelsListElementMetaInfo();
auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo();
return false;
return node.metaInfo().isBasedOn(listElement, xmlRole);
}
static bool parentTakesOverRendering(const ModelNode &modelNode)
@@ -644,7 +641,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(),
key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
};
}
break;
case AuxiliaryDataType::NodeInstanceAuxiliary:
@@ -656,7 +653,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(),
key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
};
}
break;
case AuxiliaryDataType::NodeInstancePropertyOverwrite:
@@ -991,6 +988,8 @@ QRectF NodeInstanceView::sceneRect() const
return {};
}
namespace {
QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
{
QList<ModelNode> filteredNodeList;
@@ -1003,14 +1002,12 @@ QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
return filteredNodeList;
}
namespace {
bool shouldSendAuxiliary(const AuxiliaryDataKey &key)
{
return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite
|| key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty
|| key == lockedProperty;
}
} // namespace
bool parentIsBehavior(ModelNode node)
{
@@ -1024,6 +1021,31 @@ bool parentIsBehavior(ModelNode node)
return false;
}
TypeName createQualifiedTypeName(const ModelNode &node)
{
if (!node)
return {};
#ifdef QDS_USE_PROJECTSTORAGE
auto model = node.model();
auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId());
if (exportedTypes.size()) {
const auto &exportedType = exportedTypes.front();
Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId);
typeName += '/';
typeName += exportedType.name;
return typeName.toQByteArray();
}
return {};
#else
return node.type();
#endif
}
} // namespace
CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
{
QList<ModelNode> nodeList = allModelNodes();
@@ -1079,8 +1101,9 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
nodeFlags |= InstanceContainer::ParentTakesOverRendering;
const auto modelNode = instance.modelNode();
InstanceContainer container(instance.instanceId(),
modelNode.type(),
createQualifiedTypeName(modelNode),
modelNode.majorVersion(),
modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode),
@@ -1243,7 +1266,7 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis
const auto modelNode = instance.modelNode();
InstanceContainer container(instance.instanceId(),
modelNode.type(),
createQualifiedTypeName(modelNode),
modelNode.majorVersion(),
modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode),
@@ -1850,7 +1873,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
ModelNodePreviewImageData imageData;
imageData.id = modelNode.id();
imageData.type = QString::fromLatin1(modelNode.type());
imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode));
const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) {
@@ -1958,7 +1981,7 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model
if (m_imageDataMap.contains(id)) {
imageData = m_imageDataMap[id];
} else {
imageData.type = QString::fromLatin1(modelNode.type());
imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode));
imageData.id = id;
m_imageDataMap.insert(id, imageData);
}

View File

@@ -2766,7 +2766,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const
}
}
bool NodeMetaInfo::isQtQuickListElement() const
bool NodeMetaInfo::isQtQmlModelsListElement() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;

View File

@@ -2210,6 +2210,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const
}
}
NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;
return createNodeMetaInfo<QtQml_XmlListModel, XmlListModelRole>();
} else {
return metaInfo("QtQml.XmlListModel.XmlListModelRole");
}
}
NodeMetaInfo Model::qmlQtObjectMetaInfo() const
{
if constexpr (useProjectStorage()) {

View File

@@ -501,7 +501,7 @@ public:
if (!propertyMetaInfo.isValid()) {
const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper();
// Only list elements might have unknown properties.
if (!node.metaInfo().isQtQuickListElement() && !isAttached) {
if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) {
qCInfo(texttomodelMergerLog)
<< Q_FUNC_INFO << "\nUnknown property"
<< propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line"

View File

@@ -91,6 +91,7 @@ inline constexpr char QtMultimedia[] = "QtMultimedia";
inline constexpr char QtObject[] = "QtObject";
inline constexpr char QtQml[] = "QtQml";
inline constexpr char QtQml_Models[] = "QtQml.Models";
inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel";
inline constexpr char QtQuick3D[] = "QtQuick3D";
inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D";
inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative";
@@ -131,6 +132,7 @@ inline constexpr char Transition[] = "Transition";
inline constexpr char UIntType[] = "uint";
inline constexpr char View3D[] = "View3D";
inline constexpr char Window[] = "Window";
inline constexpr char XmlListModelRole[] = "XmlListModelRole";
inline constexpr char color[] = "color";
inline constexpr char date[] = "date";
inline constexpr char font[] = "font";
@@ -176,6 +178,7 @@ class CommonTypeCache
CacheType<QtMultimedia, SoundEffect>,
CacheType<QtQml_Models, ListElement>,
CacheType<QtQml_Models, ListModel>,
CacheType<QtQml_XmlListModel, XmlListModelRole>,
CacheType<QtQuick, BorderImage>,
CacheType<QtQuick, GridView>,
CacheType<QtQuick, Image>,

View File

@@ -168,7 +168,7 @@ public:
return moduleId;
}
Utils::SmallString moduleName(ModuleId moduleId) const
Utils::SmallString moduleName(ModuleId moduleId) const override
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get module name"_t,
@@ -3278,8 +3278,9 @@ private:
auto update = [&](const TypeWithDefaultPropertyView &view,
const Storage::Synchronization::Type &value) {
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"reset default properties by update"_t,
NanotraceHR::Tracer tracer{"synchronize default properties by update"_t,
projectStorageCategory(),
keyValue("type id", value.typeId),
keyValue("value", value),
keyValue("view", view)};
@@ -3294,7 +3295,8 @@ private:
updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId);
tracer.end(keyValue("updated", "yes"));
tracer.end(keyValue("updated", "yes"),
keyValue("default property id", valueDefaultPropertyId));
return Sqlite::UpdateChange::Update;
};
@@ -3324,6 +3326,7 @@ private:
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"reset changed default properties by update"_t,
projectStorageCategory(),
keyValue("type id", value.typeId),
keyValue("value", value),
keyValue("view", view)};

View File

@@ -32,6 +32,7 @@ public:
virtual void removeObserver(ProjectStorageObserver *observer) = 0;
virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0;
virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;
virtual TypeId typeId(ModuleId moduleId,

View File

@@ -230,7 +230,9 @@ void DocumentManager::setCurrentDesignDocument(Core::IEditor *editor)
auto found = m_designDocuments.find(editor);
if (found == m_designDocuments.end()) {
auto &inserted = m_designDocuments[editor] = std::make_unique<DesignDocument>(
m_projectManager.projectStorageDependencies(), m_externalDependencies);
editor->document()->filePath().toString(),
m_projectManager.projectStorageDependencies(),
m_externalDependencies);
m_currentDesignDocument = inserted.get();
m_currentDesignDocument->setEditor(editor);
} else {

View File

@@ -51,6 +51,7 @@ ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName)
incrementBasicId(moduleId);
ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName));
ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId));
return moduleId;
@@ -122,6 +123,14 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId,
exportedTypeName[typeId].emplace_back(moduleId, typeName);
}
void ProjectStorageMock::addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::SourceId sourceId)
{
exportedTypeNameBySourceId[{typeId, sourceId}].emplace_back(moduleId, typeName);
}
void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName)
@@ -364,6 +373,10 @@ ProjectStorageMock::ProjectStorageMock()
ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) {
return exportedTypeName[id];
});
ON_CALL(*this, exportedTypeNames(_, _)).WillByDefault([&](TypeId typeId, SourceId sourceId) {
return exportedTypeNameBySourceId[{typeId, sourceId}];
});
}
void ProjectStorageMock::setupQtQuick()

View File

@@ -46,6 +46,11 @@ public:
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName);
void addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::SourceId sourceId);
void removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName);
@@ -122,6 +127,7 @@ public:
MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override));
MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override));
MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override));
MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>,
propertyDeclaration,
@@ -331,6 +337,8 @@ public:
QmlDesigner::Storage::Info::CommonTypeCache<QmlDesigner::ProjectStorageInterface> typeCache{*this};
std::map<QmlDesigner::TypeId, QmlDesigner::Storage::Info::ExportedTypeNames> exportedTypeName;
std::map<std::pair<QmlDesigner::TypeId, QmlDesigner::SourceId>, QmlDesigner::Storage::Info::ExportedTypeNames>
exportedTypeNameBySourceId;
};
class ProjectStorageMockWithQtQtuick : public ProjectStorageMock

View File

@@ -141,7 +141,7 @@ TEST_F(ModelUtils, find_lowest_common_ancestor_when_one_of_the_nodes_is_parent)
ASSERT_THAT(commonAncestor, parentNode);
}
TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandFather)
TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandfather)
{
auto grandFatherNode = model.createModelNode("Item");
auto fatherNode = model.createModelNode("Item");