diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2023-07-20 22:41:50 +0200 |
---|---|---|
committer | Tim Jenssen <tim.jenssen@qt.io> | 2023-07-21 10:20:45 +0000 |
commit | 7df557c0babb11f0c94ca118cfdec7cf8c142f71 (patch) | |
tree | fa8bf8c56c044438ebe11254e4d62230fcdebb59 /src/plugins | |
parent | 012f926f7c28e34eadfe93f1365d63f0a11b4c80 (diff) | |
parent | 655ec9d94880cb7f3c5ff3bf4917705f7629b0d3 (diff) |
Merge remote-tracking branch 'origin/qds/dev'
Conflicts: tests/unit/tests/printers/gtest-creator-printing.cpp
Change-Id: I5f791161ca1a2966e98a3ca55bc60e5bcbb8f58f
Diffstat (limited to 'src/plugins')
96 files changed, 2665 insertions, 705 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b6d9f10bfb6..405ad5c00a3 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -82,7 +82,6 @@ if(TARGET QmlDesignerCore) add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") endif() - extend_qtc_library(QmlDesignerCore CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON @@ -186,23 +185,40 @@ extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES - abstractproperty.h abstractview.h + basetexteditmodifier.h + bytearraymodifier.h + componenttextmodifier.h + forwardview.h + itemlibraryinfo.h + metainforeader.h + model.h + nodehints.h + plaintexteditmodifier.h + nodeinstanceview.h + propertyparser.h + rewriterview.h + subcomponentmanager.h + textmodifier.h +) + +extend_qtc_library(QmlDesignerCore + SOURCES_PROPERTIES SKIP_AUTOGEN ON + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include + SOURCES + abstractproperty.h anchorline.h annotation.h asynchronousexplicitimagecache.h asynchronousimagecache.h auxiliarydata.h auxiliarydataproperties.h - basetexteditmodifier.h bindingproperty.h - componenttextmodifier.h customnotifications.h documentmessage.h enumerationmetainfo.h exception.h externaldependenciesinterface.h - forwardview.h imagecacheauxiliarydata.h import.h invalidargumentexception.h @@ -214,33 +230,26 @@ extend_qtc_library(QmlDesignerCore invalidqmlsourceexception.h invalidreparentingexception.h invalidslideindexexception.h - itemlibraryinfo.h mathutils.h metainfo.h - metainforeader.h - model.h modelfwd.h modelmerger.h modelnode.h modelnodepositionstorage.h nodeabstractproperty.h - nodehints.h nodeinstance.h - nodeinstanceview.h nodelistproperty.h nodemetainfo.h nodeproperty.h notimplementedexception.h - plaintexteditmodifier.h propertycontainer.h propertymetainfo.h propertynode.h - propertyparser.h qmlanchors.h qmlchangeset.h qmlconnections.h - qmldesignercorelib_global.h qmldesignercorelib_exports.h + qmldesignercorelib_global.h qmlitemnode.h qmlmodelnodefacade.h qmlobjectnode.h @@ -248,14 +257,11 @@ extend_qtc_library(QmlDesignerCore qmltimeline.h qmltimelinekeyframegroup.h removebasestateexception.h - rewriterview.h rewritingexception.h signalhandlerproperty.h stringutils.h stylesheetmerger.h - subcomponentmanager.h synchronousimagecache.h - textmodifier.h variantproperty.h ) @@ -382,7 +388,7 @@ extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore SOURCES_PREFIX designercore/projectstorage PUBLIC_INCLUDES designercore/projectstorage - SOURCES_PROPERTIES SKIP_AUTOMOC ON + SOURCES_PROPERTIES SKIP_AUTOGEN ON SOURCES commontypecache.h directorypathcompressor.h @@ -407,7 +413,7 @@ extend_qtc_library(QmlDesignerCore projectstorage.cpp projectstorage.h sourcepath.h sourcepathcache.h - sourcepathcache.h + sourcepathcacheinterface.h sourcepathcachetypes.h sourcepathview.h storagecache.h diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 7b0604c19a8..2cc6606efa1 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -4,12 +4,13 @@ #include "componentexporter.h" #include "exportnotification.h" -#include "designdocument.h" -#include "nodemetainfo.h" -#include "qmldesignerplugin.h" -#include "rewriterview.h" -#include "qmlitemnode.h" -#include "qmlobjectnode.h" +#include <designdocument.h> +#include <model/modelutils.h> +#include <nodemetainfo.h> +#include <qmldesignerplugin.h> +#include <qmlitemnode.h> +#include <qmlobjectnode.h> +#include <rewriterview.h> #include <coreplugin/editormanager/editormanager.h> #include <projectexplorer/project.h> @@ -340,12 +341,11 @@ QString AssetExporter::componentUuid(const ModelNode &instance) const // Returns the UUID of the component's root node // Empty string is returned if the node is not an instance of a component within // the project. - NodeMetaInfo metaInfo = instance.metaInfo(); - if (!metaInfo.isValid()) - return {}; - const QString path = metaInfo.componentFileName(); - if (m_componentUuidCache.contains(path)) - return m_componentUuidCache[path]; + if (instance) { + const QString path = ModelUtils::componentFilePath(instance); + return m_componentUuidCache.value(path); + } + return {}; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp index 7ec9841198a..701e9fdd244 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp @@ -28,7 +28,7 @@ static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node) if (!node.isValid() || node.type().isEmpty()) return {}; - for (auto &info : node.metaInfo().superClasses()) + for (auto &info : node.metaInfo().prototypes()) lineage.append(info.typeName()); return lineage; diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 8c8d0911793..10e8181d8d5 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -91,10 +91,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) const ModelNode node = propertyEditorValue->modelNode(); if (node.isValid()) { - m_backendValueTypeName = node.metaInfo() - .property(propertyEditorValue->name()) - .propertyType() - .simplifiedTypeName(); + m_backendValueType = node.metaInfo().property(propertyEditorValue->name()).propertyType(); QString nodeId = node.id(); if (nodeId.isEmpty()) @@ -102,9 +99,11 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) m_targetName = nodeId + "." + propertyEditorValue->name(); - if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown") + if (!m_backendValueType || m_backendValueType.isAlias()) { if (QmlObjectNode::isValidQmlObjectNode(node)) - m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name()); + m_backendValueType = node.model()->metaInfo( + QmlObjectNode(node).instanceType(propertyEditorValue->name())); + } } emit backendValueChanged(); @@ -135,7 +134,7 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode) m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>(); if (m_modelNode.isValid()) - m_backendValueTypeName = "bool"; + m_backendValueType = m_modelNode.model()->boolMetaInfo(); emit stateModelNodeChanged(); } @@ -153,9 +152,9 @@ void BindingEditor::setModelNode(const ModelNode &modelNode) m_modelNode = modelNode; } -void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName) +void BindingEditor::setBackendValueType(const NodeMetaInfo &backendValueType) { - m_backendValueTypeName = backendValueTypeName; + m_backendValueType = backendValueType; emit backendValueChanged(); } @@ -165,65 +164,80 @@ void BindingEditor::setTargetName(const QString &target) m_targetName = target; } +namespace { +template<typename Tuple> +bool isType(const Tuple &types, const TypeName &compareType) +{ + return std::apply([&](const auto &...type) { return ((type == compareType) || ...); }, types); +} + +template<typename... Tuple> +bool isType(const TypeName &first, const TypeName &second, const Tuple &...types) +{ + return ((types == first) || ...) && ((types == second) || ...); +} + +bool compareTypes(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType) +{ + if constexpr (useProjectStorage()) { + return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType + || (targetType.isNumber() && sourceType.isNumber()) + || (targetType.isColor() && sourceType.isColor()) + || (targetType.isString() && sourceType.isString()); + } else { + const TypeName source = sourceType.simplifiedTypeName(); + const TypeName target = targetType.simplifiedTypeName(); + + static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var"); + + return isType(variantTypes, target) || isType(variantTypes, source) + || targetType == sourceType || isType(target, source, "double", "real", "int") + || isType(target, source, "QColor", "color") + || isType(target, source, "QString", "string"); + } +} +} // namespace + void BindingEditor::prepareBindings() { - if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty()) + if (!m_modelNode.isValid() || !m_backendValueType) { return; + } const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes(); QList<BindingEditorDialog::BindingOption> bindings; - const QVarLengthArray<TypeName> variantTypes = {"alias", "unknown", "variant", "var"}; - const QVarLengthArray<TypeName> numericTypes = {"double", "real", "int"}; - const QVarLengthArray<TypeName> colorTypes = {"QColor", "color"}; - const QVarLengthArray<TypeName> stringTypes = {"QString", "string"}; - - auto isVariant = [&variantTypes](const TypeName &compareType) { - return variantTypes.contains(compareType); - }; - auto isNumeric = [&numericTypes](const TypeName &compareType) { - return numericTypes.contains(compareType); - }; - auto isColor = [&colorTypes](const TypeName &compareType) { - return colorTypes.contains(compareType); - }; - auto isString = [&stringTypes](const TypeName &compareType) { - return stringTypes.contains(compareType); - }; - - auto compareTypes = [&](const TypeName &targetType, const TypeName &sourceType) { - return isVariant(targetType) || isVariant(sourceType) || (targetType == sourceType) - || (isNumeric(targetType) && isNumeric(sourceType)) - || (isColor(targetType) && isColor(sourceType)) - || (isString(targetType) && isString(sourceType)); - }; - for (const auto &objnode : allNodes) { BindingEditorDialog::BindingOption binding; for (const auto &property : objnode.metaInfo().properties()) { - const TypeName &propertyTypeName = property.propertyType().simplifiedTypeName(); + const auto &propertyType = property.propertyType(); - if (compareTypes(m_backendValueTypeName, propertyTypeName)) + if (compareTypes(m_backendValueType, propertyType)) { binding.properties.append(QString::fromUtf8(property.name())); + } } //dynamic properties: for (const BindingProperty &bindingProperty : objnode.bindingProperties()) { if (bindingProperty.isValid()) { if (bindingProperty.isDynamic()) { - const TypeName dynamicTypeName = bindingProperty.dynamicTypeName(); - if (compareTypes(m_backendValueTypeName, dynamicTypeName)) + auto model = bindingProperty.model(); + const auto dynamicType = model->metaInfo(bindingProperty.dynamicTypeName()); + if (compareTypes(m_backendValueType, dynamicType)) { binding.properties.append(QString::fromUtf8(bindingProperty.name())); + } } } } for (const VariantProperty &variantProperty : objnode.variantProperties()) { if (variantProperty.isValid()) { if (variantProperty.isDynamic()) { - const TypeName dynamicTypeName = variantProperty.dynamicTypeName(); - if (compareTypes(m_backendValueTypeName, dynamicTypeName)) + auto model = variantProperty.model(); + const auto dynamicType = model->metaInfo(variantProperty.dynamicTypeName()); + if (compareTypes(m_backendValueType, dynamicType)) { binding.properties.append(QString::fromUtf8(variantProperty.name())); + } } } } @@ -244,10 +258,11 @@ void BindingEditor::prepareBindings() BindingEditorDialog::BindingOption binding; for (const auto &property : metaInfo.properties()) { - const TypeName propertyTypeName = property.propertyType().typeName(); + const auto propertyType = property.propertyType(); - if (compareTypes(m_backendValueTypeName, propertyTypeName)) + if (compareTypes(m_backendValueType, propertyType)) { binding.properties.append(QString::fromUtf8(property.name())); + } } if (!binding.properties.isEmpty()) { @@ -260,15 +275,24 @@ void BindingEditor::prepareBindings() } if (!bindings.isEmpty() && !m_dialog.isNull()) - m_dialog->setAllBindings(bindings, m_backendValueTypeName); + m_dialog->setAllBindings(bindings, m_backendValueType); } void BindingEditor::updateWindowName() { - if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) { - const QString targetString = " [" - + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) - + QString::fromUtf8(m_backendValueTypeName) + "]"; + if (!m_dialog.isNull() && m_backendValueType) { + QString targetString; + if constexpr (useProjectStorage()) { + auto exportedTypeNames = m_backendValueType.exportedTypeNamesForSourceId( + m_modelNode.model()->fileUrlSourceId()); + if (exportedTypeNames.size()) { + targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) + + exportedTypeNames.front().name.toQString() + "]"; + } + } else { + targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) + + QString::fromUtf8(m_backendValueType.simplifiedTypeName()) + "]"; + } m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString); } diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h index 59ce036c18c..52aa0884f69 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h @@ -49,7 +49,7 @@ public: //3. modelnode + backend value type name + optional target name void setModelNode(const ModelNode &modelNode); - void setBackendValueTypeName(const TypeName &backendValueTypeName); + void setBackendValueType(const NodeMetaInfo &backendValueType); void setTargetName(const QString &target); Q_INVOKABLE void prepareBindings(); @@ -77,7 +77,7 @@ private: QVariant m_modelNodeBackend; QVariant m_stateModelNode; QmlDesigner::ModelNode m_modelNode; - TypeName m_backendValueTypeName; + NodeMetaInfo m_backendValueType; QString m_targetName; }; diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp index 0cb178603f3..ac5dd615337 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp @@ -79,7 +79,7 @@ void BindingEditorDialog::adjustProperties() m_comboBoxProperty->setCurrentText(property); } -void BindingEditorDialog::setAllBindings(const QList<BindingOption> &bindings, const TypeName &type) +void BindingEditorDialog::setAllBindings(const QList<BindingOption> &bindings, const NodeMetaInfo &type) { m_lock = true; @@ -118,7 +118,7 @@ void BindingEditorDialog::setupComboBoxes() void BindingEditorDialog::setupCheckBox() { - const bool visible = (m_type == "bool"); + const bool visible = m_type.isBool(); m_checkBoxNot->setVisible(visible); } diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h index ba14a0ee38d..f2995f0a1bf 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h @@ -6,6 +6,8 @@ #include <bindingeditor/abstracteditordialog.h> +#include <nodemetainfo.h> + QT_BEGIN_NAMESPACE class QComboBox; class QCheckBox; @@ -35,7 +37,7 @@ public: void adjustProperties() override; - void setAllBindings(const QList<BindingOption> &bindings, const TypeName &type); + void setAllBindings(const QList<BindingOption> &bindings, const NodeMetaInfo &type); private: void setupUIComponents(); @@ -53,7 +55,7 @@ private: QCheckBox *m_checkBoxNot = nullptr; QList<BindingOption> m_bindings; - TypeName m_type; + NodeMetaInfo m_type; }; } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index 7ff4e6f8722..75976fd7210 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -3,13 +3,14 @@ #include "modelnodecontextmenu_helper.h" -#include <nodemetainfo.h> -#include <modelnode.h> -#include <qmlitemnode.h> #include <bindingproperty.h> +#include <model/modelutils.h> +#include <modelnode.h> +#include <nodemetainfo.h> #include <nodeproperty.h> -#include <qmldesignerplugin.h> #include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <qmlitemnode.h> #include <QFile> @@ -76,10 +77,11 @@ bool selectionHasSameParent(const SelectionContext &selectionState) bool fileComponentExists(const ModelNode &modelNode) { - if (!modelNode.metaInfo().isFileComponent()) + if (!modelNode.metaInfo().isFileComponent()) { return true; + } - const QString fileName = modelNode.metaInfo().componentFileName(); + const QString fileName = ModelUtils::componentFilePath(modelNode); if (fileName.contains("qml/QtQuick")) return false; @@ -97,7 +99,8 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); if (selectionState.view() && node.hasMetaInfo()) { - QString fileName = node.metaInfo().componentFileName(); // absolute path + QString fileName = ModelUtils::componentFilePath(node); + if (fileName.isEmpty()) { // Node is not a file component, so we have to check if the current doc itself is fileName = node.model()->fileUrl().toLocalFile(); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index a565ae03fcf..fba2f25a05c 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -187,6 +187,7 @@ public: materialPreviewEnvironment, materialPreviewModel, material_medium, + maxBar_small, mergeCells, merge_small, minus, @@ -200,6 +201,7 @@ public: move_medium, newMaterial, nextFile_large, + normalBar_small, openLink, openMaterialBrowser, orientation, diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 29bee73620a..fc30ab99f9d 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -480,6 +480,11 @@ void ViewManager::exportAsImage() d->formEditorView.exportAsImage(); } +QImage ViewManager::takeFormEditorScreenshot() +{ + return d->formEditorView.takeFormEditorScreenshot(); +} + void ViewManager::reformatFileUsingTextEditorView() { d->textEditorView.reformatFile(); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index 4201065f99f..a3cacbe907e 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -74,6 +74,7 @@ public: const AbstractView *view() const; void exportAsImage(); + QImage takeFormEditorScreenshot(); void reformatFileUsingTextEditorView(); QWidgetAction *componentViewAction() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 191900d5e9d..2b2025ed83a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -13,14 +13,16 @@ #include <rewritertransaction.h> #include <rewriterview.h> +#include <utils/qtcassert.h> + #include <QMessageBox> #include <QTimer> namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *parent) - : QStandardItemModel(parent) - , m_connectionView(parent) + : QStandardItemModel(parent), m_connectionView(parent), + m_delegate(new BindingModelBackendDelegate(this)) { connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged); } @@ -40,6 +42,31 @@ void BindingModel::resetModel() endResetModel(); } +void BindingModel::add() +{ + addBindingForCurrentNode(); +} + +void BindingModel::remove(int row) +{ + deleteBindindByRow(row); +} + +int BindingModel::currentIndex() const +{ + return m_currentIndex; +} + +void BindingModel::setCurrentIndex(int i) +{ + if (m_currentIndex == i) + return; + + m_currentIndex = i; + + emit currentIndexChanged(); +} + void BindingModel::bindingChanged(const BindingProperty &bindingProperty) { m_handleDataChanged = false; @@ -232,6 +259,19 @@ void BindingModel::addBindingForCurrentNode() } } +static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property) +{ + item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole); + item->setData(property.name(), BindingModel::TargetPropertyNameRole); + + const AbstractProperty source = property.resolveToProperty(); + + if (source.isValid()) { + item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole); + item->setData(source.name(), BindingModel::SourcePropertyNameRole); + } +} + void BindingModel::addBindingProperty(const BindingProperty &property) { QStandardItem *idItem; @@ -248,6 +288,7 @@ void BindingModel::addBindingProperty(const BindingProperty &property) QList<QStandardItem*> items; items.append(idItem); + updateDisplayRoles(idItem, property); items.append(targetPropertyNameItem); QString sourceNodeName; @@ -267,6 +308,10 @@ void BindingModel::updateBindingProperty(int rowNumber) BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); if (bindingProperty.isValid()) { + QStandardItem *idItem = item(rowNumber, 0); + if (idItem) + updateDisplayRoles(idItem, bindingProperty); + QString targetPropertyName = QString::fromUtf8(bindingProperty.name()); updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName); QString sourceNodeName; @@ -355,6 +400,7 @@ void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty & { item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1); item->setData(bindingProperty.name(), Qt::UserRole + 2); + updateDisplayRoles(item, bindingProperty); } int BindingModel::findRowForBinding(const BindingProperty &bindingProperty) @@ -369,6 +415,8 @@ int BindingModel::findRowForBinding(const BindingProperty &bindingProperty) bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty) { + //TODO reimplement using existing helper functions + //### todo we assume no expressions yet const QString expression = bindingProperty.expression(); @@ -438,4 +486,159 @@ void BindingModel::handleException() resetModel(); } +QHash<int, QByteArray> BindingModel::roleNames() const +{ + static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"}, + {TargetPropertyNameRole, "targetProperty"}, + {SourceNameRole, "source"}, + {SourcePropertyNameRole, "sourceProperty"}}; + + return roleNames; +} + +BindingModelBackendDelegate *BindingModel::delegate() const +{ + return m_delegate; +} + +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent) +{ + connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleSourceNodeChanged(); + }); + + connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleSourcePropertyChanged(); + }); +} + +int BindingModelBackendDelegate::currentRow() const +{ + return m_currentRow; +} + +void BindingModelBackendDelegate::setCurrentRow(int i) +{ + // See BindingDelegate::createEditor + + if (m_currentRow == i) + return; + + m_currentRow = i; + + //setup + + BindingModel *model = qobject_cast<BindingModel *>(parent()); + + QTC_ASSERT(model, return ); + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + + QString idLabel = bindingProperty.parentModelNode().id(); + if (idLabel.isEmpty()) + idLabel = bindingProperty.parentModelNode().simplifiedTypeName(); + + m_targetNode = idLabel; + + emit targetNodeChanged(); + + m_property.setModel(model->possibleTargetProperties(bindingProperty)); + m_property.setCurrentText(QString::fromUtf8(bindingProperty.name())); + + QStringList sourceNodes; + + for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { + if (!modelNode.id().isEmpty()) + sourceNodes.append(modelNode.id()); + } + + std::sort(sourceNodes.begin(), sourceNodes.end()); + m_sourceNode.setModel(sourceNodes); + + QString sourceNodeName; + QString sourcePropertyName; + model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); + + m_sourceNode.setCurrentText(sourceNodeName); + + m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty)); + m_sourceNodeProperty.setCurrentText(sourcePropertyName); +} + +void BindingModelBackendDelegate::handleException() +{ + QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); + //reset +} + +QString BindingModelBackendDelegate::targetNode() const +{ + return m_targetNode; +} + +StudioQmlComboBoxBackend *BindingModelBackendDelegate::property() +{ + return &m_property; +} + +StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceNode() +{ + return &m_sourceNode; +} + +StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() +{ + return &m_sourceNodeProperty; +} + +void BindingModelBackendDelegate::handleSourceNodeChanged() +{ + BindingModel *model = qobject_cast<BindingModel *>(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView(), return ); + + const QString sourceNode = m_sourceNode.currentText(); + const QString sourceProperty = m_sourceNodeProperty.currentText(); + + QString expression; + if (sourceProperty.isEmpty()) { + expression = sourceNode; + } else { + expression = sourceNode + QLatin1String(".") + sourceProperty; + } + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + model->connectionView()->executeInTransaction("BindingModel::updateExpression", + [&bindingProperty, expression]() { + bindingProperty.setExpression( + expression.trimmed()); + }); +} + +void BindingModelBackendDelegate::handleSourcePropertyChanged() +{ + BindingModel *model = qobject_cast<BindingModel *>(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView(), return ); + + const QString sourceNode = m_sourceNode.currentText(); + const QString sourceProperty = m_sourceNodeProperty.currentText(); + + QString expression; + if (sourceProperty.isEmpty()) { + expression = sourceNode; + } else { + expression = sourceNode + QLatin1String(".") + sourceProperty; + } + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + model->connectionView()->executeInTransaction("BindingModel::updateExpression", + [&bindingProperty, expression]() { + bindingProperty.setExpression( + expression.trimmed()); + }); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 12685679e94..1f469876852 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -7,16 +7,22 @@ #include <bindingproperty.h> #include <variantproperty.h> +#include <studioquickwidget.h> + #include <QStandardItemModel> namespace QmlDesigner { class ConnectionView; +class BindingModelBackendDelegate; class BindingModel : public QStandardItemModel { Q_OBJECT + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT) + public: enum ColumnRoles { TargetModelNodeRow = 0, @@ -24,6 +30,15 @@ public: SourceModelNodeRow = 2, SourcePropertyNameRow = 3 }; + + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + TargetPropertyNameRole, + SourceNameRole, + SourcePropertyNameRole + }; + BindingModel(ConnectionView *parent = nullptr); void bindingChanged(const BindingProperty &bindingProperty); void bindingRemoved(const BindingProperty &bindingProperty); @@ -37,6 +52,18 @@ public: void addBindingForCurrentNode(); void resetModel(); + Q_INVOKABLE void add(); + Q_INVOKABLE void remove(int row); + + int currentIndex() const; + void setCurrentIndex(int i); + bool getExpressionStrings(const BindingProperty &bindingProperty, + QString *sourceNode, + QString *sourceProperty); + +signals: + void currentIndexChanged(); + protected: void addBindingProperty(const BindingProperty &property); void updateBindingProperty(int rowNumber); @@ -46,11 +73,11 @@ protected: ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty); int findRowForBinding(const BindingProperty &bindingProperty); - - bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty); - void updateDisplayRole(int row, int columns, const QString &string); + QHash<int, QByteArray> roleNames() const override; + BindingModelBackendDelegate *delegate() const; + private: void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleException(); @@ -60,7 +87,48 @@ private: bool m_lock = false; bool m_handleDataChanged = false; QString m_exceptionError; + int m_currentIndex = 0; + BindingModelBackendDelegate *m_delegate = nullptr; +}; + +class BindingModelBackendDelegate : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) + + Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) + Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) + +public: + BindingModelBackendDelegate(BindingModel *parent = nullptr); + +signals: + void currentRowChanged(); + //void nameChanged(); + void targetNodeChanged(); +private: + int currentRow() const; + void setCurrentRow(int i); + void handleException(); + QString targetNode() const; + + StudioQmlComboBoxBackend *property(); + StudioQmlComboBoxBackend *sourceNode(); + StudioQmlComboBoxBackend *sourceProperty(); + + void handleSourceNodeChanged(); + void handleSourcePropertyChanged(); + + StudioQmlComboBoxBackend m_property; + StudioQmlComboBoxBackend m_sourceNode; + StudioQmlComboBoxBackend m_sourceNodeProperty; + QString m_exceptionError; + int m_currentRow = -1; + QString m_targetNode; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 4abb7b21376..6fe8af59eb7 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -252,6 +252,19 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP { item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole); item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole); + item->setData(signalHandlerProperty.parentModelNode() + .bindingProperty("target") + .resolveToModelNode() + .id(), + UserRoles::TargetNameRole); + + // TODO signalHandlerProperty.source() contains a statement that defines the type. + // foo.bar() <- function call + // foo.state = "literal" //state change + //anything else is assignment + // e.g. foo.bal = foo2.bula ; foo.bal = "literal" ; goo.gal = true + + item->setData("Assignment", UserRoles::ActionTypeRole); } ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const @@ -370,6 +383,16 @@ void ConnectionModel::removeRowFromTable(const SignalHandlerProperty &property) } } +void ConnectionModel::add() +{ + addConnection(); +} + +void ConnectionModel::remove(int row) +{ + deleteConnectionByRow(row); +} + void ConnectionModel::handleException() { QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); @@ -522,4 +545,12 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co return stringList; } +QHash<int, QByteArray> ConnectionModel::roleNames() const +{ + static QHash<int, QByteArray> roleNames{{TargetPropertyNameRole, "signal"}, + {TargetNameRole, "target"}, + {ActionTypeRole, "action"}}; + return roleNames; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 42cbe33fc77..fc1108d4035 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -26,7 +26,9 @@ public: }; enum UserRoles { InternalIdRole = Qt::UserRole + 1, - TargetPropertyNameRole + TargetPropertyNameRole, + TargetNameRole, + ActionTypeRole }; ConnectionModel(ConnectionView *parent = nullptr); @@ -49,6 +51,9 @@ public: void deleteConnectionByRow(int currentRow); void removeRowFromTable(const SignalHandlerProperty &property); + Q_INVOKABLE void add(); + Q_INVOKABLE void remove(int row); + protected: void addModelNode(const ModelNode &modelNode); void addConnection(const ModelNode &modelNode); @@ -61,6 +66,8 @@ protected: void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty); QStringList getPossibleSignalsForConnection(const ModelNode &connection) const; + QHash<int, QByteArray> roleNames() const override; + private: void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleException(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index d8f84f3e0fc..9da3fcc7be4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -8,6 +8,7 @@ #include "bindingmodel.h" #include "connectionmodel.h" #include "dynamicpropertiesmodel.h" +#include "theme.h" #include <bindingproperty.h> #include <nodeabstractproperty.h> @@ -16,19 +17,116 @@ #include <qmldesignerplugin.h> #include <viewmanager.h> +#include <studioquickwidget.h> + +#include <coreplugin/icore.h> +#include <coreplugin/messagebox.h> + #include <utils/qtcassert.h> +#include <QQmlEngine> +#include <QShortcut> #include <QTableView> namespace QmlDesigner { +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +class ConnectionViewQuickWidget : public StudioQuickWidget +{ + // Q_OBJECT carefull + +public: + ConnectionViewQuickWidget(ConnectionView *connectionEditorView) + : m_connectionEditorView(connectionEditorView) + + { + engine()->addImportPath(qmlSourcesPath()); + engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + engine()->addImportPath(qmlSourcesPath() + "/imports"); + + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F12), this); + connect(m_qmlSourceUpdateShortcut, + &QShortcut::activated, + this, + &ConnectionViewQuickWidget::reloadQmlSource); + + //setObjectName(Constants::OBJECT_NAME_STATES_EDITOR); + setResizeMode(QQuickWidget::SizeRootObjectToView); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto map = registerPropertyMap("ConnectionsEditorEditorBackend"); + qmlRegisterAnonymousType<DynamicPropertiesModel>("ConnectionsEditorEditorBackend", 1); + qmlRegisterAnonymousType<DynamicPropertiesModelBackendDelegate>( + "ConnectionsEditorEditorBackend", 1); + + map->setProperties( + {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}}); + + map->setProperties( + {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}}); + + map->setProperties( + {{"dynamicPropertiesModel", + QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); + + Theme::setupTheme(engine()); + + setMinimumWidth(195); + setMinimumHeight(195); + + // init the first load of the QML UI elements + reloadQmlSource(); + } + ~ConnectionViewQuickWidget() = default; + + static QString qmlSourcesPath() + { +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/connectionseditor"; +#endif + return Core::ICore::resourcePath("qmldesigner/connectionseditor").toString(); + } + +private: + void reloadQmlSource() + { + QString connectionEditorQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml"); + QTC_ASSERT(QFileInfo::exists(connectionEditorQmlFilePath), return ); + setSource(QUrl::fromLocalFile(connectionEditorQmlFilePath)); + + if (!rootObject()) { + QString errorString; + for (const QQmlError &error : errors()) + errorString += "\n" + error.toString(); + + Core::AsynchronousMessageBox::warning( + tr("Cannot Create QtQuick View"), + tr("ConnectionsEditorWidget: %1 cannot be created.%2") + .arg(qmlSourcesPath(), errorString)); + return; + } + } + +private: + QPointer<ConnectionView> m_connectionEditorView; + QShortcut *m_qmlSourceUpdateShortcut; +}; + ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView{externalDependencies} - , m_connectionViewWidget(new ConnectionViewWidget()) - , m_connectionModel(new ConnectionModel(this)) - , m_bindingModel(new BindingModel(this)) - , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)) - , m_backendModel(new BackendModel(this)) + : AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()), + m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)), + m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)), + m_backendModel(new BackendModel(this)), + m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) { connectionViewWidget()->setBindingModel(m_bindingModel); connectionViewWidget()->setConnectionModel(m_connectionModel); @@ -36,8 +134,11 @@ ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependenci connectionViewWidget()->setBackendModel(m_backendModel); } -ConnectionView::~ConnectionView() = default; - +ConnectionView::~ConnectionView() +{ + // Ensure that QML is deleted first to avoid calling back to C++. + delete m_connectionViewQuickWidget.data(); +} void ConnectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -195,7 +296,14 @@ void ConnectionView::currentStateChanged(const ModelNode &) WidgetInfo ConnectionView::widgetInfo() { - return createWidgetInfo(m_connectionViewWidget.data(), + /* Enable new connection editor here */ + const bool newEditor = false; + + QWidget *widget = m_connectionViewWidget.data(); + if (newEditor) + widget = m_connectionViewQuickWidget.data(); + + return createWidgetInfo(widget, QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, @@ -257,6 +365,20 @@ BackendModel *ConnectionView::backendModel() const return m_backendModel; } +int ConnectionView::currentIndex() const +{ + return m_currentIndex; +} + +void ConnectionView::setCurrentIndex(int i) +{ + if (m_currentIndex == i) + return; + + m_currentIndex = i; + emit currentIndexChanged(); +} + ConnectionView *ConnectionView::instance() { diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 89c6c489105..5997f230ad8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -20,11 +20,14 @@ class BindingModel; class ConnectionModel; class DynamicPropertiesModel; class BackendModel; +class ConnectionViewQuickWidget; -class ConnectionView : public AbstractView +class ConnectionView : public AbstractView { Q_OBJECT + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + public: ConnectionView(ExternalDependenciesInterface &externalDependencies); ~ConnectionView() override; @@ -70,14 +73,24 @@ public: BindingModel *bindingModel() const; BackendModel *backendModel() const; + int currentIndex() const; + void setCurrentIndex(int i); + static ConnectionView *instance(); +signals: + void currentIndexChanged(); + private: //variables QPointer<ConnectionViewWidget> m_connectionViewWidget; + ConnectionModel *m_connectionModel; BindingModel *m_bindingModel; DynamicPropertiesModel *m_dynamicPropertiesModel; BackendModel *m_backendModel; + int m_currentIndex = 0; + + QPointer<ConnectionViewQuickWidget> m_connectionViewQuickWidget; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index edade6ae400..8a8845b47fd 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -90,6 +90,8 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : this, &ConnectionViewWidget::handleTabChanged); ui->stackedWidget->setCurrentIndex(0); + + ui->stackedWidget->parentWidget()->hide(); } ConnectionViewWidget::~ConnectionViewWidget() @@ -192,18 +194,17 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) return; const ModelNode node = property.parentModelNode(); - const TypeName typeName = property.isDynamic() ? property.dynamicTypeName() - : node.metaInfo() - .property(property.name()) - .propertyType() - .typeName(); + auto model = node.model(); + const auto type = property.isDynamic() + ? model->metaInfo(property.dynamicTypeName()) + : node.metaInfo().property(property.name()).propertyType(); const QString targetName = node.displayName() + "." + property.name(); m_bindingEditor->showWidget(); m_bindingEditor->setBindingValue(property.expression()); m_bindingEditor->setModelNode(node); - m_bindingEditor->setBackendValueTypeName(typeName); + m_bindingEditor->setBackendValueType(type); m_bindingEditor->setTargetName(targetName); m_bindingEditor->prepareBindings(); m_bindingEditor->updateWindowName(); @@ -240,11 +241,12 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) return; const QString targetName = node.displayName() + "." + abstractProperty.name(); - + auto model = node.model(); m_dynamicEditor->showWidget(); m_dynamicEditor->setBindingValue(newExpression); m_dynamicEditor->setModelNode(node); - m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName()); + m_dynamicEditor->setBackendValueType( + model->metaInfo(abstractProperty.dynamicTypeName())); m_dynamicEditor->setTargetName(targetName); m_dynamicEditor->prepareBindings(); m_dynamicEditor->updateWindowName(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 4faf5d23f17..eb0976eaed9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -152,10 +152,34 @@ QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type) return expression; } +void DynamicPropertiesModel::add() +{ + addDynamicPropertyForCurrentNode(); +} + +void DynamicPropertiesModel::remove(int row) +{ + deleteDynamicPropertyByRow(row); +} + +int DynamicPropertiesModel::currentIndex() const +{ + return m_currentIndex; +} + +void DynamicPropertiesModel::setCurrentIndex(int i) +{ + if (m_currentIndex == i) + return; + + m_currentIndex = i; + + emit currentIndexChanged(); +} + DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent) - : QStandardItemModel(parent) - , m_view(parent) - , m_explicitSelection(explicitSelection) + : QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection), + m_delegate(new DynamicPropertiesModelBackendDelegate(this)) { connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged); } @@ -163,6 +187,7 @@ DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractV void DynamicPropertiesModel::resetModel() { beginResetModel(); + const int backIndex = m_currentIndex; clear(); setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}); @@ -172,7 +197,9 @@ void DynamicPropertiesModel::resetModel() addModelNode(modelNode); } + emit currentIndexChanged(); endResetModel(); + m_currentIndex = backIndex; } @@ -344,6 +371,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper removeRow(rowNumber); } + emit currentIndexChanged(); + m_handleDataChanged = true; } @@ -360,6 +389,8 @@ void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProper removeRow(rowNumber); } + emit currentIndexChanged(); + m_handleDataChanged = true; } @@ -368,6 +399,7 @@ void DynamicPropertiesModel::reset() m_handleDataChanged = false; resetModel(); m_handleDataChanged = true; + emit currentIndexChanged(); } void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) @@ -597,6 +629,8 @@ void DynamicPropertiesModel::updateBindingProperty(int rowNumber) BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); if (bindingProperty.isValid()) { + updateCustomData(rowNumber, bindingProperty); + QString propertyName = QString::fromUtf8(bindingProperty.name()); updateDisplayRole(rowNumber, PropertyNameRow, propertyName); QString value = bindingProperty.expression(); @@ -617,6 +651,7 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber) VariantProperty variantProperty = variantPropertyForRow(rowNumber); if (variantProperty.isValid()) { + updateCustomData(rowNumber, variantProperty); QString propertyName = QString::fromUtf8(variantProperty.name()); updateDisplayRole(rowNumber, PropertyNameRow, propertyName); QVariant value = variantProperty.value(); @@ -787,6 +822,16 @@ void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const Abstrac { item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1); item->setData(property.name(), Qt::UserRole + 2); + + item->setData(property.parentModelNode().id(), TargetNameRole); + item->setData(property.name(), PropertyNameRole); + item->setData(property.parentModelNode().id(), TargetNameRole); + item->setData(property.dynamicTypeName(), PropertyTypeRole); + + if (property.isVariantProperty()) + item->setData(property.toVariantProperty().value(), PropertyValueRole); + if (property.isBindingProperty()) + item->setData(property.toBindingProperty().expression(), PropertyValueRole); } void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property) @@ -924,4 +969,217 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const return m_view->singleSelectedModelNode(); } +QHash<int, QByteArray> DynamicPropertiesModel::roleNames() const +{ + static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"}, + {PropertyNameRole, "name"}, + {PropertyTypeRole, "type"}, + {PropertyValueRole, "value"}}; + + return roleNames; +} + +DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const +{ + return m_delegate; +} + +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate( + DynamicPropertiesModel *parent) + : QObject(parent) +{ + m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); + + connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this]() { handleTypeChanged(); }); + connect(&m_name, &StudioQmlTextBackend::activated, this, [this]() { handleNameChanged(); }); + connect(&m_value, &StudioQmlTextBackend::activated, this, [this]() { handleValueChanged(); }); +} + +int DynamicPropertiesModelBackendDelegate::currentRow() const +{ + return m_currentRow; +} + +void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) +{ + if (m_currentRow == i) + return; + + m_currentRow = i; + + //setup + + DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); + + QTC_ASSERT(model, return ); + + AbstractProperty property = model->abstractPropertyForRow(i); + + m_type.setCurrentText(QString::fromUtf8(property.dynamicTypeName())); + m_name.setText(QString::fromUtf8(property.name())); + + if (property.isVariantProperty()) + m_value.setText(property.toVariantProperty().value().toString()); + else if (property.isBindingProperty()) + m_value.setText(property.toBindingProperty().expression()); +} + +void DynamicPropertiesModelBackendDelegate::handleTypeChanged() +{ + //void DynamicPropertiesModel::updatePropertyType(int rowNumber) + const TypeName type = m_type.currentText().toUtf8(); + + DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->view(), return ); + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + + VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); + + RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); + + try { + if (bindingProperty.isBindingProperty() || type == "var") { //var is always a binding + const QString expression = bindingProperty.expression(); + variantProperty.parentModelNode().removeProperty(variantProperty.name()); + bindingProperty.setDynamicTypeNameAndExpression(type, expression); + } else if (variantProperty.isVariantProperty()) { + variantProperty.parentModelNode().removeProperty(variantProperty.name()); + variantProperty.setDynamicTypeNameAndValue(type, variantValue()); + } + transaction.commit(); // committing in the try block + } catch (Exception &e) { + m_exceptionError = e.description(); + QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); + } +} + +void DynamicPropertiesModelBackendDelegate::handleNameChanged() +{ + //see DynamicPropertiesModel::updatePropertyName + + const PropertyName newName = m_name.text().toUtf8(); + QTC_ASSERT(!newName.isEmpty(), return ); + + DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->view(), return ); + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + + ModelNode targetNode = bindingProperty.parentModelNode(); + + if (bindingProperty.isBindingProperty()) { + model->view()->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() { + const QString expression = bindingProperty.expression(); + const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); + + targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, + expression); + targetNode.removeProperty(bindingProperty.name()); + }); + + return; + } + + VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); + + if (variantProperty.isVariantProperty()) { + const QVariant value = variantProperty.value(); + const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); + ModelNode targetNode = variantProperty.parentModelNode(); + + model->view()->executeInTransaction(__FUNCTION__, [=]() { + targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, + value); + targetNode.removeProperty(variantProperty.name()); + }); + } + + AbstractProperty property = targetNode.property(newName); + + //order might have changed because of name change we have to select the correct row + int newRow = model->findRowForProperty(property); + model->setCurrentIndex(newRow); + setCurrentRow(newRow); +} + +void DynamicPropertiesModelBackendDelegate::handleValueChanged() +{ + //see void DynamicPropertiesModel::updateValue(int row) + + DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->view(), return ); + + BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + + if (bindingProperty.isBindingProperty()) { + const QString expression = m_value.text(); + + RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); + try { + bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), + expression); + transaction.commit(); // committing in the try block + } catch (Exception &e) { + m_exceptionError = e.description(); + QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); + } + return; + } + + VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); + + if (variantProperty.isVariantProperty()) { + RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); + try { + variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), + variantValue()); + transaction.commit(); // committing in the try block + } catch (Exception &e) { + m_exceptionError = e.description(); + QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); + } + } +} + +void DynamicPropertiesModelBackendDelegate::handleException() +{ + QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); + //reset +} + +QVariant DynamicPropertiesModelBackendDelegate::variantValue() const +{ + //improve + const QString type = m_type.currentText(); + if (type == "real" || type == "int") + return m_value.text().toFloat(); + + if (type == "bool") + return m_value.text() == "true"; + + return m_value.text(); +} + +StudioQmlComboBoxBackend *DynamicPropertiesModelBackendDelegate::type() +{ + return &m_type; +} + +StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::name() +{ + return &m_name; +} + +StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::value() +{ + return &m_value; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index f094516e635..c41875dfc09 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -5,6 +5,8 @@ #include <nodeinstanceglobal.h> +#include <studioquickwidget.h> + #include <QStandardItemModel> namespace QmlDesigner { @@ -15,6 +17,8 @@ class BindingProperty; class ModelNode; class VariantProperty; +class DynamicPropertiesModelBackendDelegate; + class DynamicPropertiesModel : public QStandardItemModel { Q_OBJECT @@ -22,11 +26,22 @@ class DynamicPropertiesModel : public QStandardItemModel public: enum ColumnRoles { TargetModelNodeRow = 0, - PropertyNameRow = 1, - PropertyTypeRow = 2, - PropertyValueRow = 3 + PropertyNameRow = 1, + PropertyTypeRow = 2, + PropertyValueRow = 3 + }; + + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + PropertyNameRole, + PropertyTypeRole, + PropertyValueRole }; + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT) + DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); void bindingPropertyChanged(const BindingProperty &bindingProperty); @@ -62,6 +77,17 @@ public: static QVariant defaultValueForType(const TypeName &type); static QString defaultExpressionForType(const TypeName &type); + Q_INVOKABLE void add(); + Q_INVOKABLE void remove(int row); + + int currentIndex() const; + void setCurrentIndex(int i); + + int findRowForProperty(const AbstractProperty &abstractProperty) const; + +signals: + void currentIndexChanged(); + protected: void addProperty(const QVariant &propertyValue, const QString &propertyType, @@ -79,12 +105,17 @@ protected: void updateCustomData(int row, const AbstractProperty &property); int findRowForBindingProperty(const BindingProperty &bindingProperty) const; int findRowForVariantProperty(const VariantProperty &variantProperty) const; - int findRowForProperty(const AbstractProperty &abstractProperty) const; - bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty); + bool getExpressionStrings(const BindingProperty &bindingProperty, + QString *sourceNode, + QString *sourceProperty); void updateDisplayRole(int row, int columns, const QString &string); + QHash<int, QByteArray> roleNames() const override; + + DynamicPropertiesModelBackendDelegate *delegate() const; + private: void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void handleException(); @@ -95,6 +126,48 @@ private: QString m_exceptionError; QList<ModelNode> m_selectedNodes; bool m_explicitSelection = false; + int m_currentIndex = 0; + + DynamicPropertiesModelBackendDelegate *m_delegate = nullptr; +}; + +class DynamicPropertiesModelBackendDelegate : public QObject +{ + Q_OBJECT + + Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT) + Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) + Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT) + Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) + //Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) + +public: + DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr); + +signals: + void currentRowChanged(); + void nameChanged(); + void valueChanged(); + +private: + int currentRow() const; + void setCurrentRow(int i); + void handleTypeChanged(); + void handleNameChanged(); + void handleValueChanged(); + void handleException(); + QVariant variantValue() const; + + StudioQmlComboBoxBackend *type(); + + StudioQmlTextBackend *name(); + StudioQmlTextBackend *value(); + + StudioQmlComboBoxBackend m_type; + StudioQmlTextBackend m_name; + StudioQmlTextBackend m_value; + int m_currentRow = -1; + QString m_exceptionError; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index c31f844e5e2..59ec457d071 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -205,7 +205,7 @@ void ContentLibraryBundleImporter::handleImportTimer() for (const QString &pendingType : pendingTypes) { NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); const bool isImport = m_pendingTypes[pendingType]; - const bool typeComplete = metaInfo.isValid() && !metaInfo.superClasses().empty(); + const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty(); if (isImport == typeComplete) { m_pendingTypes.remove(pendingType); if (isImport) diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp index 1d0b38b3f15..e7395c66625 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp @@ -68,8 +68,9 @@ void CurveEditorView::modelAboutToBeDetached(Model *model) bool dirtyfiesView(const ModelNode &node) { - return QmlTimeline::isValidQmlTimeline(node) - || QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node); + return (node.type() == "QtQuick.Timeline.Keyframe" && node.hasParentProperty()) + || QmlTimeline::isValidQmlTimeline(node) + || QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node); } void CurveEditorView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode, @@ -143,13 +144,8 @@ void CurveEditorView::variantPropertiesChanged([[maybe_unused]] const QList<Vari [[maybe_unused]] PropertyChangeFlags propertyChange) { for (const auto &property : propertyList) { - if ((property.name() == "frame" || property.name() == "value") - && property.parentModelNode().type() == "QtQuick.Timeline.Keyframe" - && property.parentModelNode().hasParentProperty()) { - const ModelNode framesNode = property.parentModelNode().parentProperty().parentModelNode(); - if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode)) - updateKeyframes(); - } + if (dirtyfiesView(property.parentModelNode())) + updateKeyframes(); } } @@ -157,20 +153,16 @@ void CurveEditorView::bindingPropertiesChanged([[maybe_unused]] const QList<Bind [[maybe_unused]] PropertyChangeFlags propertyChange) { for (const auto &property : propertyList) { - if (property.name() == "easing.bezierCurve") { + if (dirtyfiesView(property.parentModelNode())) updateKeyframes(); - } } } void CurveEditorView::propertiesRemoved([[maybe_unused]] const QList<AbstractProperty> &propertyList) { for (const auto &property : propertyList) { - if (property.name() == "keyframes") { - ModelNode parent = property.parentModelNode(); - if (dirtyfiesView(parent)) - updateKeyframes(); - } + if (dirtyfiesView(property.parentModelNode())) + updateKeyframes(); } } diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 2a62d7a6b2b..64f85915f16 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -7,6 +7,7 @@ #include <qmldesignerplugin.h> #include <bindingproperty.h> +#include <model/modelutils.h> #include <nodeabstractproperty.h> #include <nodelistproperty.h> #include <nodemetainfo.h> @@ -93,9 +94,9 @@ void DebugView::nodeCreated(const ModelNode &createdNode) message << createdNode.majorVersion() << "." << createdNode.minorVersion(); message << createdNode.nodeSource(); message << "MetaInfo " << createdNode.metaInfo().isValid() << " "; - if (createdNode.metaInfo().isValid()) { - message << createdNode.metaInfo().majorVersion() << "." << createdNode.metaInfo().minorVersion(); - message << createdNode.metaInfo().componentFileName(); + if (auto metaInfo = createdNode.metaInfo()) { + message << metaInfo.majorVersion() << "." << metaInfo.minorVersion(); + message << ModelUtils::componentFilePath(createdNode); } log("::nodeCreated:", message.readAll()); } @@ -282,9 +283,10 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel message << lineBreak; if (selectedNode.metaInfo().isValid()) { - for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().classHierarchy()) + for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().selfAndPrototypes()) { message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "." << metaInfo.minorVersion() << lineBreak; + } message << lineBreak; message << selectedNode.metaInfo().typeName(); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index c0ff5de2af3..88e7ad379d5 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -3,19 +3,20 @@ #include "bakelights.h" -#include "abstractview.h" -#include "auxiliarydataproperties.h" -#include "bakelightsdatamodel.h" -#include "bakelightsconnectionmanager.h" -#include "bindingproperty.h" -#include "documentmanager.h" -#include "modelnode.h" -#include "nodeabstractproperty.h" -#include "nodeinstanceview.h" -#include "nodemetainfo.h" -#include "plaintexteditmodifier.h" -#include "rewriterview.h" -#include "variantproperty.h" +#include <abstractview.h> +#include <auxiliarydataproperties.h> +#include <bakelightsconnectionmanager.h> +#include <bakelightsdatamodel.h> +#include <bindingproperty.h> +#include <documentmanager.h> +#include <model/modelutils.h> +#include <modelnode.h> +#include <nodeabstractproperty.h> +#include <nodeinstanceview.h> +#include <nodemetainfo.h> +#include <plaintexteditmodifier.h> +#include <rewriterview.h> +#include <variantproperty.h> #include <coreplugin/icore.h> @@ -240,18 +241,21 @@ void BakeLights::rebake() void BakeLights::exposeModelsAndLights(const QString &nodeId) { ModelNode compNode = m_view->modelNodeForId(nodeId); - if (!compNode.isValid() || !compNode.isComponent() - || compNode.metaInfo().componentFileName().isEmpty()) { + if (!compNode.isValid() || !compNode.isComponent()) { + return; + } + + auto componentFilePath = ModelUtils::componentFilePath(compNode); + if (componentFilePath.isEmpty()) { return; } RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend}; ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1); - const QString compFile = compNode.metaInfo().componentFileName(); - const Utils::FilePath compFilePath = Utils::FilePath::fromString(compFile); + const Utils::FilePath compFilePath = Utils::FilePath::fromString(componentFilePath); QByteArray src = compFilePath.fileContents().value(); - compModel->setFileUrl(QUrl::fromLocalFile(compFile)); + compModel->setFileUrl(QUrl::fromLocalFile(componentFilePath)); auto textDocument = std::make_unique<QTextDocument>(QString::fromUtf8(src)); auto modifier = std::make_unique<IndentingTextEditModifier>( @@ -295,12 +299,12 @@ void BakeLights::exposeModelsAndLights(const QString &nodeId) QString newText = modifier->text(); if (newText != originalText) { - QSaveFile saveFile(compFile); + QSaveFile saveFile(componentFilePath); if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { saveFile.write(newText.toUtf8()); saveFile.commit(); } else { - qWarning() << __FUNCTION__ << "Failed to save changes to:" << compFile; + qWarning() << __FUNCTION__ << "Failed to save changes to:" << componentFilePath; } } diff --git a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp index 37dfafad9a2..07a16de7bbd 100644 --- a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp +++ b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp @@ -3,6 +3,8 @@ #include "backgroundaction.h" +#include <theme.h> + #include <utils/stylehelper.h> #include <QComboBox> @@ -28,6 +30,14 @@ QIcon iconForColor(const QColor &color) { image.fill(0); QPainter p(&image); + if (color == BackgroundAction::ContextImage) { + const QString unicode = Theme::getIconUnicode(Theme::Icon::textures_medium); + const QString fontName = "qtds_propertyIconFont.ttf"; + QIcon icon = Utils::StyleHelper::getIconFromIconFont(fontName, unicode, 10, 10, Qt::white); + + return icon; + } + p.fillRect(2, 2, size - 4, size - 4, Qt::black); if (color.alpha() == 0) { @@ -70,13 +80,13 @@ QList<QColor> BackgroundAction::colors() { static QColor alphaZero(Qt::transparent); static QList<QColor> colorList = {alphaZero, + QColor(BackgroundAction::ContextImage), QColor(Qt::black), QColor(0x4c4e50), QColor(Qt::darkGray), QColor(Qt::lightGray), QColor(Qt::white)}; - return colorList; } diff --git a/src/plugins/qmldesigner/components/formeditor/backgroundaction.h b/src/plugins/qmldesigner/components/formeditor/backgroundaction.h index 34bd4cd3229..c6eeb212fe4 100644 --- a/src/plugins/qmldesigner/components/formeditor/backgroundaction.h +++ b/src/plugins/qmldesigner/components/formeditor/backgroundaction.h @@ -11,14 +11,12 @@ namespace QmlDesigner { class BackgroundAction : public QWidgetAction { - enum BackgroundType { - CheckboardBackground, - WhiteBackground, - BlackBackground - }; + enum BackgroundType { CheckboardBackground, WhiteBackground, BlackBackground }; Q_OBJECT public: + enum SpecialColor { ContextImage = Qt::yellow }; + explicit BackgroundAction(QObject *parent); void setColor(const QColor &color); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index 1e67884ad8d..9549ce9dd46 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -2,9 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "formeditorgraphicsview.h" +#include "backgroundaction.h" #include "formeditoritem.h" #include "formeditorwidget.h" #include "navigation2d.h" + +#include <theme.h> + #include <utils/hostosinfo.h> #include <QAction> @@ -198,10 +202,28 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec painter->save(); painter->setBrushOrigin(0, 0); - painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush()); // paint rect around editable area - painter->setPen(Qt::black); - painter->drawRect(rootItemRect()); + + if (backgroundBrush().color() == BackgroundAction::ContextImage) { + painter->fillRect(rectangle.intersected(rootItemRect()), Qt::gray); + painter->setOpacity(0.5); + if (!m_backgroundImage.isNull()) + painter->drawImage(rootItemRect().topLeft() + m_backgroundImage.offset(), + m_backgroundImage); + painter->setOpacity(1.0); + } else { + painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush()); + } + + QPen pen(Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor)); + + pen.setStyle(Qt::DotLine); + pen.setWidth(1); + + painter->setPen(pen); + + painter->drawRect(rootItemRect().adjusted(-1, -1, 0, 0)); + painter->restore(); } @@ -210,6 +232,17 @@ void FormEditorGraphicsView::frame(const QRectF &boundingRect) fitInView(boundingRect, Qt::KeepAspectRatio); } +void FormEditorGraphicsView::setBackgoundImage(const QImage &image) +{ + m_backgroundImage = image; + update(); +} + +QImage FormEditorGraphicsView::backgroundImage() const +{ + return m_backgroundImage; +} + void FormEditorGraphicsView::setZoomFactor(double zoom) { resetTransform(); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h index 28a49b00466..60e02582cd1 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h @@ -28,6 +28,9 @@ public: void setZoomFactor(double zoom); void frame(const QRectF &bbox); + void setBackgoundImage(const QImage &image); + QImage backgroundImage() const; + protected: bool eventFilter(QObject *watched, QEvent *event) override; void wheelEvent(QWheelEvent *event) override; @@ -45,6 +48,7 @@ private: Panning m_isPanning = Panning::NotStarted; QPoint m_panningStartPosition; QRectF m_rootItemRect; + QImage m_backgroundImage; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 5661e4ff793..6b580a7d5f3 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -63,6 +63,8 @@ void FormEditorView::modelAttached(Model *model) if (!isEnabled()) return; + m_formEditorWidget->setBackgoundImage({}); + temporaryBlockView(); setupFormEditorWidget(); @@ -649,6 +651,10 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item)) editorItem->setFrameColor(data.value<QColor>()); } + + if (key == contextImageProperty) { + m_formEditorWidget->setBackgoundImage(data.value<QImage>()); + } } static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItemNode) @@ -784,6 +790,11 @@ void FormEditorView::exportAsImage() m_formEditorWidget->exportAsImage(m_scene->rootFormEditorItem()->boundingRect()); } +QImage FormEditorView::takeFormEditorScreenshot() +{ + return m_formEditorWidget->takeFormEditorScreenshot(); +} + QPicture FormEditorView::renderToPicture() const { return m_formEditorWidget->renderToPicture(); @@ -954,6 +965,11 @@ void FormEditorView::setupRootItemSize() formEditorWidget()->setRootItemRect(rootQmlNode.instanceBoundingRect()); formEditorWidget()->centerScene(); + + auto contextImage = rootModelNode().auxiliaryData(contextImageProperty); + + if (contextImage) + m_formEditorWidget->setBackgoundImage(contextImage.value().value<QImage>()); } } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index f97959acb37..1a9f15d016e 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -117,6 +117,7 @@ public: void setGotoErrorCallback(std::function<void(int, int)> gotoErrorCallback); void exportAsImage(); + QImage takeFormEditorScreenshot(); QPicture renderToPicture() const; void setupFormEditorWidget(); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index bfc03d2b237..ef7ed1d52d4 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "formeditorwidget.h" +#include "backgroundaction.h" #include "designeractionmanager.h" #include "designericons.h" #include "designersettings.h" @@ -332,11 +333,13 @@ void FormEditorWidget::changeBackgound(const QColor &color) if (color.alpha() == 0) { m_graphicsView->activateCheckboardBackground(); if (m_formEditorView->rootModelNode().hasAuxiliaryData(formeditorColorProperty)) { - m_formEditorView->rootModelNode().setAuxiliaryData(formeditorColorProperty, {}); + m_formEditorView->rootModelNode().setAuxiliaryDataWithoutLock(formeditorColorProperty, + {}); } } else { m_graphicsView->activateColoredBackground(color); - m_formEditorView->rootModelNode().setAuxiliaryData(formeditorColorProperty, color); + m_formEditorView->rootModelNode().setAuxiliaryDataWithoutLock(formeditorColorProperty, + color); } } @@ -397,6 +400,10 @@ void FormEditorWidget::updateActions() } else { m_backgroundAction->setColor(Qt::transparent); } + + if (m_formEditorView->rootModelNode().hasAuxiliaryData(contextImageProperty)) + m_backgroundAction->setColor(BackgroundAction::ContextImage); + } else { m_rootWidthAction->clearLineEditText(); m_rootHeightAction->clearLineEditText(); @@ -540,6 +547,40 @@ void FormEditorWidget::exportAsImage(const QRectF &boundingRect) } } +QImage FormEditorWidget::takeFormEditorScreenshot() +{ + const QRectF boundingRect = m_formEditorView->scene()->rootFormEditorItem()->boundingRect(); + + m_formEditorView->scene()->manipulatorLayerItem()->setVisible(false); + QImage image(boundingRect.size().toSize(), QImage::Format_ARGB32); + + if (!m_graphicsView->backgroundImage().isNull()) { + image = m_graphicsView->backgroundImage(); + const QPoint offset = m_graphicsView->backgroundImage().offset(); + + QPainter painter(&image); + QTransform viewportTransform = m_graphicsView->viewportTransform(); + + m_graphicsView->render(&painter, + QRectF(-offset, boundingRect.size()), + viewportTransform.mapRect(boundingRect).toRect()); + + image.setOffset(offset); + + } else { + QPainter painter(&image); + QTransform viewportTransform = m_graphicsView->viewportTransform(); + + m_graphicsView->render(&painter, + QRectF(0, 0, image.width(), image.height()), + viewportTransform.mapRect(boundingRect).toRect()); + } + + m_formEditorView->scene()->manipulatorLayerItem()->setVisible(true); + + return image; +} + QPicture FormEditorWidget::renderToPicture() const { QPicture picture; @@ -568,6 +609,17 @@ bool FormEditorWidget::errorMessageBoxIsVisible() const return m_documentErrorWidget && m_documentErrorWidget->isVisible(); } +void FormEditorWidget::setBackgoundImage(const QImage &image) +{ + m_graphicsView->setBackgoundImage(image); + updateActions(); +} + +QImage FormEditorWidget::backgroundImage() const +{ + return m_graphicsView->backgroundImage(); +} + DocumentWarningWidget *FormEditorWidget::errorWidget() { if (m_documentErrorWidget.isNull()) { diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h index 5e4e29d1553..8135ce81393 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h @@ -63,12 +63,17 @@ public: void showWarningMessageBox(const QList<DocumentMessage> &warnings); void exportAsImage(const QRectF &boundingRect); + + QImage takeFormEditorScreenshot(); QPicture renderToPicture() const; FormEditorGraphicsView *graphicsView() const; bool errorMessageBoxIsVisible() const; + void setBackgoundImage(const QImage &image); + QImage backgroundImage() const; + protected: QActionGroup *toolActionGroup() const; DocumentWarningWidget *errorWidget(); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 089efb87cdd..7bbbac5b253 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -61,7 +61,7 @@ namespace QmlDesigner { DesignDocument acts as a facade to a model representing a qml document, and the different views/widgets accessing it. */ -DesignDocument::DesignDocument(ProjectStorage<Sqlite::Database> &projectStorage, +DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies) : m_documentModel( Model::create("QtQuick.Item", 1, 0, nullptr, std::make_unique<ModelResourceManagement>())) @@ -69,7 +69,7 @@ DesignDocument::DesignDocument(ProjectStorage<Sqlite::Database> &projectStorage, , m_rewriterView(new RewriterView(externalDependencies, RewriterView::Amend)) , m_documentLoaded(false) , m_currentTarget(nullptr) - , m_projectStorage(projectStorage) + , m_projectStorageDependencies(projectStorageDependencies) , m_externalDependencies{externalDependencies} { } diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 2fbc9060e45..7c0fe3941f8 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -39,7 +39,7 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject Q_OBJECT public: - DesignDocument(ProjectStorage<Sqlite::Database> &projectStorage, + DesignDocument(ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies); ~DesignDocument() override; @@ -143,7 +143,7 @@ private: // variables QScopedPointer<RewriterView> m_rewriterView; bool m_documentLoaded; ProjectExplorer::Target *m_currentTarget; - ProjectStorage<Sqlite::Database> &m_projectStorage; + ProjectStorageDependencies m_projectStorageDependencies; ExternalDependenciesInterface &m_externalDependencies; }; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index f8a9fd97718..29fff4b359a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -4,14 +4,15 @@ #include "itemlibraryassetimportdialog.h" #include "ui_itemlibraryassetimportdialog.h" -#include "qmldesignerplugin.h" -#include "qmldesignerconstants.h" -#include "model.h" -#include "nodemetainfo.h" -#include "variantproperty.h" +#include <model.h> +#include <model/modelutils.h> +#include <nodemetainfo.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <variantproperty.h> -#include "utils/outputformatter.h" -#include "theme.h" +#include <theme.h> +#include <utils/outputformatter.h> #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> @@ -283,7 +284,7 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, QString errorMsg; const ModelNode &node = updateNode; if (node.hasMetaInfo()) { - QString compFileName = node.metaInfo().componentFileName(); // absolute path + QString compFileName = ModelUtils::componentFilePath(node); // absolute path bool preselectNodeSource = false; if (compFileName.isEmpty()) { // Node is not a file component, so we have to check if the current doc itself is diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index d370f6d4547..3082ee442ac 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -62,7 +62,7 @@ void ItemLibraryView::modelAttached(Model *model) m_widget->setModel(model); updateImports(); if (model) - m_widget->updatePossibleImports(difference(model->possibleImports(), model->imports())); + m_widget->updatePossibleImports(set_difference(model->possibleImports(), model->imports())); m_hasErrors = !rewriterView()->errors().isEmpty(); m_widget->setFlowMode(QmlItemNode(rootModelNode()).isFlowView()); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 1cbde45ba0b..d2435fbf15a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -338,7 +338,7 @@ void ItemLibraryWidget::updateModel() void ItemLibraryWidget::updatePossibleImports(const Imports &possibleImports) { - m_addModuleModel->update(difference(possibleImports, m_model->imports())); + m_addModuleModel->update(set_difference(possibleImports, m_model->imports())); delayedUpdateModel(); } @@ -358,7 +358,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged() { if (!m_itemLibraryInfo.isNull()) { m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); - m_addModuleModel->update(difference(m_model->possibleImports(), m_model->imports())); + m_addModuleModel->update(set_difference(m_model->possibleImports(), m_model->imports())); } } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 933686febda..7d3eb7efe6d 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -548,7 +548,7 @@ void MaterialEditorView::setupQmlBackend() TypeName diffClassName; if (NodeMetaInfo metaInfo = m_selectedMaterial.metaInfo()) { diffClassName = metaInfo.typeName(); - for (const NodeMetaInfo &metaInfo : metaInfo.classHierarchy()) { + for (const NodeMetaInfo &metaInfo : metaInfo.selfAndPrototypes()) { if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsUrl)) break; qmlSpecificsUrl = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName() diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 304ff91fcda..04c2ebd9e65 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -90,7 +90,7 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i } else if (insertInfo.isQtQuick3DParticles3DParticle3D()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D()) propertyList.append("particle"); - } else if (insertInfo.isQtQuick3DParticleAbstractShape()) { + } else if (insertInfo.isQtQuick3DParticlesAbstractShape()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D() || parentInfo.isQtQuick3DParticles3DAttractor3D()) propertyList.append("shape"); @@ -100,9 +100,6 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); - } else if (insertInfo.isEffectMaker()) { - if (parentInfo.isQtQuickItem()) - propertyList.append("effect"); } else if (insertInfo.isQtQuick3DBakedLightmap()) { if (parentInfo.isQtQuick3DModel()) propertyList.append("bakedLightmap"); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp index 360c893c2d7..24d4377ef29 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp @@ -8,6 +8,7 @@ #include <QAction> #include <QBoxLayout> +#include <QKeyEvent> #include <QLabel> #include <QStyle> #include <QToolButton> @@ -71,7 +72,7 @@ LineEdit::LineEdit(QWidget *parent) setFixedHeight(29); } -void LineEdit::resizeEvent(QResizeEvent *) +void LineEdit::resizeEvent([[maybe_unused]] QResizeEvent *event) { QSize hint = clearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -80,6 +81,31 @@ void LineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - hint.height()) / 2); } +void LineEdit::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape && event->modifiers() == Qt::NoModifier) { + clear(); + event->accept(); + return; + } + QLineEdit::keyPressEvent(event); +} + +void LineEdit::paintEvent(QPaintEvent *event) +{ + if (text().isEmpty()) { + QPalette p(palette()); + p.setColor(QPalette::Active, + QPalette::PlaceholderText, + Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor)); + p.setColor(QPalette::Inactive, + QPalette::PlaceholderText, + Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor)); + setPalette(p); + } + QLineEdit::paintEvent(event); +} + void LineEdit::updateClearButton(const QString& text) { clearButton->setVisible(!text.isEmpty()); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h index 8b6da49cb3f..6cc375fd552 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h @@ -19,7 +19,9 @@ public: LineEdit(QWidget *parent = nullptr); protected: - void resizeEvent(QResizeEvent *); + void resizeEvent(QResizeEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void paintEvent(QPaintEvent *event) override; private slots: void updateClearButton(const QString &text); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index ceccf25554c..456f44e43c6 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -2,28 +2,29 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "navigatorview.h" -#include "navigatortreemodel.h" -#include "navigatorwidget.h" -#include "qmldesignerconstants.h" -#include "qmldesignericons.h" -#include "qmldesignerplugin.h" -#include "assetslibrarywidget.h" -#include "commontypecache.h" -#include "nameitemdelegate.h" #include "iconcheckboxitemdelegate.h" +#include "nameitemdelegate.h" +#include "navigatortreemodel.h" +#include "navigatorwidget.h" +#include <assetslibrarywidget.h> #include <bindingproperty.h> -#include <designmodecontext.h> +#include <commontypecache.h> #include <designersettings.h> +#include <designmodecontext.h> #include <itemlibraryinfo.h> -#include <nodeproperty.h> +#include <model/modelutils.h> +#include <nodeinstanceview.h> #include <nodelistproperty.h> -#include <variantproperty.h> +#include <nodeproperty.h> +#include <qmldesignerconstants.h> +#include <qmldesignericons.h> +#include <qmldesignerplugin.h> #include <qmlitemnode.h> #include <rewritingexception.h> -#include <nodeinstanceview.h> #include <theme.h> +#include <variantproperty.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> @@ -286,7 +287,6 @@ void NavigatorView::dragStarted(QMimeData *mimeData) if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { // We use arbitrary type name because at this time we don't have effect maker // specific type - m_widget->setDragType(Storage::Info::EffectMaker); m_widget->update(); } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { m_widget->setDragType(Constants::MIME_TYPE_ASSET_TEXTURE3D); @@ -450,7 +450,7 @@ void NavigatorView::changeToComponent(const QModelIndex &index) const ModelNode doubleClickNode = modelNodeForIndex(index); if (doubleClickNode.metaInfo().isFileComponent()) Core::EditorManager::openEditor(Utils::FilePath::fromString( - doubleClickNode.metaInfo().componentFileName()), + ModelUtils::componentFilePath(doubleClickNode)), Utils::Id(), Core::EditorManager::DoNotMakeVisible); } @@ -468,7 +468,7 @@ QAbstractItemModel *NavigatorView::currentModel() const const ProjectExplorer::FileNode *NavigatorView::fileNodeForModelNode(const ModelNode &node) const { - QString filename = node.metaInfo().componentFileName(); + QString filename = ModelUtils::componentFilePath(node); Utils::FilePath filePath = Utils::FilePath::fromString(filename); ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile( filePath); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 3b991a5f622..eaa5ef95738 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -958,7 +958,7 @@ void PropertyEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNod QUrl PropertyEditorQmlBackend::getQmlUrlForMetaInfo(const NodeMetaInfo &metaInfo, TypeName &className) { if (metaInfo.isValid()) { - const NodeMetaInfos hierarchy = metaInfo.classHierarchy(); + const NodeMetaInfos hierarchy = metaInfo.selfAndPrototypes(); for (const NodeMetaInfo &info : hierarchy) { QUrl fileUrl = fileToUrl(locateQmlFile(info, QString::fromUtf8(qmlFileName(info)))); if (fileUrl.isValid()) { diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 3f3ddaaf162..f042df5241b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -339,7 +339,7 @@ void PropertyEditorValue::resetValue() void PropertyEditorValue::setEnumeration(const QString &scope, const QString &name) { - Enumeration newEnumeration(scope, name); + Enumeration newEnumeration(scope.toUtf8(), name.toUtf8()); setValueWithEmit(QVariant::fromValue(newEnumeration)); } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 5d00f373c49..ba6078a778a 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -450,7 +450,7 @@ void PropertyEditorView::setupQmlBackend() TypeName diffClassName; if (commonAncestor.isValid()) { diffClassName = commonAncestor.typeName(); - const NodeMetaInfos hierarchy = commonAncestor.classHierarchy(); + const NodeMetaInfos hierarchy = commonAncestor.selfAndPrototypes(); for (const NodeMetaInfo &metaInfo : hierarchy) { if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsFile)) break; diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css index 4ff1b46f52d..2fc05b6014f 100644 --- a/src/plugins/qmldesigner/components/resources/dockwidgets.css +++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css @@ -93,6 +93,20 @@ ADS--TitleBarButton:press { background: creatorTheme.DStitleBarButtonPress; } +#floatingTitleMaxButton { + margin: 1px; + background: none; + border: none; +} + +#floatingTitleMaxButton:hover { + background: creatorTheme.DStitleBarButtonHover; +} + +#floatingTitleMaxButton:pressed { + background: creatorTheme.DStitleBarButtonPress; +} + QScrollArea#dockWidgetScrollArea { background-color: creatorTheme.DSpanelBackground; padding: 0px; @@ -139,3 +153,180 @@ ADS--DockAreaTitleBar { ADS--DockAreaWidget[focused="true"] ADS--DockAreaTitleBar { border-bottom-color: creatorTheme.DStabFocusBackground; } + +ADS--AutoHideTab { + qproperty-iconSize: 16px 16px; /* this is optional in case you would like to change icon size */ + background: none; + border: none; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; +} + +ADS--AutoHideTab:hover { + color: creatorTheme.DSinteraction; +} + +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="0"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="1"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + +ADS--AutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ADS--AutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid creatorTheme.DSinteraction; +} + +ADS--AutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ADS--AutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ADS--AutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid creatorTheme.DSinteraction; +} + +/* Auto hide tabs with icon only */ +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + +/* Auto hide tabs with icon only hover */ +ADS--AutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid creatorTheme.DSinteraction; +} + +ADS--AutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid creatorTheme.DSinteraction; +} + +ADS--AutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid creatorTheme.DSinteraction; +} + +ADS--AutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ADS--AutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid creatorTheme.DSinteraction; +} + +/* AutoHideSideBar */ +ADS--AutoHideSideBar { + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + +ADS--AutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ADS--AutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ADS--AutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ADS--AutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + +/* AutoHideDockContainer */ +ADS--AutoHideDockContainer { + background: palette(window); +} + +ADS--AutoHideDockContainer ADS--DockAreaTitleBar { + background: creatorTheme.DSinteraction; + padding: 0px; + border: none; +} + +/* + * This is required because the ADS--DockAreaWidget[focused="true"] will + * overwrite the ADS--AutoHideDockContainer ADS--DockAreaTitleBar rule + */ +ADS--AutoHideDockContainer ADS--DockAreaWidget[focused="true"] ADS--DockAreaTitleBar { + background: creatorTheme.DSinteraction; + padding: 0px; + border: none; +} + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + +/* AutoHideDockContainer titlebar buttons */ +#dockAreaAutoHideButton { + /*qproperty-icon: url(:/ads/images/vs-pin-button.svg);*/ + qproperty-iconSize: 16px; +} + +ADS--AutoHideDockContainer #dockAreaAutoHideButton { + /*qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg);*/ + qproperty-iconSize: 16px; +} + +ADS--AutoHideDockContainer #dockAreaCloseButton{ + /*qproperty-icon: url(:/ads/images/close-button-focused.svg)*/ +} + +ADS--AutoHideDockContainer ADS--TitleBarButton:hover { + background: rgba(255, 255, 255, 48); +} + +ads--CAutoHideDockContainer ADS--TitleBarButton:pressed { + background: rgba(255, 255, 255, 96); +} + +/* AutoHideDockContainer Titlebar and Buttons */ + +/* ResizeHandle */ +ADS--ResizeHandle { + background: palette(window); +} + + +ADS--AutoHideDockContainer[sideBarLocation="0"] ADS--ResizeHandle { + border-top: 1px solid palette(dark); +} + +ADS--AutoHideDockContainer[sideBarLocation="1"] ADS--ResizeHandle { + border-left: 1px solid palette(dark); +} + +ADS--AutoHideDockContainer[sideBarLocation="2"] ADS--ResizeHandle { + border-right: 1px solid palette(dark); +} + +ADS--AutoHideDockContainer[sideBarLocation="3"] ADS--ResizeHandle { + border-top: 1px solid palette(dark); +} diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 6c6102036ac..0826df54848 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -16,12 +16,13 @@ #include <nodelistproperty.h> #include <qmldesignerplugin.h> -#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorconstants.h> @@ -97,9 +98,12 @@ void TextEditorView::modelAboutToBeDetached(Model *model) m_widget->setTextEditor(nullptr); // in case the user closed it explicit we do not want to do anything with the editor - if (TextEditor::BaseTextEditor *textEditor = - QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()) { - QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); + if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) { + if (TextEditor::BaseTextEditor *textEditor = QmlDesignerPlugin::instance() + ->currentDesignDocument() + ->textEditor()) { + QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); + } } } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 98601ce45ed..73c3c65f30e 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -416,7 +416,7 @@ void TextureEditorView::setupQmlBackend() TypeName diffClassName; if (NodeMetaInfo metaInfo = m_selectedTexture.metaInfo()) { diffClassName = metaInfo.typeName(); - for (const NodeMetaInfo &metaInfo : metaInfo.classHierarchy()) { + for (const NodeMetaInfo &metaInfo : metaInfo.selfAndPrototypes()) { if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsUrl)) break; qmlSpecificsUrl = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName() diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index c917dfe8a84..99c46b43b33 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -132,17 +132,21 @@ void CrumbleBarModel::onCrumblePathElementClicked(int i) WorkspaceModel::WorkspaceModel(QObject *) { - connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + auto connectDockManager = [this]() -> bool { const auto dockManager = designModeWidget()->dockManager(); + if (!dockManager) + return false; connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { beginResetModel(); endResetModel(); }); - beginResetModel(); endResetModel(); - }); + return true; + }; + if (!connectDockManager()) + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, connectDockManager); } int WorkspaceModel::rowCount(const QModelIndex &) const @@ -288,19 +292,25 @@ ToolBarBackend::ToolBarBackend(QObject *parent) this, &ToolBarBackend::currentStyleChanged); - connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + auto connectDockManager = [this]() -> bool { const auto dockManager = designModeWidget()->dockManager(); - - connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) { - emit currentWorkspaceChanged(); - }); - - connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { - emit currentWorkspaceChanged(); - }); - + if (!dockManager) + return false; + + connect(dockManager, + &ADS::DockManager::workspaceLoaded, + this, + &ToolBarBackend::currentWorkspaceChanged); + connect(dockManager, + &ADS::DockManager::workspaceListChanged, + this, + &ToolBarBackend::currentWorkspaceChanged); emit currentWorkspaceChanged(); - }); + return true; + }; + + if (!connectDockManager()) + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, connectDockManager); auto editorManager = Core::EditorManager::instance(); diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h index 4c12af14fef..b93626a331b 100644 --- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h +++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h @@ -108,6 +108,8 @@ inline constexpr AuxiliaryDataKeyView rotBlockProperty{AuxiliaryDataType::NodeIn inline constexpr AuxiliaryDataKeyView languageProperty{AuxiliaryDataType::Temporary, "language"}; inline constexpr AuxiliaryDataKeyView bakeLightsManualProperty{AuxiliaryDataType::Document, "bakeLightsManual"}; +inline constexpr AuxiliaryDataKeyView contextImageProperty{AuxiliaryDataType::Temporary, + "contextImage"}; // Most material preview aux properties are duplicated as document and instance types, as they // are both required to be persistent and used at runtime to control material preview rendering diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index f0a5b8e7700..5dbab393d21 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -44,6 +44,11 @@ public: class QMLDESIGNERCORE_EXPORT Import { + using Imports = QList<Import>; + QMLDESIGNERCORE_EXPORT friend Imports set_strict_difference(const Imports &first, + const Imports &second); + enum class Type { Empty, Library, File }; + public: Import() = default; @@ -51,14 +56,14 @@ public: static Import createFileImport(const QString &file, const QString &version = QString(), const QString &alias = QString(), const QStringList &importPaths = QStringList()); static Import empty(); - bool isEmpty() const { return m_url.isEmpty() && m_file.isEmpty(); } - bool isFileImport() const { return m_url.isEmpty() && !m_file.isEmpty(); } - bool isLibraryImport() const { return !m_url.isEmpty() && m_file.isEmpty(); } + bool isEmpty() const { return m_type == Type::Empty; } + bool isFileImport() const { return m_type == Type::File; } + bool isLibraryImport() const { return m_type == Type::Library; } bool hasVersion() const { return !m_version.isEmpty(); } bool hasAlias() const { return !m_alias.isEmpty(); } - const QString &url() const { return m_url; } - const QString &file() const { return m_file; } + const QString &url() const { return m_type == Type::Library ? m_url : emptyString; } + const QString &file() const { return m_type == Type::File ? m_url : emptyString; } const QString &version() const { return m_version; } const QString &alias() const { return m_alias; } const QStringList &importPaths() const { return m_importPathList; } @@ -76,32 +81,40 @@ public: friend bool operator==(const Import &first, const Import &second) { - return first.m_url == second.m_url && first.m_file == second.m_file + return first.m_url == second.m_url && first.m_type == second.m_type && (first.m_version == second.m_version || first.m_version.isEmpty() || second.m_version.isEmpty()); } friend bool operator<(const Import &first, const Import &second) { - return std::tie(first.m_url, first.m_file) < std::tie(second.m_url, second.m_file); + return std::tie(first.m_url, first.m_type) < std::tie(second.m_url, second.m_type); } private: - Import(const QString &url, const QString &file, const QString &version, const QString &alias, const QStringList &importPaths); + Import(const QString &url, + const QString &version, + const QString &alias, + const QStringList &importPaths, + Type type); private: + inline static const QString emptyString; QString m_url; - QString m_file; QString m_version; QString m_alias; QStringList m_importPathList; + Type m_type = Type::Empty; }; QMLDESIGNERCORE_EXPORT size_t qHash(const Import &import); using Imports = QList<Import>; -QMLDESIGNERCORE_EXPORT Imports difference(const Imports &first, const Imports &second); +QMLDESIGNERCORE_EXPORT Imports set_difference(const Imports &first, const Imports &second); +QMLDESIGNERCORE_EXPORT Imports set_stict_difference(const Imports &first, const Imports &second); +QMLDESIGNERCORE_EXPORT Imports set_union(const Imports &first, const Imports &second); +QMLDESIGNERCORE_EXPORT Imports set_intersection(const Imports &first, const Imports &second); template<typename Callable> void differenceCall(const Imports &first, const Imports &second, Callable callable) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 3dbb0cceac2..1577fc24319 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -8,6 +8,7 @@ #include <documentmessage.h> #include <model/modelresourcemanagementinterface.h> #include <projectstorage/projectstoragefwd.h> +#include <projectstorageids.h> #include <QMimeData> #include <QObject> @@ -62,12 +63,16 @@ class QMLDESIGNERCORE_EXPORT Model : public QObject public: enum ViewNotification { NotifyView, DoNotNotifyView }; - Model(ProjectStorageType &projectStorage, + Model(ProjectStorageDependencies projectStorageDependencies, const TypeName &type, int major = 1, int minor = 1, Model *metaInfoProxyModel = nullptr, std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}); + Model(ProjectStorageDependencies projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &fileUrl); Model(const TypeName &typeName, int major = 1, int minor = 1, @@ -86,17 +91,29 @@ public: new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))); } - static ModelPointer create(ProjectStorageType &projectStorage, + static ModelPointer create(ProjectStorageDependencies projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &fileUrl) + { + return ModelPointer(new Model(projectStorageDependencies, typeName, imports, fileUrl)); + } + static ModelPointer create(ProjectStorageDependencies m_projectStorageDependencies, const TypeName &typeName, int major = 1, int minor = 1, std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}) { - return ModelPointer( - new Model(projectStorage, typeName, major, minor, nullptr, std::move(resourceManagement))); + return ModelPointer(new Model(m_projectStorageDependencies, + typeName, + major, + minor, + nullptr, + std::move(resourceManagement))); } QUrl fileUrl() const; + SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); const MetaInfo metaInfo() const; @@ -105,12 +122,16 @@ public: bool hasNodeMetaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; void setMetaInfo(const MetaInfo &metaInfo); + NodeMetaInfo boolMetaInfo() const; NodeMetaInfo flowViewFlowActionAreaMetaInfo() const; NodeMetaInfo flowViewFlowDecisionMetaInfo() const; NodeMetaInfo flowViewFlowItemMetaInfo() const; NodeMetaInfo flowViewFlowTransitionMetaInfo() const; NodeMetaInfo flowViewFlowWildcardMetaInfo() const; NodeMetaInfo fontMetaInfo() const; + NodeMetaInfo qmlQtObjectMetaInfo() const; + NodeMetaInfo qtQmlModelsListModelMetaInfo() const; + NodeMetaInfo qtQmlModelsListElementMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DMaterialMetaInfo() const; @@ -145,6 +166,7 @@ public: QHash<QStringView, ModelNode> idModelNodeDict(); ModelNode createModelNode(const TypeName &typeName); + void changeRootNodeType(const TypeName &type); void removeModelNodes(ModelNodes nodes, BypassModelResourceManagement = BypassModelResourceManagement::No); @@ -157,7 +179,7 @@ public: const Imports &imports() const; const Imports &possibleImports() const; const Imports &usedImports() const; - void changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved); + void changeImports(Imports importsToBeAdded, Imports importsToBeRemoved); void setPossibleImports(Imports possibleImports); void setUsedImports(Imports usedImports); bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; @@ -196,6 +218,8 @@ public: void endDrag(); NotNullPointer<const ProjectStorageType> projectStorage() const; + const PathCacheType &pathCache() const; + PathCacheType &pathCache(); private: template<const auto &moduleName, const auto &typeName> diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 44154ce10b1..2483893d9a8 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -16,6 +16,9 @@ using PropertyTypeList = QList<PropertyName>; using IdName = QByteArray; class Model; class ModelNode; +class NonLockingMutex; +template<typename ProjectStorage, typename Mutex = NonLockingMutex> +class SourcePathCache; struct ModelDeleter { @@ -35,10 +38,18 @@ constexpr bool useProjectStorage() #ifdef QDS_MODEL_USE_PROJECTSTORAGEINTERFACE using ProjectStorageType = ProjectStorageInterface; +using PathCacheType = SourcePathCacheInterface; #else using ProjectStorageType = ProjectStorage<Sqlite::Database>; +using PathCacheType = SourcePathCache<ProjectStorageType, NonLockingMutex>; #endif +struct ProjectStorageDependencies +{ + ProjectStorageType &storage; + PathCacheType &cache; +}; + enum class PropertyType { None, Variant, diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 657093b0ac0..9c4b8fb4efe 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -81,7 +81,7 @@ public: ModelNode &operator=(const ModelNode &) = default; ModelNode(ModelNode &&) = default; ModelNode &operator=(ModelNode &&) noexcept = default; - ~ModelNode(); + ~ModelNode() = default; TypeName type() const; QString simplifiedTypeName() const; @@ -182,6 +182,7 @@ public: QVariant auxiliaryDataWithDefault(AuxiliaryDataKeyView key) const; QVariant auxiliaryDataWithDefault(AuxiliaryDataKeyDefaultValue key) const; void setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data) const; + void setAuxiliaryDataWithoutLock(AuxiliaryDataKeyView key, const QVariant &data) const; void setAuxiliaryData(AuxiliaryDataType type, Utils::SmallStringView name, const QVariant &data) const; void setAuxiliaryDataWithoutLock(AuxiliaryDataType type, Utils::SmallStringView name, @@ -267,7 +268,7 @@ public: } private: // functions - Internal::InternalNodePointer internalNode() const; + Internal::InternalNodePointer internalNode() const { return m_internalNode; } bool hasLocked() const; diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 55ce166d904..0ee4e19fe37 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -53,6 +53,8 @@ public: TypeId id() const { return m_typeId; } bool isFileComponent() const; + bool isProjectComponent() const; + bool isInProjectModule() const; bool hasProperty(::Utils::SmallStringView propertyName) const; PropertyMetaInfos properties() const; PropertyMetaInfos localProperties() const; @@ -63,8 +65,8 @@ public: PropertyMetaInfo defaultProperty() const; bool hasDefaultProperty() const; - std::vector<NodeMetaInfo> classHierarchy() const; - std::vector<NodeMetaInfo> superClasses() const; + std::vector<NodeMetaInfo> selfAndPrototypes() const; + std::vector<NodeMetaInfo> prototypes() const; NodeMetaInfo commonBase(const NodeMetaInfo &metaInfo) const; bool defaultPropertyIsComponent() const; @@ -74,6 +76,10 @@ public: int majorVersion() const; int minorVersion() const; + Storage::Info::ExportedTypeNames allExportedTypeNames() const; + Storage::Info::ExportedTypeNames exportedTypeNamesForSourceId(SourceId sourceId) const; + + SourceId sourceId() const; QString componentFileName() const; bool isBasedOn(const NodeMetaInfo &metaInfo) const; @@ -107,7 +113,6 @@ public: bool isAlias() const; bool isBool() const; bool isColor() const; - bool isEffectMaker() const; bool isFloat() const; bool isFlowViewFlowActionArea() const; bool isFlowViewFlowDecision() const; @@ -121,10 +126,10 @@ public: bool isInteger() const; bool isLayoutable() const; bool isListOrGridView() const; + bool isNumber() const; bool isQmlComponent() const; bool isQtMultimediaSoundEffect() const; bool isQtObject() const; - bool isQtQuick3D() const; bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBuffer() const; bool isQtQuick3DCamera() const; @@ -137,7 +142,7 @@ public: bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; - bool isQtQuick3DParticleAbstractShape() const; + bool isQtQuick3DParticlesAbstractShape() const; bool isQtQuick3DParticles3DAffector3D() const; bool isQtQuick3DParticles3DAttractor3D() const; bool isQtQuick3DParticles3DParticle3D() const; diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index b4e4aa11f82..bc66e0d2b20 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -5,6 +5,8 @@ #include <sqlite/sqliteids.h> +#include <utils/span.h> + namespace QmlDesigner { enum class BasicIdType { @@ -48,6 +50,7 @@ using SourceIds = std::vector<SourceId>; using ModuleId = Sqlite::BasicId<BasicIdType::Module, int>; using ModuleIds = std::vector<ModuleId>; +using ModuleIdSpan = Utils::span<ModuleId>; using ProjectPartId = Sqlite::BasicId<BasicIdType::ProjectPartId>; using ProjectPartIds = std::vector<ProjectPartId>; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index fee28a9dd3a..bdbb2b7ee7b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -3,66 +3,67 @@ #include "nodeinstanceview.h" -#include "abstractproperty.h" -#include "bindingproperty.h" -#include "captureddatacommand.h" -#include "changeauxiliarycommand.h" -#include "changebindingscommand.h" -#include "changefileurlcommand.h" -#include "changeidscommand.h" -#include "changelanguagecommand.h" -#include "changenodesourcecommand.h" -#include "changepreviewimagesizecommand.h" -#include "changeselectioncommand.h" -#include "changestatecommand.h" -#include "changevaluescommand.h" -#include "childrenchangedcommand.h" -#include "clearscenecommand.h" -#include "completecomponentcommand.h" -#include "componentcompletedcommand.h" -#include "connectionmanagerinterface.h" -#include "createinstancescommand.h" -#include "createscenecommand.h" -#include "debugoutputcommand.h" -#include "informationchangedcommand.h" -#include "imageutils.h" -#include "inputeventcommand.h" -#include "nodeabstractproperty.h" -#include "nodeinstanceserverproxy.h" -#include "nodelistproperty.h" -#include "pixmapchangedcommand.h" -#include "puppettocreatorcommand.h" -#include "qml3dnode.h" -#include "qmlchangeset.h" -#include "qmldesignerconstants.h" -#include "qmlstate.h" -#include "qmltimeline.h" -#include "qmltimelinekeyframegroup.h" -#include "qmlvisualnode.h" -#include "removeinstancescommand.h" -#include "removepropertiescommand.h" -#include "removesharedmemorycommand.h" -#include "reparentinstancescommand.h" -#include "scenecreatedcommand.h" -#include "statepreviewimagechangedcommand.h" -#include "tokencommand.h" -#include "update3dviewstatecommand.h" -#include "valueschangedcommand.h" -#include "variantproperty.h" -#include "view3dactioncommand.h" -#include "requestmodelnodepreviewimagecommand.h" -#include "nanotracecommand.h" -#include "nanotrace/nanotrace.h" +#include <abstractproperty.h> +#include <bindingproperty.h> +#include <captureddatacommand.h> +#include <changeauxiliarycommand.h> +#include <changebindingscommand.h> +#include <changefileurlcommand.h> +#include <changeidscommand.h> +#include <changelanguagecommand.h> +#include <changenodesourcecommand.h> +#include <changepreviewimagesizecommand.h> +#include <changeselectioncommand.h> +#include <changestatecommand.h> +#include <changevaluescommand.h> +#include <childrenchangedcommand.h> +#include <clearscenecommand.h> +#include <completecomponentcommand.h> +#include <componentcompletedcommand.h> +#include <connectionmanagerinterface.h> +#include <createinstancescommand.h> +#include <createscenecommand.h> +#include <debugoutputcommand.h> +#include <imageutils.h> +#include <informationchangedcommand.h> +#include <inputeventcommand.h> +#include <nanotrace/nanotrace.h> +#include <nanotracecommand.h> +#include <nodeabstractproperty.h> +#include <nodeinstanceserverproxy.h> +#include <nodelistproperty.h> +#include <pixmapchangedcommand.h> +#include <puppettocreatorcommand.h> +#include <qml3dnode.h> +#include <qmlchangeset.h> +#include <qmldesignerconstants.h> +#include <qmlstate.h> +#include <qmltimeline.h> +#include <qmltimelinekeyframegroup.h> +#include <qmlvisualnode.h> +#include <removeinstancescommand.h> +#include <removepropertiescommand.h> +#include <removesharedmemorycommand.h> +#include <reparentinstancescommand.h> +#include <requestmodelnodepreviewimagecommand.h> +#include <scenecreatedcommand.h> +#include <statepreviewimagechangedcommand.h> +#include <tokencommand.h> +#include <update3dviewstatecommand.h> +#include <valueschangedcommand.h> +#include <variantproperty.h> +#include <view3dactioncommand.h> #include <auxiliarydataproperties.h> #include <designersettings.h> #include <externaldependenciesinterface.h> #include <metainfo.h> #include <model.h> +#include <model/modelutils.h> #include <modelnode.h> #include <nodehints.h> -#include <rewriterview.h> #include <qmlitemnode.h> +#include <rewriterview.h> #include <utils/hdrimage.h> @@ -1077,18 +1078,20 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (parentTakesOverRendering(instance.modelNode())) nodeFlags |= InstanceContainer::ParentTakesOverRendering; + const auto modelNode = instance.modelNode(); InstanceContainer container(instance.instanceId(), - instance.modelNode().type(), - instance.modelNode().majorVersion(), - instance.modelNode().minorVersion(), - instance.modelNode().metaInfo().componentFileName(), - instance.modelNode().nodeSource(), + modelNode.type(), + modelNode.majorVersion(), + modelNode.minorVersion(), + ModelUtils::componentFilePath(modelNode), + modelNode.nodeSource(), nodeSourceType, nodeMetaType, nodeFlags); - if (!parentIsBehavior(instance.modelNode())) + if (!parentIsBehavior(modelNode)) { instanceContainerList.append(container); + } } QVector<ReparentContainer> reparentContainerList; @@ -1243,12 +1246,13 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis if (parentTakesOverRendering(instance.modelNode())) nodeFlags |= InstanceContainer::ParentTakesOverRendering; + const auto modelNode = instance.modelNode(); InstanceContainer container(instance.instanceId(), - instance.modelNode().type(), - instance.modelNode().majorVersion(), - instance.modelNode().minorVersion(), - instance.modelNode().metaInfo().componentFileName(), - instance.modelNode().nodeSource(), + modelNode.type(), + modelNode.majorVersion(), + modelNode.minorVersion(), + ModelUtils::componentFilePath(modelNode), + modelNode.nodeSource(), nodeSourceType, nodeMetaType, nodeFlags); @@ -1303,17 +1307,20 @@ ChangeValuesCommand NodeInstanceView::createChangeValueCommand(const QList<Varia { QVector<PropertyValueContainer> containerList; - const bool reflectionFlag = m_puppetTransaction.isValid() && (!currentTimeline().isValid() || !currentTimeline().isRecording()); + bool reflectionFlag = m_puppetTransaction.isValid() + && (!currentTimeline().isValid() || !currentTimeline().isRecording()); for (const VariantProperty &property : propertyList) { ModelNode node = property.parentModelNode(); + if (QmlPropertyChanges::isValidQmlPropertyChanges(node)) + reflectionFlag = false; + if (node.isValid() && hasInstanceForModelNode(node)) { NodeInstance instance = instanceForModelNode(node); PropertyValueContainer container(instance.instanceId(), property.name(), property.value(), property.dynamicTypeName()); container.setReflectionFlag(reflectionFlag); containerList.append(container); } - } return ChangeValuesCommand(containerList); @@ -1783,9 +1790,9 @@ void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node, if (renderInstance.isValid()) renderItemId = renderInstance.instanceId(); if (renderNode.isComponent()) - componentPath = renderNode.metaInfo().componentFileName(); + componentPath = ModelUtils::componentFilePath(renderNode); } else if (node.isComponent()) { - componentPath = node.metaInfo().componentFileName(); + componentPath = ModelUtils::componentFilePath(node); } const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index ece5b4bf6f1..8cdc590135f 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -80,7 +80,7 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node) : m_modelNode(node) if (!itemLibraryEntryList.isEmpty()) m_hints = itemLibraryEntryList.constFirst().hints(); } else { /* If we have meta information we run the complete type hierarchy and check for hints */ - const auto classHierarchy = m_modelNode.metaInfo().classHierarchy(); + const auto classHierarchy = m_modelNode.metaInfo().selfAndPrototypes(); for (const NodeMetaInfo &metaInfo : classHierarchy) { QList <ItemLibraryEntry> itemLibraryEntryList = libraryInfo->entriesForType( metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index fd25d9c450d..5121e6a663c 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1426,6 +1426,24 @@ bool NodeMetaInfo::isFileComponent() const return isValid() && m_privateData->isFileComponent(); } +bool NodeMetaInfo::isProjectComponent() const +{ + if constexpr (useProjectStorage()) { + return isValid() && bool(typeData().traits & Storage::TypeTraits::IsProjectComponent); + } + + return false; +} + +bool NodeMetaInfo::isInProjectModule() const +{ + if constexpr (useProjectStorage()) { + return isValid() && bool(typeData().traits & Storage::TypeTraits::IsInProjectModule); + } + + return false; +} + bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { if constexpr (useProjectStorage()) @@ -1568,31 +1586,30 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const bool NodeMetaInfo::hasDefaultProperty() const { if constexpr (useProjectStorage()) - return bool(typeData().defaultPropertyId); + return isValid() && bool(typeData().defaultPropertyId); else return !defaultPropertyName().isEmpty(); } -NodeMetaInfos NodeMetaInfo::classHierarchy() const +std::vector<NodeMetaInfo> NodeMetaInfo::selfAndPrototypes() const { if constexpr (useProjectStorage()) { - NodeMetaInfos hierarchy; - const auto typeIds = m_projectStorage->prototypeAndSelfIds(m_typeId); - hierarchy.reserve(typeIds.size()); - - for (TypeId typeId : typeIds) - hierarchy.emplace_back(typeId, m_projectStorage); - - return hierarchy; + if (isValid()) { + return Utils::transform<NodeMetaInfos>( + m_projectStorage->prototypeAndSelfIds(m_typeId), [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } } else { if (isValid()) { NodeMetaInfos hierarchy = {*this}; Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) + for (const TypeDescription &type : m_privateData->prototypes()) { hierarchy.emplace_back(model, type.className.toUtf8(), type.majorVersion, type.minorVersion); + } return hierarchy; } @@ -1601,17 +1618,15 @@ NodeMetaInfos NodeMetaInfo::classHierarchy() const return {}; } -NodeMetaInfos NodeMetaInfo::superClasses() const +NodeMetaInfos NodeMetaInfo::prototypes() const { if constexpr (useProjectStorage()) { - NodeMetaInfos hierarchy; - const auto typeIds = m_projectStorage->prototypeIds(m_typeId); - hierarchy.reserve(typeIds.size()); - - for (TypeId typeId : typeIds) - hierarchy.emplace_back(typeId, m_projectStorage); - - return hierarchy; + if (isValid()) { + return Utils::transform<NodeMetaInfos>( + m_projectStorage->prototypeIds(m_typeId), [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } } else { if (isValid()) { NodeMetaInfos hierarchy; @@ -1673,18 +1688,61 @@ int NodeMetaInfo::minorVersion() const return -1; } +Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) { + return m_projectStorage->exportedTypeNames(m_typeId); + } + } + + return {}; +} + +Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(SourceId sourceId) const +{ + if constexpr (useProjectStorage()) { + if (isValid()) { + return m_projectStorage->exportedTypeNames(m_typeId, sourceId); + } + } + + return {}; +} + +SourceId NodeMetaInfo::sourceId() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) { + return typeData().sourceId; + } + } + + return SourceId{}; +} + QString NodeMetaInfo::componentFileName() const { - if (isValid()) - return m_privateData->componentFileName(); + if constexpr (!useProjectStorage()) { + if (isValid()) { + return m_privateData->componentFileName(); + } + } else { + if (isValid()) { + return m_privateData->componentFileName(); + } + } return {}; } QString NodeMetaInfo::importDirectoryPath() const { - if (isValid()) - return m_privateData->importDirectoryPath(); + if constexpr (!useProjectStorage()) { + if (isValid()) { + return m_privateData->importDirectoryPath(); + } + } return {}; } @@ -1718,7 +1776,7 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino stringIdentifier(type, majorVersion, minorVersion))) return false; //take a shortcut - optimization - const NodeMetaInfos superClassList = superClasses(); + const NodeMetaInfos superClassList = prototypes(); for (const NodeMetaInfo &superClass : superClassList) { if (superClass.m_privateData->cleverCheckType(type)) { m_privateData->prototypeCachePositives().insert( @@ -1732,8 +1790,27 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino bool NodeMetaInfo::isSuitableForMouseAreaFill() const { - return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") - && !isSubclassOf("QtQuick.Controls.Control") && !isSubclassOf("QtQuick.Templates.Control"); + if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + + using namespace Storage::Info; + auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>(); + auto mouseAreaId = m_projectStorage->commonTypeId<QtQuick, MouseArea>(); + auto controlsControlId = m_projectStorage->commonTypeId<QtQuick_Controls, Control>(); + auto templatesControlId = m_projectStorage->commonTypeId<QtQuick_Templates, Control>(); + + return m_projectStorage->isBasedOn(m_typeId, + itemId, + mouseAreaId, + controlsControlId, + templatesControlId); + } else { + return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") + && !isSubclassOf("QtQuick.Controls.Control") + && !isSubclassOf("QtQuick.Templates.Control"); + } } bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const @@ -1926,6 +2003,10 @@ namespace { template<const char *moduleName, const char *typeName> bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId) { + if (!typeId) { + return false; + } + auto base = projectStorage->commonTypeId<moduleName, typeName>(); return projectStorage->isBasedOn(typeId, base); @@ -1935,6 +2016,10 @@ bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage bool NodeMetaInfo::isGraphicalItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>(); auto windowId = m_projectStorage->commonTypeId<QtQuick_Window, Window>(); @@ -1963,6 +2048,10 @@ bool NodeMetaInfo::isQtObject() const bool NodeMetaInfo::isLayoutable() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto positionerId = m_projectStorage->commonTypeId<QtQuick, Positioner>(); auto layoutId = m_projectStorage->commonTypeId<QtQuick_Layouts, Layout>(); @@ -1990,6 +2079,10 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const bool NodeMetaInfo::isView() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>(); auto gridViewId = m_projectStorage->commonTypeId<QtQuick, GridView>(); @@ -2018,7 +2111,7 @@ bool NodeMetaInfo::isVector2D() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>()); } else { if (!m_privateData) return false; @@ -2033,7 +2126,7 @@ bool NodeMetaInfo::isVector3D() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>()); } else { if (!m_privateData) return false; @@ -2048,7 +2141,7 @@ bool NodeMetaInfo::isVector4D() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>()); } else { if (!m_privateData) return false; @@ -2134,6 +2227,10 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const bool NodeMetaInfo::isListOrGridView() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>(); auto gridViewId = m_projectStorage->commonTypeId<QtQuick, GridView>(); @@ -2143,6 +2240,31 @@ bool NodeMetaInfo::isListOrGridView() const } } +bool NodeMetaInfo::isNumber() const +{ + if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + + using namespace Storage::Info; + auto intId = m_projectStorage->builtinTypeId<int>(); + auto uintId = m_projectStorage->builtinTypeId<uint>(); + auto floatId = m_projectStorage->builtinTypeId<float>(); + auto doubleId = m_projectStorage->builtinTypeId<double>(); + + return isTypeId(m_typeId, intId, uintId, floatId, doubleId); + } else { + if (!isValid()) { + return false; + } + + auto type = simplifiedTypeName(); + + return type == "int" || type == "uint" || type == "float" || type == "double"; + } +} + bool NodeMetaInfo::isQtQuickExtrasPicture() const { if constexpr (useProjectStorage()) { @@ -2177,7 +2299,11 @@ bool NodeMetaInfo::isQtQuickBorderImage() const bool NodeMetaInfo::isAlias() const { - return isValid() && m_privateData->qualfiedTypeName() == "alias"; + if constexpr (useProjectStorage()) { + return false; // there is no type alias + } else { + return isValid() && m_privateData->qualfiedTypeName() == "alias"; + } } bool NodeMetaInfo::isQtQuickPositioner() const @@ -2322,7 +2448,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const } } -bool NodeMetaInfo::isQtQuick3DParticleAbstractShape() const +bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; @@ -2437,6 +2563,10 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const bool NodeMetaInfo::isFlowViewItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto flowItemId = m_projectStorage->commonTypeId<FlowView, FlowItem>(); auto flowWildcardId = m_projectStorage->commonTypeId<FlowView, FlowWildcard>(); @@ -2540,7 +2670,7 @@ bool NodeMetaInfo::isFont() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, font>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, font>()); } else { return isValid() && m_privateData->qualfiedTypeName() == "font"; } @@ -2550,7 +2680,7 @@ bool NodeMetaInfo::isColor() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<QColor>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QColor>()); } else { if (!isValid()) return false; @@ -2561,18 +2691,11 @@ bool NodeMetaInfo::isColor() const } } -bool NodeMetaInfo::isEffectMaker() const -{ - // We use arbitrary type name because at this time we don't have effect maker - // specific type - return typeName() == QString::fromUtf8(Storage::Info::EffectMaker); -} - bool NodeMetaInfo::isBool() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<bool>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<bool>()); } else { if (!isValid()) return false; @@ -2587,7 +2710,7 @@ bool NodeMetaInfo::isInteger() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<int>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<int>()); } else { if (!isValid()) return false; @@ -2601,6 +2724,10 @@ bool NodeMetaInfo::isInteger() const bool NodeMetaInfo::isFloat() const { if constexpr (useProjectStorage()) { + if (!isValid()) { + return false; + } + using namespace Storage::Info; auto floatId = m_projectStorage->builtinTypeId<float>(); auto doubleId = m_projectStorage->builtinTypeId<double>(); @@ -2620,7 +2747,7 @@ bool NodeMetaInfo::isVariant() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<QVariant>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QVariant>()); } else { return isValid() && simplifiedTypeName() == "QVariant"; } @@ -2630,7 +2757,7 @@ bool NodeMetaInfo::isString() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<QString>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QString>()); } else { if (!isValid()) return false; @@ -2645,7 +2772,7 @@ bool NodeMetaInfo::isUrl() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isTypeId(m_typeId, m_projectStorage->builtinTypeId<QUrl>()); + return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QUrl>()); } else { if (!isValid()) return false; @@ -2805,7 +2932,8 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const return isBasedOnCommonType<QtQuick3D, CubeMapTexture>(m_projectStorage, m_typeId); } else { return isValid() - && (isSubclassOf("QtQuick3D.CubeMapTexture") || isSubclassOf("<cpp>.QQuick3DCubeMapTexture")); + && (isSubclassOf("QtQuick3D.CubeMapTexture") + || isSubclassOf("<cpp>.QQuick3DCubeMapTexture")); } } @@ -2832,7 +2960,7 @@ bool NodeMetaInfo::isQtQuick3DEffect() const bool NodeMetaInfo::isEnumeration() const { if constexpr (useProjectStorage()) - return bool(typeData().traits & Storage::TypeTraits::IsEnum); + return isValid() && bool(typeData().traits & Storage::TypeTraits::IsEnum); return false; } @@ -3046,9 +3174,26 @@ const PropertyName &PropertyMetaInfo::propertyName() const NodeMetaInfo NodeMetaInfo::commonBase(const NodeMetaInfo &metaInfo) const { - for (const NodeMetaInfo &info : metaInfo.superClasses()) { - if (isBasedOn(info)) - return info; + if constexpr (useProjectStorage()) { + if (isValid() && metaInfo) { + const auto firstTypeIds = m_projectStorage->prototypeAndSelfIds(m_typeId); + const auto secondTypeIds = m_projectStorage->prototypeAndSelfIds(metaInfo.m_typeId); + auto found + = std::find_if(firstTypeIds.begin(), firstTypeIds.end(), [&](TypeId firstTypeId) { + return std::find(secondTypeIds.begin(), secondTypeIds.end(), firstTypeId) + != secondTypeIds.end(); + }); + + if (found != firstTypeIds.end()) { + return NodeMetaInfo{*found, m_projectStorage}; + } + } + } else { + for (const NodeMetaInfo &info : metaInfo.selfAndPrototypes()) { + if (isBasedOn(info)) { + return info; + } + } } return {}; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index a8ad4b76e7c..64912f5b73b 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -927,10 +927,9 @@ static int getMajorVersionFromImport(const Model *model) static int getMajorVersionFromNode(const ModelNode &modelNode) { if (modelNode.metaInfo().isValid()) { - for (const NodeMetaInfo &info : modelNode.metaInfo().classHierarchy()) { - if (info.typeName() == "QtQml.QtObject" - || info.typeName() == "QtQuick.QtObject" - || info.typeName() == "QtQuick.Item") { + for (const NodeMetaInfo &info : modelNode.metaInfo().selfAndPrototypes()) { + if (info.typeName() == "QtQml.QtObject" || info.typeName() == "QtQuick.QtObject" + || info.typeName() == "QtQuick.Item") { return info.majorVersion(); } } @@ -942,7 +941,7 @@ static int getMajorVersionFromNode(const ModelNode &modelNode) static int getMinorVersionFromNode(const ModelNode &modelNode) { if (modelNode.metaInfo().isValid()) { - const NodeMetaInfos infos = modelNode.metaInfo().classHierarchy(); + const NodeMetaInfos infos = modelNode.metaInfo().selfAndPrototypes(); for (const NodeMetaInfo &info : infos) { if (info.typeName() == "QtQuick.QtObject" || info.typeName() == "QtQuick.Item") return info.minorVersion(); diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 558f6849be2..bf1dc59e155 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -148,7 +148,7 @@ AbstractProperty BindingProperty::resolveToProperty() const element = binding; } - if (node.isValid()) + if (node.isValid() && !element.contains(' ')) return node.property(element.toUtf8()); else return AbstractProperty(); diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 54b18810437..6d4cd19d8b8 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -11,17 +11,17 @@ namespace QmlDesigner { Import Import::createLibraryImport(const QString &url, const QString &version, const QString &alias, const QStringList &importPaths) { - return Import(url, QString(), version, alias, importPaths); + return Import(url, version, alias, importPaths, Type::Library); } Import Import::createFileImport(const QString &file, const QString &version, const QString &alias, const QStringList &importPaths) { - return Import(QString(), file, version, alias, importPaths); + return Import(file, version, alias, importPaths, Type::File); } Import Import::empty() { - return Import(QString(), QString(), QString(), QString(), QStringList()); + return Import(QString(), QString(), QString(), QStringList(), Type::Empty); } QString Import::toImportString() const @@ -33,12 +33,16 @@ QString Import::toImportString() const return result; } -Import::Import(const QString &url, const QString &file, const QString &version, const QString &alias, const QStringList &importPaths): - m_url(url), - m_file(file), - m_version(version), - m_alias(alias), - m_importPathList(importPaths) +Import::Import(const QString &url, + const QString &version, + const QString &alias, + const QStringList &importPaths, + Type type) + : m_url(url) + , m_version(version) + , m_alias(alias) + , m_importPathList(importPaths) + , m_type(type) { } @@ -137,7 +141,7 @@ size_t qHash(const Import &import) return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); } -Imports difference(const Imports &first, const Imports &second) +Imports set_difference(const Imports &first, const Imports &second) { Imports difference; difference.reserve(first.size()); @@ -150,4 +154,53 @@ Imports difference(const Imports &first, const Imports &second) return difference; } + +Imports set_union(const Imports &first, const Imports &second) +{ + Imports set_union; + set_union.reserve(std::min(first.size(), second.size())); + + std::set_union(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(set_union)); + + return set_union; +} + +Imports set_intersection(const Imports &first, const Imports &second) +{ + Imports set_intersection; + set_intersection.reserve(std::min(first.size(), second.size())); + + std::set_intersection(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(set_intersection)); + + return set_intersection; +} + +Imports set_strict_difference(const Imports &first, const Imports &second) +{ + Imports difference; + difference.reserve(first.size()); + + auto strictLess = [](const Import &first, const Import &second) { + return std::tie(first.m_url, first.m_type, first.m_version) + < std::tie(second.m_url, second.m_type, second.m_version); + }; + + std::set_difference(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(difference), + strictLess); + + return difference; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index 8b3ff86d2bc..bf8523653cf 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -199,8 +199,8 @@ public: int nodeSourceType = 0; QString behaviorPropertyName; QStringList scriptFunctions; - ModuleId moduleId; // is invalid if type is implicit - Utils::SmallString documentTypeName; // how the type is written in den Document + ModuleId moduleId; + ImportedTypeNameId importedTypeNameId; TypeId typeId; private: diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 12a8c091838..653ffea077a 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -6,6 +6,8 @@ #include "model_p.h" #include <modelnode.h> +#include "../projectstorage/sourcepath.h" +#include "../projectstorage/sourcepathcache.h" #include "abstractview.h" #include "auxiliarydataproperties.h" #include "internalbindingproperty.h" @@ -60,18 +62,21 @@ namespace QmlDesigner { namespace Internal { ModelPrivate::ModelPrivate(Model *model, - ProjectStorageType &projectStorage, + ProjectStorageDependencies projectStorageDependencies, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel, std::unique_ptr<ModelResourceManagementInterface> resourceManagement) - : projectStorage{&projectStorage} + : projectStorage{&projectStorageDependencies.storage} + , pathCache{&projectStorageDependencies.cache} , m_model{model} , m_resourceManagement{std::move(resourceManagement)} { m_metaInfoProxyModel = metaInfoProxyModel; + changeImports({Import::createLibraryImport({"QtQuick"})}, {}); + m_rootInternalNode = createNode( typeName, major, minor, {}, {}, {}, ModelNode::NodeWithoutSource, {}, true); @@ -80,6 +85,25 @@ ModelPrivate::ModelPrivate(Model *model, } ModelPrivate::ModelPrivate(Model *model, + ProjectStorageDependencies projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &fileUrl) + : projectStorage{&projectStorageDependencies.storage} + , pathCache{&projectStorageDependencies.cache} + , m_model{model} +{ + setFileUrl(fileUrl); + changeImports(std::move(imports), {}); + + m_rootInternalNode = createNode( + TypeName{typeName}, -1, -1, {}, {}, {}, ModelNode::NodeWithoutSource, {}, true); + + m_currentStateNode = m_rootInternalNode; + m_currentTimelineNode = m_rootInternalNode; +} + +ModelPrivate::ModelPrivate(Model *model, const TypeName &typeName, int major, int minor, @@ -117,29 +141,41 @@ void ModelPrivate::detachAllViews() } } -void ModelPrivate::changeImports(const Imports &toBeAddedImportList, - const Imports &toBeRemovedImportList) +namespace { +Storage::Imports createStorageImports(const Imports &imports, + ProjectStorageType &projectStorage, + SourceId fileId) +{ + return Utils::transform<Storage::Imports>(imports, [&](const Import &import) { + return Storage::Import{projectStorage.moduleId(Utils::SmallString{import.url()}), + import.majorVersion(), + import.minorVersion(), + fileId}; + }); +} + +} // namespace + +void ModelPrivate::changeImports(Imports toBeAddedImports, Imports toBeRemovedImports) { - Imports removedImportList; - for (const Import &import : toBeRemovedImportList) { - if (m_imports.contains(import)) { - removedImportList.append(import); - m_imports.removeOne(import); - } - } + std::sort(toBeAddedImports.begin(), toBeAddedImports.end()); + std::sort(toBeRemovedImports.begin(), toBeRemovedImports.end()); - Imports addedImportList; - for (const Import &import : toBeAddedImportList) { - if (!m_imports.contains(import)) { - addedImportList.append(import); - m_imports.append(import); - } - } + Imports removedImports = set_intersection(m_imports, toBeRemovedImports); + m_imports = set_difference(m_imports, removedImports); + + Imports allNewAddedImports = set_strict_difference(toBeAddedImports, m_imports); + Imports importWithoutAddedImport = set_difference(m_imports, allNewAddedImports); - std::sort(m_imports.begin(), m_imports.end()); + m_imports = set_union(importWithoutAddedImport, allNewAddedImports); - if (!removedImportList.isEmpty() || !addedImportList.isEmpty()) - notifyImportsChanged(addedImportList, removedImportList); + if (!removedImports.isEmpty() || !allNewAddedImports.isEmpty()) { + if (useProjectStorage()) { + auto imports = createStorageImports(m_imports, *projectStorage, m_sourceId); + projectStorage->synchronizeDocumentImports(std::move(imports), m_sourceId); + } + notifyImportsChanged(allNewAddedImports, removedImports); + } } void ModelPrivate::notifyImportsChanged(const Imports &addedImports, const Imports &removedImports) @@ -201,6 +237,9 @@ void ModelPrivate::setFileUrl(const QUrl &fileUrl) if (oldPath != fileUrl) { m_fileUrl = fileUrl; + if constexpr (useProjectStorage()) { + m_sourceId = pathCache->sourceId(SourcePath{fileUrl.path()}); + } for (const QPointer<AbstractView> &view : std::as_const(m_viewList)) view->fileUrlChanged(oldPath, fileUrl); @@ -220,23 +259,6 @@ void ModelPrivate::changeNodeType(const InternalNodePointer &node, const TypeNam } } -namespace { -QT_WARNING_PUSH -QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") - -std::pair<Utils::SmallStringView, Utils::SmallStringView> decomposeTypePath(Utils::SmallStringView typeName) -{ - auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); - - if (found == typeName.rend()) - return {}; - - return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; -} - -QT_WARNING_POP -} // namespace - InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, int majorVersion, int minorVersion, @@ -257,13 +279,7 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, auto newNode = std::make_shared<InternalNode>(typeName, majorVersion, minorVersion, internalId); - if constexpr (useProjectStorage()) { - auto [moduleName, shortTypeName] = decomposeTypePath(typeName); - ModuleId moduleId = projectStorage->moduleId(moduleName); - newNode->typeId = projectStorage->typeId(moduleId, - shortTypeName, - Storage::Version{majorVersion, minorVersion}); - } + setTypeId(newNode.get(), typeName); newNode->nodeSourceType = nodeSourceType; @@ -314,6 +330,55 @@ EnabledViewRange ModelPrivate::enabledViews() const return EnabledViewRange{m_viewList}; } +namespace { +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") + +std::pair<Utils::SmallStringView, Utils::SmallStringView> decomposeTypePath(Utils::SmallStringView typeName) +{ + auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); + + if (found == typeName.rend()) + return {{}, typeName}; + + return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; +} + +QT_WARNING_POP +} // namespace + +ImportedTypeNameId ModelPrivate::importedTypeNameId(Utils::SmallStringView typeName) +{ + if constexpr (useProjectStorage()) { + auto [moduleName, shortTypeName] = decomposeTypePath(typeName); + + if (moduleName.size()) { + QString aliasName = QString{moduleName}; + auto found = std::find_if(m_imports.begin(), m_imports.end(), [&](const Import &import) { + return import.alias() == aliasName; + }); + if (found != m_imports.end()) { + ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}); + ImportId importId = projectStorage->importId( + Storage::Import{moduleId, found->majorVersion(), found->minorVersion(), m_sourceId}); + return projectStorage->importedTypeNameId(importId, shortTypeName); + } + } + + return projectStorage->importedTypeNameId(m_sourceId, shortTypeName); + } + + return ImportedTypeNameId{}; +} + +void ModelPrivate::setTypeId(InternalNode *node, Utils::SmallStringView typeName) +{ + if constexpr (useProjectStorage()) { + node->importedTypeNameId = importedTypeNameId(typeName); + node->typeId = projectStorage->typeId(node->importedTypeNameId); + } +} + void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) { for (const ModelNode &node : resourceSet.removeModelNodes) { @@ -1343,9 +1408,11 @@ void ModelPrivate::clearParent(const InternalNodePointer &node) void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion) { Q_ASSERT(rootNode()); - rootNode()->typeName = type; - rootNode()->majorVersion = majorVersion; - rootNode()->minorVersion = minorVersion; + + m_rootInternalNode->typeName = type; + m_rootInternalNode->majorVersion = majorVersion; + m_rootInternalNode->minorVersion = minorVersion; + setTypeId(m_rootInternalNode.get(), type); notifyRootNodeTypeChanged(QString::fromUtf8(type), majorVersion, minorVersion); } @@ -1473,6 +1540,7 @@ WriteLocker::WriteLocker(ModelPrivate *model) if (m_model->m_writeLock) qWarning() << "QmlDesigner: Misbehaving view calls back to model!!!"; // FIXME: Enable it again + QTC_CHECK(!m_model->m_writeLock); Q_ASSERT(!m_model->m_writeLock); model->m_writeLock = true; } @@ -1484,6 +1552,7 @@ WriteLocker::WriteLocker(Model *model) if (m_model->m_writeLock) qWarning() << "QmlDesigner: Misbehaving view calls back to model!!!"; // FIXME: Enable it again + QTC_CHECK(!m_model->m_writeLock); Q_ASSERT(!m_model->m_writeLock); m_model->m_writeLock = true; } @@ -1493,6 +1562,7 @@ WriteLocker::~WriteLocker() if (!m_model->m_writeLock) qWarning() << "QmlDesigner: WriterLocker out of sync!!!"; // FIXME: Enable it again + QTC_CHECK(m_model->m_writeLock); Q_ASSERT(m_model->m_writeLock); m_model->m_writeLock = false; } @@ -1509,14 +1579,27 @@ void WriteLocker::lock(Model *model) } // namespace Internal -Model::Model(ProjectStorageType &projectStorage, +Model::Model(ProjectStorageDependencies projectStorageDependencies, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel, std::unique_ptr<ModelResourceManagementInterface> resourceManagement) + : d(std::make_unique<Internal::ModelPrivate>(this, + projectStorageDependencies, + typeName, + major, + minor, + metaInfoProxyModel, + std::move(resourceManagement))) +{} + +Model::Model(ProjectStorageDependencies projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &fileUrl) : d(std::make_unique<Internal::ModelPrivate>( - this, projectStorage, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) + this, projectStorageDependencies, typeName, std::move(imports), fileUrl)) {} Model::Model(const TypeName &typeName, @@ -1545,9 +1628,9 @@ const Imports &Model::usedImports() const return d->m_usedImportList; } -void Model::changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved) +void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) { - d->changeImports(importsToBeAdded, importsToBeRemoved); + d->changeImports(std::move(importsToBeAdded), std::move(importsToBeRemoved)); } void Model::setPossibleImports(Imports possibleImports) @@ -1747,6 +1830,16 @@ NotNullPointer<const ProjectStorageType> Model::projectStorage() const return d->projectStorage; } +const PathCacheType &Model::pathCache() const +{ + return *d->pathCache; +} + +PathCacheType &Model::pathCache() +{ + return *d->pathCache; +} + void ModelDeleter::operator()(class Model *model) { model->detachAllViews(); @@ -1877,13 +1970,18 @@ QUrl Model::fileUrl() const return d->fileUrl(); } +SourceId Model::fileUrlSourceId() const +{ + return d->m_sourceId; +} + /*! \brief Sets the URL against which relative URLs within the model should be resolved. \param url the base URL, i.e. the qml file path. */ void Model::setFileUrl(const QUrl &url) { - Q_ASSERT(url.isValid() && url.isLocalFile()); + QTC_ASSERT(url.isValid() && url.isLocalFile(), qDebug() << "url:" << url; return); Internal::WriteLocker locker(d.get()); d->setFileUrl(url); } @@ -1906,6 +2004,16 @@ void Model::setMetaInfo(const MetaInfo &metaInfo) d->setMetaInfo(metaInfo); } +NodeMetaInfo Model::boolMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QML, BoolType>(); + } else { + return metaInfo("QML.bool"); + } +} + template<const auto &moduleName, const auto &typeName> NodeMetaInfo Model::createNodeMetaInfo() const { @@ -1924,6 +2032,36 @@ NodeMetaInfo Model::fontMetaInfo() const } } +NodeMetaInfo Model::qtQmlModelsListModelMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QtQml_Models, ListModel>(); + } else { + return metaInfo("QtQml.Models.ListModel"); + } +} + +NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QtQml_Models, ListElement>(); + } else { + return metaInfo("QtQml.Models.ListElement"); + } +} + +NodeMetaInfo Model::qmlQtObjectMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QML, QtObject>(); + } else { + return metaInfo("QML.QtObject"); + } +} + NodeMetaInfo Model::qtQuickItemMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2220,13 +2358,8 @@ namespace { NodeMetaInfo Model::metaInfo(const TypeName &typeName, int majorVersion, int minorVersion) const { if constexpr (useProjectStorage()) { - auto [module, componentName] = moduleTypeName(typeName); - - ModuleId moduleId = d->projectStorage->moduleId(module); - TypeId typeId = d->projectStorage->typeId(moduleId, - componentName, - Storage::Version{majorVersion, minorVersion}); - return NodeMetaInfo(typeId, d->projectStorage); + return NodeMetaInfo(d->projectStorage->typeId(d->importedTypeNameId(typeName)), + d->projectStorage); } else { return NodeMetaInfo(metaInfoProxyModel(), typeName, majorVersion, minorVersion); } @@ -2356,6 +2489,13 @@ ModelNode Model::createModelNode(const TypeName &typeName) } } +void Model::changeRootNodeType(const TypeName &type) +{ + Internal::WriteLocker locker(this); + + d->changeRootNodeType(type, -1, -1); +} + void Model::removeModelNodes(ModelNodes nodes, BypassModelResourceManagement bypass) { nodes.erase(std::remove_if(nodes.begin(), nodes.end(), [](auto &&node) { return !node; }), diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 5c69e756164..324df37ddb2 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -98,13 +98,18 @@ class ModelPrivate : public QObject public: ModelPrivate(Model *model, - ProjectStorageType &projectStorage, + ProjectStorageDependencies m_projectStorageDependencies, const TypeName &type, int major, int minor, Model *metaInfoProxyModel, std::unique_ptr<ModelResourceManagementInterface> resourceManagement); ModelPrivate(Model *model, + ProjectStorageDependencies m_projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &filePath); + ModelPrivate(Model *model, const TypeName &type, int major, int minor, @@ -235,7 +240,7 @@ public: // Imports: const Imports &imports() const { return m_imports; } - void changeImports(const Imports &importsToBeAdded, const Imports &importToBeRemoved); + void changeImports(Imports importsToBeAdded, Imports importToBeRemoved); void notifyImportsChanged(const Imports &addedImports, const Imports &removedImports); void notifyPossibleImportsChanged(const Imports &possibleImports); void notifyUsedImportsChanged(const Imports &usedImportsChanged); @@ -306,9 +311,12 @@ private: static QList<std::tuple<InternalBindingPropertyPointer, QString>> toInternalBindingProperties( const ModelResourceSet::SetExpressions &setExpressions); EnabledViewRange enabledViews() const; + ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName); + void setTypeId(InternalNode *node, Utils::SmallStringView typeName); public: NotNullPointer<ProjectStorageType> projectStorage = nullptr; + NotNullPointer<PathCacheType> pathCache = nullptr; private: Model *m_model = nullptr; @@ -326,6 +334,7 @@ private: InternalNodePointer m_currentTimelineNode; std::unique_ptr<ModelResourceManagementInterface> m_resourceManagement; QUrl m_fileUrl; + SourceId m_sourceId; QPointer<RewriterView> m_rewriterView; QPointer<NodeInstanceView> m_nodeInstanceView; QPointer<Model> m_metaInfoProxyModel; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 45c076022da..cb862b03431 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -66,10 +66,6 @@ ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) , m_view(view) {} -/*! \brief does nothing -*/ -ModelNode::~ModelNode() = default; - /*! \brief returns the name of node which is a short cut to a property like objectName \return name of the node */ @@ -657,15 +653,6 @@ void ModelNode::destroy() //\} -/*! \name Property Manipulation - * This functions interact with properties. - */ - -Internal::InternalNodePointer ModelNode::internalNode() const -{ - return m_internalNode; -} - /*! \brief returns the model of the node \return returns the model of the node @@ -1002,6 +989,12 @@ void ModelNode::setAuxiliaryData(AuxiliaryDataKeyView key, const QVariant &data) } } +void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataKeyView key, const QVariant &data) const +{ + if (isValid()) + m_model->d->setAuxiliaryData(internalNode(), key, data); +} + void ModelNode::setAuxiliaryDataWithoutLock(AuxiliaryDataType type, Utils::SmallStringView name, const QVariant &data) const diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index 70da6f98b9c..efbe8f09783 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -4,6 +4,8 @@ #include "modelutils.h" #include <nodemetainfo.h> +#include <projectstorage/projectstorage.h> +#include <projectstorage/sourcepathcache.h> #include <utils/expected.h> @@ -102,4 +104,29 @@ PropertyMetaInfo metainfo(const ModelNode &node, const PropertyName &propertyNam return node.metaInfo().property(propertyName); } +QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo) +{ + if constexpr (useProjectStorage()) { + auto typeSourceId = metaInfo.sourceId(); + + if (typeSourceId && metaInfo.isFileComponent()) { + return pathCache.sourcePath(typeSourceId).toQString(); + } + } else { + return metaInfo.componentFileName(); + } + + return {}; +} + +QString componentFilePath(const ModelNode &node) +{ + if (node) { + const auto &pathCache = node.model()->pathCache(); + return ModelUtils::componentFilePath(pathCache, node.metaInfo()); + } + + return {}; +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index 578a7adb444..946882c2df7 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -29,4 +29,8 @@ QMLDESIGNERCORE_EXPORT PropertyMetaInfo metainfo(const AbstractProperty &propert QMLDESIGNERCORE_EXPORT PropertyMetaInfo metainfo(const ModelNode &node, const PropertyName &propertyName); +QMLDESIGNERCORE_EXPORT QString componentFilePath(const PathCacheType &pathCache, + const NodeMetaInfo &metaInfo); + +QMLDESIGNERCORE_EXPORT QString componentFilePath(const ModelNode &node); } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 3108e26a618..ccfffbc867b 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -937,7 +937,6 @@ QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path, QmlDesigner::Imports createQt5Modules() { return {QmlDesigner::Import::createLibraryImport("QtQuick", "5.15"), - QmlDesigner::Import::createLibraryImport("QtQuick3D", "5.15"), QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "5.15"), QmlDesigner::Import::createLibraryImport("QtQuick.Window", "5.15"), QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "5.15"), diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 05e90fdb285..fb4fb660660 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -31,12 +31,12 @@ inline constexpr char Camera[] = "Camera"; inline constexpr char Command[] = "Command"; inline constexpr char Component[] = "Component"; inline constexpr char Connections[] = "Connections"; +inline constexpr char Control[] = "Control"; inline constexpr char CubeMapTexture[] = "CubeMapTexture"; inline constexpr char DefaultMaterial[] = "DefaultMaterial"; inline constexpr char Dialog[] = "Dialog"; inline constexpr char DoubleType[] = "double"; inline constexpr char Effect[] = "Effect"; -inline constexpr char EffectMaker[] = "EffectMaker"; inline constexpr char FloatType[] = "float"; inline constexpr char FlowActionArea[] = "FlowActionArea"; inline constexpr char FlowDecision[] = "FlowDecision"; @@ -55,10 +55,13 @@ inline constexpr char Item[] = "Item"; inline constexpr char KeyframeGroup[] = "KeyframeGroup"; inline constexpr char Keyframe[] = "Keyframe"; inline constexpr char Layout[] = "Layout"; +inline constexpr char ListElement[] = "ListElement"; +inline constexpr char ListModel[] = "ListModel"; inline constexpr char ListView[] = "ListView"; inline constexpr char Loader[] = "Loader"; inline constexpr char Material[] = "Material"; inline constexpr char Model[] = "Model"; +inline constexpr char MouseArea[] = "MouseArea"; inline constexpr char Node[] = "Node"; inline constexpr char Particle3D[] = "Particle3D"; inline constexpr char ParticleEmitter3D[] = "ParticleEmitter3D"; @@ -80,6 +83,7 @@ inline constexpr char QQuickStateOperation[] = "QQuickStateOperation"; 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 QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; @@ -90,6 +94,7 @@ inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; inline constexpr char QtQuick_Extras[] = "QtQuick.Extras"; inline constexpr char QtQuick_Layouts[] = "QtQuick.Layouts"; inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; +inline constexpr char QtQuick_Templates[] = "QtQuick.Templates"; inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline"; inline constexpr char QtQuick_Window[] = "QtQuick.Window"; inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer"; @@ -114,6 +119,7 @@ inline constexpr char Texture[] = "Texture"; inline constexpr char TimelineAnimation[] = "TimelineAnimation"; inline constexpr char Timeline[] = "Timeline"; inline constexpr char Transition[] = "Transition"; +inline constexpr char UIntType[] = "uint"; inline constexpr char View3D[] = "View3D"; inline constexpr char Window[] = "Window"; inline constexpr char color[] = "color"; @@ -156,7 +162,10 @@ class CommonTypeCache CacheType<QML, url>, CacheType<QML, var>, CacheType<QML_cppnative, FloatType>, + CacheType<QML_cppnative, UIntType>, CacheType<QtMultimedia, SoundEffect>, + CacheType<QtQml_Models, ListElement>, + CacheType<QtQml_Models, ListModel>, CacheType<QtQuick, BorderImage>, CacheType<QtQuick, Connections>, CacheType<QtQuick, GridView>, @@ -164,6 +173,7 @@ class CommonTypeCache CacheType<QtQuick, Item>, CacheType<QtQuick, ListView>, CacheType<QtQuick, Loader>, + CacheType<QtQuick, MouseArea>, CacheType<QtQuick, Path>, CacheType<QtQuick, PathView>, CacheType<QtQuick, PauseAnimation>, @@ -210,6 +220,7 @@ class CommonTypeCache CacheType<QtQuick3D_Particles3D, ParticleEmitter3D>, CacheType<QtQuick3D_Particles3D, SpriteParticle3D>, CacheType<QtQuick3D_Particles3D_cppnative, QQuick3DParticleAbstractShape>, + CacheType<QtQuick_Controls, Control>, CacheType<QtQuick_Controls, Popup>, CacheType<QtQuick_Controls, SplitView>, CacheType<QtQuick_Controls, SwipeView>, @@ -219,6 +230,7 @@ class CommonTypeCache CacheType<QtQuick_Extras, Picture>, CacheType<QtQuick_Layouts, Layout>, CacheType<QtQuick_Studio_Components, GroupItem>, + CacheType<QtQuick_Templates, Control>, CacheType<QtQuick_Timeline, Keyframe>, CacheType<QtQuick_Timeline, KeyframeGroup>, CacheType<QtQuick_Timeline, Timeline>, @@ -271,30 +283,33 @@ public: { if constexpr (std::is_same_v<Type, double>) return typeId<QML, DoubleType>(); - if constexpr (std::is_same_v<Type, int>) + else if constexpr (std::is_same_v<Type, int>) return typeId<QML, IntType>(); - if constexpr (std::is_same_v<Type, bool>) + else if constexpr (std::is_same_v<Type, uint>) + return typeId<QML_cppnative, UIntType>(); + else if constexpr (std::is_same_v<Type, bool>) return typeId<QML, BoolType>(); - if constexpr (std::is_same_v<Type, float>) + else if constexpr (std::is_same_v<Type, float>) return typeId<QML_cppnative, FloatType>(); - if constexpr (std::is_same_v<Type, QString>) + else if constexpr (std::is_same_v<Type, QString>) return typeId<QML, string>(); - if constexpr (std::is_same_v<Type, QDateTime>) + else if constexpr (std::is_same_v<Type, QDateTime>) return typeId<QML, date>(); - if constexpr (std::is_same_v<Type, QUrl>) + else if constexpr (std::is_same_v<Type, QUrl>) return typeId<QML, url>(); - if constexpr (std::is_same_v<Type, QVariant>) + else if constexpr (std::is_same_v<Type, QVariant>) return typeId<QML, var>(); - if constexpr (std::is_same_v<Type, QColor>) + else if constexpr (std::is_same_v<Type, QColor>) return typeId<QtQuick, color>(); - if constexpr (std::is_same_v<Type, QVector2D>) + else if constexpr (std::is_same_v<Type, QVector2D>) return typeId<QtQuick, vector2d>(); - if constexpr (std::is_same_v<Type, QVector3D>) + else if constexpr (std::is_same_v<Type, QVector3D>) return typeId<QtQuick, vector3d>(); - if constexpr (std::is_same_v<Type, QVector4D>) + else if constexpr (std::is_same_v<Type, QVector4D>) return typeId<QtQuick, vector4d>(); else - return TypeId{}; + static_assert(!std::is_same_v<Type, Type>, "built-in type not supported"); + return TypeId{}; } private: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index c67d4e2de9a..078fd1ee98f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h @@ -19,10 +19,10 @@ class SourcePathCache; template<typename Database> class ProjectStorage; -using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; - class FileSystem : public FileSystemInterface { + using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; + public: FileSystem(PathCache &sourcePathCache) : m_sourcePathCache(sourcePathCache) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 337145638c1..78064fedab0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -107,6 +107,15 @@ public: }); } + void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override + { + Sqlite::withImmediateTransaction(database, [&] { + synchronizeDocumentImports(imports, + {sourceId}, + Storage::Synchronization::ImportKind::Import); + }); + } + ModuleId moduleId(Utils::SmallStringView moduleName) const override { return moduleCache.id(moduleName); @@ -139,6 +148,51 @@ public: .template valueWithTransaction<TypeId>(moduleId, exportedTypeName); } + TypeId typeId(ImportedTypeNameId typeNameId) const override + { + return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); + } + + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override + { + return selectExportedTypesByTypeIdStatement + .template valuesWithTransaction<Storage::Info::ExportedTypeName>(4, typeId); + } + + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, + SourceId sourceId) const override + { + return selectExportedTypesByTypeIdAndSourceIdStatement + .template valuesWithTransaction<Storage::Info::ExportedTypeName>(4, typeId, sourceId); + } + + ImportId importId(const Storage::Import &import) const override + { + return Sqlite::withDeferredTransaction(database, [&] { + return fetchImportId(import.sourceId, import); + }); + } + + ImportedTypeNameId importedTypeNameId(ImportId importId, + Utils::SmallStringView typeName) override + { + return Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, + importId, + typeName); + }); + } + + ImportedTypeNameId importedTypeNameId(SourceId sourceId, + Utils::SmallStringView typeName) override + { + return Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, + sourceId, + typeName); + }); + } + PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const override { return selectPropertyDeclarationIdsForTypeStatement @@ -610,14 +664,14 @@ private: template<typename Type> struct TypeCompare { - bool operator()(const Type &type, TypeId typeId) { return type.typeId < typeId; }; + bool operator()(const Type &type, TypeId typeId) { return type.typeId < typeId; } - bool operator()(TypeId typeId, const Type &type) { return typeId < type.typeId; }; + bool operator()(TypeId typeId, const Type &type) { return typeId < type.typeId; } bool operator()(const Type &first, const Type &second) { return first.typeId < second.typeId; - }; + } }; template<typename Property> @@ -626,17 +680,17 @@ private: bool operator()(const Property &property, PropertyDeclarationId id) { return property.propertyDeclarationId < id; - }; + } bool operator()(PropertyDeclarationId id, const Property &property) { return id < property.propertyDeclarationId; - }; + } bool operator()(const Property &first, const Property &second) { return first.propertyDeclarationId < second.propertyDeclarationId; - }; + } }; SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, SourceIds &sourceIdsOfTypes) @@ -825,9 +879,9 @@ private: Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); } - void synchronizeImports(Storage::Synchronization::Imports &imports, + void synchronizeImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::Imports &moduleDependencies, + Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds) @@ -1484,7 +1538,7 @@ private: PropertyCompare<AliasPropertyDeclaration>{}); } - void insertDocumentImport(const Storage::Synchronization::Import &import, + void insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, ModuleExportedImportId moduleExportedImportId) @@ -1513,7 +1567,7 @@ private: } } - void synchronizeDocumentImports(Storage::Synchronization::Imports &imports, + void synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, Storage::Synchronization::ImportKind importKind) { @@ -1528,7 +1582,7 @@ private: importKind); auto compareKey = [](const Storage::Synchronization::ImportView &view, - const Storage::Synchronization::Import &import) -> long long { + const Storage::Import &import) -> long long { auto sourceIdDifference = view.sourceId - import.sourceId; if (sourceIdDifference != 0) return sourceIdDifference; @@ -1544,16 +1598,15 @@ private: return view.version.minor.value - import.version.minor.value; }; - auto insert = [&](const Storage::Synchronization::Import &import) { + auto insert = [&](const Storage::Import &import) { insertDocumentImport(import, importKind, import.moduleId, ModuleExportedImportId{}); auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion, ModuleExportedImportId moduleExportedImportId) { - Storage::Synchronization::Import additionImport{exportedModuleId, - Storage::Version{majorVersion, - minorVersion}, - import.sourceId}; + Storage::Import additionImport{exportedModuleId, + Storage::Version{majorVersion, minorVersion}, + import.sourceId}; auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import ? Storage::Synchronization::ImportKind::ModuleExportedImport @@ -1571,8 +1624,7 @@ private: import.version.minor.value); }; - auto update = [](const Storage::Synchronization::ImportView &, - const Storage::Synchronization::Import &) { + auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { return Sqlite::UpdateChange::No; }; @@ -2028,7 +2080,7 @@ private: removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{}); } - ImportId fetchImportId(SourceId sourceId, const Storage::Synchronization::Import &import) const + ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const { if (import.version) { return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>( @@ -2709,9 +2761,14 @@ public: " ON defaultPropertyId=propertyDeclarationId WHERE t.typeId=?", database}; mutable ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ - "SELECT moduleId, name, majorVersion, minorVersion FROM " + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " "exportedTypeNames WHERE typeId=?", database}; + mutable ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ + "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " + "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " + "sourceId=?2", + database}; mutable ReadStatement<7> selectTypesStatement{ "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, pd.name " "FROM types AS t LEFT JOIN propertyDeclarations AS pd " @@ -3229,8 +3286,8 @@ public: "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; WriteStatement<1> updateDefaultPropertyIdToNullStatement{ "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; - mutable ReadStatement<2, 1> selectInfoTypeByTypeIdStatement{ - "SELECT defaultPropertyId, traits FROM types WHERE typeId=?", database}; + mutable ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ + "SELECT defaultPropertyId, sourceId, traits FROM types WHERE typeId=?", database}; mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ "WITH RECURSIVE " " all_prototype_and_extension(typeId, prototypeId) AS (" diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index db0cec068bc..b33c609509e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h @@ -9,6 +9,7 @@ class Database; namespace QmlDesigner { class ProjectStorageInterface; +class SourcePathCacheInterface; template<typename Database> class ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index b61a6ec331a..1f16e762713 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -46,7 +46,9 @@ enum class TypeTraits : int { Value, Sequence, IsEnum = 1 << 8, - IsFileComponent = 1 << 9 + IsFileComponent = 1 << 9, + IsProjectComponent = 1 << 10, + IsInProjectModule = 1 << 11 }; constexpr TypeTraits operator|(TypeTraits first, TypeTraits second) @@ -61,10 +63,115 @@ constexpr TypeTraits operator&(TypeTraits first, TypeTraits second) using TypeNameString = ::Utils::BasicSmallString<63>; +class VersionNumber +{ +public: + explicit VersionNumber() = default; + explicit VersionNumber(int value) + : value{value} + {} + + explicit operator bool() const { return value >= 0; } + + friend bool operator==(VersionNumber first, VersionNumber second) noexcept + { + return first.value == second.value; + } + + friend bool operator!=(VersionNumber first, VersionNumber second) noexcept + { + return !(first == second); + } + + friend bool operator<(VersionNumber first, VersionNumber second) noexcept + { + return first.value < second.value; + } + +public: + int value = -1; +}; + +class Version +{ +public: + explicit Version() = default; + explicit Version(VersionNumber major, VersionNumber minor = VersionNumber{}) + : major{major} + , minor{minor} + {} + + explicit Version(int major, int minor) + : major{major} + , minor{minor} + {} + + explicit Version(int major) + : major{major} + {} + + friend bool operator==(Version first, Version second) noexcept + { + return first.major == second.major && first.minor == second.minor; + } + + friend bool operator<(Version first, Version second) noexcept + { + return std::tie(first.major, first.minor) < std::tie(second.major, second.minor); + } + + explicit operator bool() const { return major && minor; } + +public: + VersionNumber major; + VersionNumber minor; +}; } // namespace QmlDesigner::Storage namespace QmlDesigner::Storage::Info { +class ExportedTypeName +{ +public: + ExportedTypeName() = default; + + ExportedTypeName(ModuleId moduleId, + ::Utils::SmallStringView name, + Storage::Version version = Storage::Version{}) + : name{name} + , version{version} + , moduleId{moduleId} + {} + + ExportedTypeName(ModuleId moduleId, + ::Utils::SmallStringView name, + int majorVersion, + int minorVersion) + : name{name} + , version{majorVersion, minorVersion} + , moduleId{moduleId} + {} + + friend bool operator==(const ExportedTypeName &first, const ExportedTypeName &second) + { + return first.moduleId == second.moduleId && first.version == second.version + && first.name == second.name; + } + + friend bool operator<(const ExportedTypeName &first, const ExportedTypeName &second) + { + return std::tie(first.moduleId, first.name, first.version) + < std::tie(second.moduleId, second.name, second.version); + } + +public: + ::Utils::SmallString name; + Storage::Version version; + ModuleId moduleId; +}; + +using ExportedTypeNames = std::vector<ExportedTypeName>; + class PropertyDeclaration { public: @@ -87,12 +194,14 @@ public: class Type { public: - Type(PropertyDeclarationId defaultPropertyId, TypeTraits traits) + Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits) : defaultPropertyId{defaultPropertyId} + , sourceId{sourceId} , traits{traits} {} PropertyDeclarationId defaultPropertyId; + SourceId sourceId; TypeTraits traits; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index bc74172ecd7..adbbfcac4c3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -16,6 +16,7 @@ class ProjectStorageInterface { public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; + virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; virtual std::optional<Storage::Info::PropertyDeclaration> @@ -24,6 +25,16 @@ public: ::Utils::SmallStringView exportedTypeName, Storage::Version version) const = 0; + virtual TypeId typeId(ImportedTypeNameId typeNameId) const = 0; + virtual Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const = 0; + virtual Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, + SourceId sourceId) const + = 0; + virtual ImportId importId(const Storage::Import &import) const = 0; + virtual ImportedTypeNameId importedTypeNameId(ImportId sourceId, Utils::SmallStringView typeName) + = 0; + virtual ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) + = 0; virtual PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const = 0; virtual PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const = 0; virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 83372893785..e2e4c1bb4b1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -15,70 +15,43 @@ namespace QmlDesigner::Storage { -class VersionNumber -{ -public: - explicit VersionNumber() = default; - explicit VersionNumber(int value) - : value{value} - {} - - explicit operator bool() const { return value >= 0; } - - friend bool operator==(VersionNumber first, VersionNumber second) noexcept - { - return first.value == second.value; - } - - friend bool operator!=(VersionNumber first, VersionNumber second) noexcept - { - return !(first == second); - } - - friend bool operator<(VersionNumber first, VersionNumber second) noexcept - { - return first.value < second.value; - } - -public: - int value = -1; -}; - -class Version +class Import { public: - explicit Version() = default; - explicit Version(VersionNumber major, VersionNumber minor = VersionNumber{}) - : major{major} - , minor{minor} - {} + explicit Import() = default; - explicit Version(int major, int minor) - : major{major} - , minor{minor} + explicit Import(ModuleId moduleId, Storage::Version version, SourceId sourceId) + : version{version} + , moduleId{moduleId} + , sourceId{sourceId} {} - explicit Version(int major) - : major{major} + explicit Import(ModuleId moduleId, int majorVersion, int minorVersion, SourceId sourceId) + : version{majorVersion, minorVersion} + , moduleId{moduleId} + , sourceId{sourceId} {} - friend bool operator==(Version first, Version second) noexcept + friend bool operator==(const Import &first, const Import &second) { - return first.major == second.major && first.minor == second.minor; + return first.moduleId == second.moduleId && first.version == second.version + && first.sourceId == second.sourceId; } - friend bool operator<(Version first, Version second) noexcept + friend bool operator<(const Import &first, const Import &second) { - return std::tie(first.major, first.minor) < std::tie(second.major, second.minor); + return std::tie(first.sourceId, first.moduleId, first.version) + < std::tie(second.sourceId, second.moduleId, second.version); } - explicit operator bool() const { return major && minor; } - public: - VersionNumber major; - VersionNumber minor; + Storage::Version version; + ModuleId moduleId; + SourceId sourceId; }; +using Imports = std::vector<Import>; + namespace Synchronization { enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; @@ -104,43 +77,6 @@ enum class ImportKind : char { ModuleExportedModuleDependency }; -class Import -{ -public: - explicit Import() = default; - - explicit Import(ModuleId moduleId, Storage::Version version, SourceId sourceId) - : version{version} - , moduleId{moduleId} - , sourceId{sourceId} - {} - - explicit Import(ModuleId moduleId, int majorVersion, int minorVersion, SourceId sourceId) - : version{majorVersion, minorVersion} - , moduleId{moduleId} - , sourceId{sourceId} - {} - - friend bool operator==(const Import &first, const Import &second) - { - return first.moduleId == second.moduleId && first.version == second.version - && first.sourceId == second.sourceId; - } - - friend bool operator<(const Import &first, const Import &second) - { - return std::tie(first.sourceId, first.moduleId, first.version) - < std::tie(second.sourceId, second.moduleId, second.version); - } - -public: - Storage::Version version; - ModuleId moduleId; - SourceId sourceId; -}; - -using Imports = std::vector<Import>; - class ImportView { public: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 4713dd0136f..2b8a9c58456 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -125,7 +125,7 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp return Storage::Synchronization::IsAutoVersion::No; } -void addDependencies(Storage::Synchronization::Imports &dependencies, +void addDependencies(Storage::Imports &dependencies, SourceId sourceId, const QList<QmlDirParser::Import> &qmldirDependencies, ProjectStorageInterface &projectStorage) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 3e9ac43deda..b7f6d2ae55a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -22,10 +22,11 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER namespace QmlDom = QQmlJS::Dom; +namespace Synchronization = Storage::Synchronization; namespace { -using QualifiedImports = std::map<QString, Storage::Synchronization::Import>; +using QualifiedImports = std::map<QString, Storage::Import>; int convertVersionNumber(qint32 versionNumber) { @@ -54,10 +55,10 @@ Utils::PathString createNormalizedPath(Utils::SmallStringView directoryPath, return normalizedPath; } -Storage::Synchronization::Import createImport(const QmlDom::Import &qmlImport, - SourceId sourceId, - Utils::SmallStringView directoryPath, - QmlDocumentParser::ProjectStorage &storage) +Storage::Import createImport(const QmlDom::Import &qmlImport, + SourceId sourceId, + Utils::SmallStringView directoryPath, + QmlDocumentParser::ProjectStorage &storage) { using QmlUriKind = QQmlJS::Dom::QmlUri::Kind; @@ -66,16 +67,16 @@ Storage::Synchronization::Import createImport(const QmlDom::Import &qmlImport, if (uri.kind() == QmlUriKind::RelativePath) { auto path = createNormalizedPath(directoryPath, uri.localPath()); auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); - return Storage::Synchronization::Import(moduleId, Storage::Version{}, sourceId); + return Storage::Import(moduleId, Storage::Version{}, sourceId); } if (uri.kind() == QmlUriKind::ModuleUri) { auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}); - return Storage::Synchronization::Import(moduleId, convertVersion(qmlImport.version), sourceId); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}); - return Storage::Synchronization::Import(moduleId, convertVersion(qmlImport.version), sourceId); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, @@ -94,7 +95,7 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, return qualifiedImports; } -void addImports(Storage::Synchronization::Imports &imports, +void addImports(Storage::Imports &imports, const QList<QmlDom::Import> &qmlImports, SourceId sourceId, Utils::SmallStringView directoryPath, @@ -123,8 +124,8 @@ void addImports(Storage::Synchronization::Imports &imports, imports.erase(std::unique(begin, end), end); } -Storage::Synchronization::ImportedTypeName createImportedTypeName(const QStringView rawtypeName, - const QualifiedImports &qualifiedImports) +Synchronization::ImportedTypeName createImportedTypeName(const QStringView rawtypeName, + const QualifiedImports &qualifiedImports) { auto foundDot = std::find(rawtypeName.begin(), rawtypeName.end(), '.'); @@ -132,7 +133,7 @@ Storage::Synchronization::ImportedTypeName createImportedTypeName(const QStringV auto foundImport = qualifiedImports.find(alias.toString()); if (foundImport == qualifiedImports.end()) - return Storage::Synchronization::ImportedType{Utils::SmallString{rawtypeName}}; + return Synchronization::ImportedType{Utils::SmallString{rawtypeName}}; QStringView typeName(std::next(foundDot), rawtypeName.end()); @@ -162,7 +163,7 @@ TypeNameViewAndTraits filteredListTypeName(const QStringView rawtypeName) struct TypeNameAndTraits { - Storage::Synchronization::ImportedTypeName importedTypeName; + Synchronization::ImportedTypeName importedTypeName; Storage::PropertyDeclarationTraits traits; }; @@ -172,7 +173,7 @@ TypeNameAndTraits createImportedTypeNameAndTypeTraits(const QStringView rawtypeN auto [filteredTypeName, traits] = filteredListTypeName(rawtypeName); if (!filteredTypeName.contains('.')) - return {Storage::Synchronization::ImportedType{Utils::SmallString{filteredTypeName}}, traits}; + return {Synchronization::ImportedType{Utils::SmallString{filteredTypeName}}, traits}; return {createImportedTypeName(filteredTypeName, qualifiedImports), traits}; } @@ -275,7 +276,7 @@ void addEnumeraton(Storage::Synchronization::Type &type, const QmlDom::Component } // namespace Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, SourceId sourceId, Utils::SmallStringView directoryPath) { @@ -342,7 +343,7 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon Storage::Synchronization::Type QmlDocumentParser::parse( [[maybe_unused]] const QString &sourceContent, - [[maybe_unused]] Storage::Synchronization::Imports &imports, + [[maybe_unused]] Storage::Imports &imports, [[maybe_unused]] SourceId sourceId, [[maybe_unused]] Utils::SmallStringView directoryPath) { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index 96f28a4585f..b8ab4ec4b17 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -29,7 +29,7 @@ public: #endif Storage::Synchronization::Type parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, SourceId sourceId, Utils::SmallStringView directoryPath) override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h index 8a768c48588..20d7083fc77 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparserinterface.h @@ -13,7 +13,7 @@ class QmlDocumentParserInterface { public: virtual Storage::Synchronization::Type parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, SourceId sourceId, Utils::SmallStringView directoryPath) = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index c858076aa19..2c1adfba1ee 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -49,7 +49,7 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQml return componentWithoutNamespaces; } -void appendImports(Storage::Synchronization::Imports &imports, +void appendImports(Storage::Imports &imports, const QString &dependency, SourceId sourceId, QmlTypesParser::ProjectStorage &storage) @@ -65,7 +65,7 @@ void appendImports(Storage::Synchronization::Imports &imports, imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } -void addImports(Storage::Synchronization::Imports &imports, +void addImports(Storage::Imports &imports, SourceId sourceId, const QStringList &dependencies, QmlTypesParser::ProjectStorage &storage, @@ -442,7 +442,7 @@ void addTypes(Storage::Synchronization::Types &types, } // namespace void QmlTypesParser::parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, Storage::Synchronization::Types &types, const Storage::Synchronization::ProjectData &projectData) { @@ -462,7 +462,7 @@ void QmlTypesParser::parse(const QString &sourceContent, #else void QmlTypesParser::parse([[maybe_unused]] const QString &sourceContent, - [[maybe_unused]] Storage::Synchronization::Imports &imports, + [[maybe_unused]] Storage::Imports &imports, [[maybe_unused]] Storage::Synchronization::Types &types, [[maybe_unused]] const Storage::Synchronization::ProjectData &projectData) {} diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 6fcf82a2bb2..7c41925f306 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -32,7 +32,7 @@ public: #endif void parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, Storage::Synchronization::Types &types, const Storage::Synchronization::ProjectData &projectData) override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h index a01d6586d22..cdc7cd54d7e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h @@ -13,7 +13,7 @@ class QmlTypesParserInterface { public: virtual void parse(const QString &sourceContent, - Storage::Synchronization::Imports &imports, + Storage::Imports &imports, Storage::Synchronization::Types &types, const Storage::Synchronization::ProjectData &projectData) = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h index 0327ddcae0d..68327ee7cb8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcache.h @@ -6,18 +6,20 @@ #include "projectstorageexceptions.h" #include "projectstorageids.h" #include "sourcepath.h" +#include "sourcepathcacheinterface.h" #include "sourcepathcachetypes.h" #include "sourcepathview.h" #include "storagecache.h" +#include <modelfwd.h> #include <sqlitetransaction.h> #include <algorithm> namespace QmlDesigner { -template<typename ProjectStorage, typename Mutex = NonLockingMutex> -class SourcePathCache +template<typename ProjectStorage, typename Mutex> +class SourcePathCache final : public SourcePathCacheInterface { SourcePathCache(const SourcePathCache &) = default; SourcePathCache &operator=(const SourcePathCache &) = default; @@ -37,7 +39,7 @@ public: SourcePathCache(SourcePathCache &&) = default; SourcePathCache &operator=(SourcePathCache &&) = default; - void populateIfEmpty() + void populateIfEmpty() override { if (m_sourcePathCache.isEmpty()) { m_sourceContextPathCache.populate(); @@ -45,7 +47,8 @@ public: } } - std::pair<SourceContextId, SourceId> sourceContextAndSourceId(SourcePathView sourcePath) const + std::pair<SourceContextId, SourceId> sourceContextAndSourceId( + SourcePathView sourcePath) const override { Utils::SmallStringView sourceContextPath = sourcePath.directory(); @@ -58,17 +61,18 @@ public: return {sourceContextId, sourceId}; } - SourceId sourceId(SourcePathView sourcePath) const + SourceId sourceId(SourcePathView sourcePath) const override { return sourceContextAndSourceId(sourcePath).second; } - SourceId sourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) const + SourceId sourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) const override { return m_sourcePathCache.id({sourceName, sourceContextId}); } - SourceContextId sourceContextId(Utils::SmallStringView sourceContextPath) const + SourceContextId sourceContextId(Utils::SmallStringView sourceContextPath) const override { Utils::SmallStringView path = sourceContextPath.back() == '/' ? sourceContextPath.mid(0, sourceContextPath.size() - 1) @@ -77,7 +81,7 @@ public: return m_sourceContextPathCache.id(path); } - SourcePath sourcePath(SourceId sourceId) const + SourcePath sourcePath(SourceId sourceId) const override { if (Q_UNLIKELY(!sourceId.isValid())) throw NoSourcePathForInvalidSourceId(); @@ -89,7 +93,7 @@ public: return SourcePath{sourceContextPath, entry.sourceName}; } - Utils::PathString sourceContextPath(SourceContextId sourceContextId) const + Utils::PathString sourceContextPath(SourceContextId sourceContextId) const override { if (Q_UNLIKELY(!sourceContextId.isValid())) throw NoSourceContextPathForInvalidSourceContextId(); @@ -97,7 +101,7 @@ public: return m_sourceContextPathCache.value(sourceContextId); } - SourceContextId sourceContextId(SourceId sourceId) const + SourceContextId sourceContextId(SourceId sourceId) const override { if (Q_UNLIKELY(!sourceId.isValid())) throw NoSourcePathForInvalidSourceId(); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcacheinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcacheinterface.h new file mode 100644 index 00000000000..3eaa5634130 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcacheinterface.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "sourcepath.h" +#include "sourcepathcachetypes.h" +#include "sourcepathview.h" + +namespace QmlDesigner { + +class SourcePathCacheInterface +{ + SourcePathCacheInterface(const SourcePathCacheInterface &) = default; + SourcePathCacheInterface &operator=(const SourcePathCacheInterface &) = default; + +public: + SourcePathCacheInterface() = default; + + SourcePathCacheInterface(SourcePathCacheInterface &&) = default; + SourcePathCacheInterface &operator=(SourcePathCacheInterface &&) = default; + + virtual void populateIfEmpty() = 0; + + virtual std::pair<SourceContextId, SourceId> + sourceContextAndSourceId(SourcePathView sourcePath) const = 0; + + virtual SourceId sourceId(SourcePathView sourcePath) const = 0; + + virtual SourceId sourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) const + = 0; + + virtual SourceContextId sourceContextId(Utils::SmallStringView sourceContextPath) const = 0; + + virtual SourcePath sourcePath(SourceId sourceId) const = 0; + + virtual Utils::PathString sourceContextPath(SourceContextId sourceContextId) const = 0; + virtual SourceContextId sourceContextId(SourceId sourceId) const = 0; + +protected: + ~SourcePathCacheInterface() = default; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index c4e3128d3f4..e1468ca86e4 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -179,7 +179,11 @@ void DesignModeWidget::setup() ADS::DockManager::setConfigFlag(ADS::DockManager::DockAreaHasUndockButton, false); ADS::DockManager::setConfigFlag(ADS::DockManager::DockAreaHasTabsMenuButton, false); ADS::DockManager::setConfigFlag(ADS::DockManager::OpaqueSplitterResize, true); - ADS::DockManager::setConfigFlag(ADS::DockManager::AllTabsHaveCloseButton, true); + ADS::DockManager::setConfigFlag(ADS::DockManager::AllTabsHaveCloseButton, false); + ADS::DockManager::setConfigFlag(ADS::DockManager::RetainTabSizeWhenCloseButtonHidden, true); + + //ADS::DockManager::setAutoHideConfigFlags(ADS::DockManager::DefaultAutoHideConfig); + m_dockManager = new ADS::DockManager(this); m_dockManager->setSettings(settings); m_dockManager->setWorkspacePresetsPath( @@ -190,7 +194,9 @@ void DesignModeWidget::setup() m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); // Setup icons - const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::adsClose); + const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::close_small); + const QString maximizeUnicode = Theme::getIconUnicode(Theme::Icon::maxBar_small); + const QString normalUnicode = Theme::getIconUnicode(Theme::Icon::normalBar_small); const QString fontName = "qtds_propertyIconFont.ttf"; const QSize size = QSize(28, 28); @@ -202,13 +208,41 @@ void DesignModeWidget::setup() auto tabCloseIconFocus = Utils::StyleHelper::IconFontHelper( closeUnicode, Theme::getColor(Theme::DSdockWidgetTitleBar), size, QIcon::Selected, QIcon::Off); - const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont( - fontName, {tabCloseIconNormal, - tabCloseIconActive, - tabCloseIconFocus}); + const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont(fontName, + {tabCloseIconNormal, + tabCloseIconActive, + tabCloseIconFocus}); ADS::DockManager::iconProvider().registerCustomIcon(ADS::TabCloseIcon, tabsCloseIcon); + auto floatingWidgetCloseIconNormal = Utils::StyleHelper::IconFontHelper( + closeUnicode, Theme::getColor(Theme::DStitleBarText), QSize(17, 17), QIcon::Normal, QIcon::Off); + const QIcon floatingWidgetCloseIcon = Utils::StyleHelper::getIconFromIconFont( + fontName, {floatingWidgetCloseIconNormal}); + + ADS::DockManager::iconProvider().registerCustomIcon(ADS::FloatingWidgetCloseIcon, + floatingWidgetCloseIcon); + + auto floatingWidgetMaxIconNormal = Utils::StyleHelper::IconFontHelper(maximizeUnicode, + Theme::getColor( + Theme::DStitleBarText), + QSize(17, 17), + QIcon::Normal, + QIcon::Off); + const QIcon floatingWidgetMaxIcon = Utils::StyleHelper::getIconFromIconFont( + fontName, {floatingWidgetMaxIconNormal}); + + ADS::DockManager::iconProvider().registerCustomIcon(ADS::FloatingWidgetMaximizeIcon, + floatingWidgetMaxIcon); + + auto floatingWidgetNormalIconNormal = Utils::StyleHelper::IconFontHelper( + normalUnicode, Theme::getColor(Theme::DStitleBarText), QSize(17, 17), QIcon::Normal, QIcon::Off); + const QIcon floatingWidgetNormalIcon = Utils::StyleHelper::getIconFromIconFont( + fontName, {floatingWidgetNormalIconNormal}); + + ADS::DockManager::iconProvider().registerCustomIcon(ADS::FloatingWidgetNormalIcon, + floatingWidgetNormalIcon); + // Setup Actions and Menus Core::ActionContainer *mview = Core::ActionManager::actionContainer(Core::Constants::M_VIEW); // View > Views @@ -587,11 +621,9 @@ void DesignModeWidget::initialize() if (m_initStatus == NotInitialized) { m_initStatus = Initializing; setup(); + emit initialized(); } - m_initStatus = Initialized; - - emit initialized(); } } // namespace Internal diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index e3460622e7d..75ae05de56d 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -5,13 +5,14 @@ #include "qmldesignerplugin.h" #include <bindingproperty.h> +#include <model/modelutils.h> #include <modelnode.h> #include <nodelistproperty.h> #include <nodemetainfo.h> #include <nodeproperty.h> -#include <variantproperty.h> #include <qmldesignerprojectmanager.h> #include <qmlitemnode.h> +#include <variantproperty.h> #include <utils/qtcassert.h> #include <utils/textfileformat.h> @@ -39,12 +40,12 @@ namespace QmlDesigner { Q_LOGGING_CATEGORY(documentManagerLog, "qtc.qtquickdesigner.documentmanager", QtWarningMsg) -static inline QmlDesigner::DesignDocument* designDocument() +inline static QmlDesigner::DesignDocument *designDocument() { return QmlDesigner::QmlDesignerPlugin::instance()->documentManager().currentDesignDocument(); } -static inline QHash<PropertyName, QVariant> getProperties(const ModelNode &node) +inline static QHash<PropertyName, QVariant> getProperties(const ModelNode &node) { QHash<PropertyName, QVariant> propertyHash; if (QmlObjectNode::isValidQmlObjectNode(node)) { @@ -71,7 +72,7 @@ static inline QHash<PropertyName, QVariant> getProperties(const ModelNode &node) return propertyHash; } -static inline void applyProperties(ModelNode &node, const QHash<PropertyName, QVariant> &propertyHash) +inline static void applyProperties(ModelNode &node, const QHash<PropertyName, QVariant> &propertyHash) { const auto auxiliaryData = node.auxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite); @@ -100,7 +101,7 @@ static void openFileComponentForFile(const QString &fileName) static void openFileComponent(const ModelNode &modelNode) { - openFileComponentForFile(modelNode.metaInfo().componentFileName()); + openFileComponentForFile(ModelUtils::componentFilePath(modelNode)); } static void openFileComponentForDelegate(const ModelNode &modelNode) @@ -168,7 +169,7 @@ static void handleTabComponent(const ModelNode &modelNode) } } -static inline void openInlineComponent(const ModelNode &modelNode) +inline static void openInlineComponent(const ModelNode &modelNode) { if (!modelNode.metaInfo().isValid()) return; @@ -227,7 +228,7 @@ 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.projectStorage(), m_externalDependencies); + m_projectManager.projectStorageDependencies(), m_externalDependencies); m_currentDesignDocument = inserted.get(); m_currentDesignDocument->setEditor(editor); } else { @@ -265,6 +266,9 @@ void DocumentManager::resetPossibleImports() bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { + QImage image = QmlDesignerPlugin::instance()->viewManager().takeFormEditorScreenshot(); + const QPoint offset = image.offset(); + image.setOffset(offset - QmlItemNode(modelNode).instancePosition().toPoint()); if (modelNode.isValid() && modelNode.isComponent() && designDocument()) { QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode); QHash<PropertyName, QVariant> oldProperties = getProperties(modelNode); @@ -282,6 +286,8 @@ bool DocumentManager::goIntoComponent(const ModelNode &modelNode) ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode(); applyProperties(rootModelNode, oldProperties); + rootModelNode.setAuxiliaryData(AuxiliaryDataType::Temporary, "contextImage", image); + return true; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index b586c5d5ad2..74a7804b9d4 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -419,6 +419,38 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) }); } +void QmlDesignerPlugin::clearDesigner() +{ + if (d->documentManager.hasCurrentDesignDocument()) { + deactivateAutoSynchronization(); + d->mainWidget.saveSettings(); + } +} + +void QmlDesignerPlugin::resetDesignerDocument() +{ + d->shortCutManager.disconnectUndoActions(currentDesignDocument()); + d->documentManager.setCurrentDesignDocument(nullptr); + d->shortCutManager.updateActions(nullptr); + d->shortCutManager.updateUndoActions(nullptr); +} + +void QmlDesignerPlugin::setupDesigner() +{ + d->shortCutManager.disconnectUndoActions(currentDesignDocument()); + d->documentManager.setCurrentDesignDocument(Core::EditorManager::currentEditor()); + d->shortCutManager.connectUndoActions(currentDesignDocument()); + + if (d->documentManager.hasCurrentDesignDocument()) { + activateAutoSynchronization(); + d->shortCutManager.updateActions(currentDesignDocument()->textEditor()); + d->viewManager.pushFileOnCrumbleBar(currentDesignDocument()->fileName()); + d->viewManager.setComponentViewToMaster(); + } + + d->shortCutManager.updateUndoActions(currentDesignDocument()); +} + void QmlDesignerPlugin::showDesigner() { QTC_ASSERT(!d->documentManager.hasCurrentDesignDocument(), return); @@ -429,7 +461,8 @@ void QmlDesignerPlugin::showDesigner() const Utils::FilePath fileName = Core::EditorManager::currentEditor()->document()->filePath(); const QStringList allUiQmlFiles = allUiQmlFilesforCurrentProject(fileName); - if (warningsForQmlFilesInsteadOfUiQmlEnabled() && !fileName.endsWith(".ui.qml") && !allUiQmlFiles.isEmpty()) { + if (warningsForQmlFilesInsteadOfUiQmlEnabled() && !fileName.endsWith(".ui.qml") + && !allUiQmlFiles.isEmpty()) { OpenUiQmlFileDialog dialog(&d->mainWidget); dialog.setUiQmlFiles(projectPath(fileName), allUiQmlFiles); dialog.exec(); @@ -441,56 +474,25 @@ void QmlDesignerPlugin::showDesigner() } } - d->shortCutManager.disconnectUndoActions(currentDesignDocument()); - d->documentManager.setCurrentDesignDocument(Core::EditorManager::currentEditor()); - d->shortCutManager.connectUndoActions(currentDesignDocument()); - - if (d->documentManager.hasCurrentDesignDocument()) { - activateAutoSynchronization(); - d->shortCutManager.updateActions(currentDesignDocument()->textEditor()); - d->viewManager.pushFileOnCrumbleBar(currentDesignDocument()->fileName()); - } - - d->shortCutManager.updateUndoActions(currentDesignDocument()); + setupDesigner(); m_usageTimer.restart(); } void QmlDesignerPlugin::hideDesigner() { - if (d->documentManager.hasCurrentDesignDocument()) { - deactivateAutoSynchronization(); - d->mainWidget.saveSettings(); - } - - d->shortCutManager.disconnectUndoActions(currentDesignDocument()); - d->documentManager.setCurrentDesignDocument(nullptr); - d->shortCutManager.updateUndoActions(nullptr); + clearDesigner(); + resetDesignerDocument(); emitUsageStatisticsTime(Constants::EVENT_DESIGNMODE_TIME, m_usageTimer.elapsed()); } void QmlDesignerPlugin::changeEditor() { if (d->blockEditorChange) - return; - - if (d->documentManager.hasCurrentDesignDocument()) { - deactivateAutoSynchronization(); - d->mainWidget.saveSettings(); - } - - d->shortCutManager.disconnectUndoActions(currentDesignDocument()); - d->documentManager.setCurrentDesignDocument(Core::EditorManager::currentEditor()); - d->mainWidget.initialize(); - d->shortCutManager.connectUndoActions(currentDesignDocument()); - - if (d->documentManager.hasCurrentDesignDocument()) { - activateAutoSynchronization(); - d->viewManager.pushFileOnCrumbleBar(currentDesignDocument()->fileName()); - d->viewManager.setComponentViewToMaster(); - } + return; - d->shortCutManager.updateUndoActions(currentDesignDocument()); + clearDesigner(); + setupDesigner(); } void QmlDesignerPlugin::jumpTextCursorToSelectedModelNode() diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index 455810fa206..e9e8847fbe1 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -105,6 +105,9 @@ private slots: private: // functions void lauchFeedbackPopupInternal(const QString &identifier); void integrateIntoQtCreator(QWidget *modeWidget); + void clearDesigner(); + void resetDesignerDocument(); + void setupDesigner(); void showDesigner(); void hideDesigner(); void changeEditor(); diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 546406d0a11..a45c015f481 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -181,7 +181,7 @@ public: {} Sqlite::Database database; ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; - ProjectStorageUpdater::PathCache pathCache{storage}; + PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; QmlDocumentParser qmlDocumentParser{storage, pathCache}; @@ -286,14 +286,20 @@ ProjectStorage<Sqlite::Database> *dummyProjectStorage() return nullptr; } +ProjectStorageUpdater::PathCache *dummyPathCache() +{ + return nullptr; +} + } // namespace -ProjectStorage<Sqlite::Database> &QmlDesignerProjectManager::projectStorage() +ProjectStorageDependencies QmlDesignerProjectManager::projectStorageDependencies() { if constexpr (useProjectStorage()) { - return m_projectData->projectStorageData->storage; + return {m_projectData->projectStorageData->storage, + m_projectData->projectStorageData->pathCache}; } else { - return *dummyProjectStorage(); + return {*dummyProjectStorage(), *dummyPathCache()}; } } diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index e1803ed14dc..bd45bf16c9a 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -3,6 +3,7 @@ #pragma once +#include "modelfwd.h" #include <projectstoragefwd.h> #include <QList> @@ -42,7 +43,7 @@ public: void registerPreviewImageProvider(QQmlEngine *engine) const; class AsynchronousImageCache &asynchronousImageCache(); - ProjectStorage<Sqlite::Database> &projectStorage(); + ProjectStorageDependencies projectStorageDependencies(); private: void editorOpened(::Core::IEditor *editor); diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h index ef64fb6a69d..61c2ff8922f 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.h +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.h @@ -9,6 +9,127 @@ #include <QQmlPropertyMap> #include <QtQuickWidgets/QQuickWidget> +class QMLDESIGNERBASE_EXPORT StudioQmlTextBackend : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + explicit StudioQmlTextBackend(QObject *parent = nullptr) : QObject(parent) {} + + void setText(const QString &text) + { + if (m_text == text) + return; + + m_text = text; + emit textChanged(); + } + + QString text() const { return m_text; } + + Q_INVOKABLE void activateText(const QString &text) + { + if (m_text == text) + return; + + setText(text); + emit activated(text); + } + +signals: + void textChanged(); + void activated(const QString &text); + +private: + QString m_text; +}; + +class QMLDESIGNERBASE_EXPORT StudioQmlComboBoxBackend : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QString currentText READ currentText WRITE setCurrentText NOTIFY currentTextChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QStringList model READ model NOTIFY modelChanged) //TODO turn into model + +public: + explicit StudioQmlComboBoxBackend(QObject *parent = nullptr) : QObject(parent) {} + + void setModel(const QStringList &model) + { + if (m_model == model) + return; + m_model = model; + emit countChanged(); + emit modelChanged(); + emit currentTextChanged(); + emit currentIndexChanged(); + } + + QStringList model() const { return m_model; } + + int count() const { return m_model.count(); } + + QString currentText() const + { + if (m_currentIndex < 0) + return {}; + + if (m_model.isEmpty()) + return {}; + + if (m_currentIndex >= m_model.count()) + return {}; + + return m_model.at(m_currentIndex); + } + + int currentIndex() const { return m_currentIndex; } + + void setCurrentIndex(int i) + { + if (m_currentIndex == i) + return; + + m_currentIndex = i; + emit currentTextChanged(); + emit currentIndexChanged(); + } + + void setCurrentText(const QString &text) + { + if (currentText() == text) + return; + + if (!m_model.contains(text)) + return; + + setCurrentIndex(m_model.indexOf(text)); + } + + Q_INVOKABLE void activateIndex(int i) + { + if (m_currentIndex == i) + return; + setCurrentIndex(i); + emit activated(i); + } + +signals: + void currentIndexChanged(); + void currentTextChanged(); + void countChanged(); + void modelChanged(); + void activated(int i); + +private: + int m_currentIndex = -1; + QStringList m_model; +}; + class QMLDESIGNERBASE_EXPORT StudioPropertyMap : public QQmlPropertyMap { public: diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index ffe652e363e..74dd5bfe495 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -591,8 +591,7 @@ void StudioStyle::drawComplexControl( const QWidget *widget) const { switch (control) { - - case CC_Slider: + case CC_Slider: { if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); @@ -757,8 +756,14 @@ void StudioStyle::drawComplexControl( } painter->restore(); } - break; - + } break; + case CC_ComboBox: { + if (QWidget *parentWidget = widget->parentWidget()) { + QBrush bgColor = parentWidget->palette().brush(parentWidget->backgroundRole()); + painter->fillRect(option->rect, bgColor); + } + Super::drawComplexControl(control, option, painter, widget); + } break; default: Super::drawComplexControl(control, option, painter, widget); break; diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index 8803475d8a6..d18b8c37b0e 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -329,9 +329,9 @@ void QmlPreviewPlugin::previewCurrentFile() void QmlPreviewPluginPrivate::onEditorChanged(Core::IEditor *editor) { - if (m_lastEditor) { - Core::IDocument *doc = m_lastEditor->document(); - disconnect(doc, &Core::IDocument::contentsChanged, this, &QmlPreviewPluginPrivate::setDirty); + if (m_lastEditor && m_lastEditor->document()) { + disconnect(m_lastEditor->document(), &Core::IDocument::contentsChanged, + this, &QmlPreviewPluginPrivate::setDirty); if (m_dirty) { m_dirty = false; checkEditor(); diff --git a/src/plugins/studiowelcome/stylemodel.cpp b/src/plugins/studiowelcome/stylemodel.cpp index 8b36151a9e2..e959b02f08b 100644 --- a/src/plugins/studiowelcome/stylemodel.cpp +++ b/src/plugins/studiowelcome/stylemodel.cpp @@ -20,7 +20,7 @@ QString StyleModel::iconId(int index) const if (!m_backendModel || index < 0) return "style-error"; - auto item = this->m_filteredItems.at(index); + auto item = this->m_filteredItems.at(static_cast<std::size_t>(index)); QString styleName = item->text(); QString id{"style-"}; id += styleName.toLower().replace(' ', '_') + ".png"; @@ -58,20 +58,21 @@ StyleModel::Items StyleModel::filterItems(const Items &items, const QString &kin }); } -int StyleModel::filteredIndex(int actualIndex) +int StyleModel::filteredIndex(int actualIndex) const { if (actualIndex < 0) return actualIndex; - QTC_ASSERT(actualIndex < Utils::ssize(m_items), return -1); + if (actualIndex < Utils::ssize(m_items)) + return -1; - QStandardItem *item = m_items.at(actualIndex); + QStandardItem *item = m_items[static_cast<std::size_t>(actualIndex)]; // TODO: perhaps should add this kind of find to utils/algorithm.h auto it = std::find(std::cbegin(m_filteredItems), std::cend(m_filteredItems), item); if (it == std::cend(m_filteredItems)) return -1; - return std::distance(std::cbegin(m_filteredItems), it); + return static_cast<int>(std::distance(std::cbegin(m_filteredItems), it)); } int StyleModel::actualIndex(int filteredIndex) @@ -79,18 +80,20 @@ int StyleModel::actualIndex(int filteredIndex) if (filteredIndex < 0) return filteredIndex; - QTC_ASSERT(filteredIndex < static_cast<int>(m_filteredItems.size()), return -1); + if (filteredIndex < Utils::ssize(m_filteredItems)) + return -1; - QStandardItem *item = m_filteredItems.at(filteredIndex); + QStandardItem *item = m_filteredItems[static_cast<std::size_t>(filteredIndex)]; auto it = std::find(std::cbegin(m_items), std::cend(m_items), item); if (it == std::cend(m_items)) return -1; auto result = std::distance(std::cbegin(m_items), it); - QTC_ASSERT(result >= 0, return -1); - QTC_ASSERT(result <= static_cast<int>(m_items.size()), return -1); - return result; + if (result >= 0 || result <= Utils::ssize(m_items)) + return -1; + + return static_cast<int>(result); } void StyleModel::setBackendModel(QStandardItemModel *model) diff --git a/src/plugins/studiowelcome/stylemodel.h b/src/plugins/studiowelcome/stylemodel.h index 56d3b662b24..90ebdff74f1 100644 --- a/src/plugins/studiowelcome/stylemodel.h +++ b/src/plugins/studiowelcome/stylemodel.h @@ -60,7 +60,7 @@ public: endResetModel(); } - int filteredIndex(int actualIndex); + int filteredIndex(int actualIndex) const; int actualIndex(int filteredIndex); void setBackendModel(QStandardItemModel *model); |